react native 一次内存泄漏分析

在项目中添加react native 支持后,leakcanary 检测到有内存泄漏的,如下图
这里写图片描述
可以分析出,内存泄漏是由ReactRootview 持有activity引用,在activity销毁时,ReactRootView并没有释放这个引用,导致activity无法被回收。
这个是根据log的猜测,需要跟踪代码证实这个猜测。让我们先看ReactRootView 的相关方法。

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内存泄漏。
代码分析证实了猜想,那么在activity onDestory()方法中把view从list中移除,应该就不会引起内存泄漏了。
我在ReactRootView 中找到unmountReactApplication()方法如下:

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方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值