react native 一次内存泄漏分析
在项目中添加react native 支持后,leakcanary 检测到有内存泄漏的,如下图 mReactRootView.startReactApplication(mReactInstanceManager,"moudle",bundle);
查看startReactApplication方法 public void startReactApplication(ReactInstanceManager reactInstanceManager,String moduleName,@Nullable Bundle launchOptions) {
UiThreadUtil.assertOnUiThread();
Assertions.assertCondition(this.mReactInstanceManager == null,"This root view has already been attached to a catalyst instance manager");
this.mReactInstanceManager = reactInstanceManager;
this.mJSModuleName = moduleName;
this.mLaunchOptions = launchOptions;
if(!this.mReactInstanceManager.hasStartedCreatingInitialContext()) {
this.mReactInstanceManager.createReactContextInBackground();
}
if(this.mWasMeasured) {
this.attachToReactInstanceManager();
}
}
mWasMeasured会在view onMeasure() 的时候赋值为true,根据view相关知识,我们知道是测量控件宽高的,这个我的这个view还没有添加到viewgroup中所以会进入这个判断,进入attachToReactInstanceManager()方法(看名字感觉也是添加到管理的操作) private void attachToReactInstanceManager() {
if(!this.mIsAttachedToInstance) {
this.mIsAttachedToInstance = true;
((ReactInstanceManager)Assertions.assertNotNull(this.mReactInstanceManager)).attachMeasuredRootView(this);
this.getViewTreeObserver().addOnGlobalLayoutListener(this.getCustomGlobalLayoutListener());
}
}
这个方法中首先是一个boolean判断,这个不影响,查看attachMeasuredRootView()方法实现 public void attachMeasuredRootView(ReactRootView rootView) {
UiThreadUtil.assertOnUiThread();
this.mAttachedRootViews.add(rootView);
if(this.mReactContextInitAsyncTask == null && this.mCurrentReactContext != null) {
this.attachMeasuredRootViewToInstance(rootView,this.mCurrentReactContext.getCatalystInstance());
}
}
重点关注代码this.mAttachedRootViews.add(rootView);这里可以看到我们的view被添加到mAttachedRootViews 的list中。而保存这个list的XReactInstanceManagerImpl实例,是在创建ReactInstanceManager 是构建。在我的项目中,我为了减少白屏时间把ReactInstanceManager用单例实现,这样就导致保存view的list一直存在内存中,引起activity内存泄漏。 public void unmountReactApplication() {
if(this.mReactInstanceManager != null && this.mIsAttachedToInstance) {
this.mReactInstanceManager.detachRootView(this);
this.mIsAttachedToInstance = false;
}
}
进入detachRootView()方法 public void detachRootView(ReactRootView rootView) {
UiThreadUtil.assertOnUiThread();
if(this.mAttachedRootViews.remove(rootView) && this.mCurrentReactContext != null && this.mCurrentReactContext.hasActiveCatalystInstance()) {
this.detachViewFromInstance(rootView,this.mCurrentReactContext.getCatalystInstance());
}
}
可以看到该方法执行了this.mAttachedRootViews.remove(rootView),移除view。我修改onDestory方法如下: @Override
protected void onDestroy() {
super.onDestroy();
if(mReactRootView!=null) {
mReactRootView.unmountReactApplication();
mReactRootView = null;
}
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostDestroy(this);
}
}
添加没有在出现内存泄漏的log,我知道React Native 提供了一个实现好的ReactActivity,于是好奇的看了一下它是如何实现onDestory的: protected void onDestroy() {
super.onDestroy();
this.mDelegate.onDestroy();
}
protected void onDestroy() {
if(this.mReactRootView != null) {
this.mReactRootView.unmountReactApplication();
this.mReactRootView = null;
}
if(this.getReactNativeHost().hasInstance()) {
this.getReactNativeHost().getReactInstanceManager().onHostDestroy(this.getPlainActivity());
}
}
看到这里就明白了,fb自己的ReactActivity中也添加了unmountReactApplication方法。我估计文档中的例子都是最简单的,但是自己的项目中不可能每个activity都像例子那样去实现一个ReactInstanceManager,所以要么用系统的ReactActivity 要么自己添加unmountReactApplication方法。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |