从实时数据更改活动会导致内存泄漏

时间:2019-11-20 09:34:49

标签: android memory-leaks android-livedata leakcanary

我的代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_navigation);
    bottomNavigationView = findViewById(R.id.bottom_navigation);
    subscribeObservers();
}

private void subscribeObservers() {
    if (!sessionManager.getAuthedUser().hasActiveObservers()) {
        sessionManager.getAuthedUser().observe(this, new Observer<AuthResource<LoggedUser>>() {
            @Override
            public void onChanged(@Nullable AuthResource<LoggedUser> loggedUserAuthResource) {
                if (loggedUserAuthResource != null) {
                    switch (loggedUserAuthResource.status) {
                        case AUTHENTICATED:
                            Log.d(TAG, "onChanged: Auth success");
                            break;
                        case LOADING:
                            Log.d(TAG, "onChanged: Auth in progress");
                            break;
                        case NOT_AUTHENTICATED:
                            goToWelcomeScreen();
                            Log.d(TAG, "onChanged: Auth failed");
                            break;
                        case ERROR:
                            Log.d(TAG, "onChanged: Auth error");
                            break;
                    }
                }
            }
        });
    }
}

protected void goToWelcomeScreen() {
    Intent intent = new Intent(this, WelcomeActivity.class);
    finish();
    startActivity(intent);
}

这是我的会话管理器方法:

    private void initAuthedUser() {
        authedUser.setValue(AuthResource.loading((LoggedUser) null));
        final LiveData<LoggedUser> source = ribonyRepository.getAuthedUser();
        authedUser.setValue(AuthResource.notAuthenticated(null));
    }

    public LiveData<AuthResource<LoggedUser>> getAuthedUser() {
        return authedUser;
    }

您可以看到我是否在观察器中运行goToWelcomeScreen方法,我的活动正在泄漏。这里是泄漏日志:

LibraryLeak(className=com.impact.ribony.activities.MainNavigationActivity, leakTrace=
┬
├─ android.app.ActivityThread
│    Leaking: NO (a class is never leaking)
│    GC Root: System class
│    ↓ static ActivityThread.sCurrentActivityThread
│                            ~~~~~~~~~~~~~~~~~~~~~~
├─ android.app.ActivityThread
│    Leaking: UNKNOWN
│    ↓ ActivityThread.mNewActivities
│                     ~~~~~~~~~~~~~~
├─ android.app.ActivityThread$ActivityClientRecord
│    Leaking: UNKNOWN
│    ↓ ActivityThread$ActivityClientRecord.nextIdle
│                                          ~~~~~~~~
├─ android.app.ActivityThread$ActivityClientRecord
│    Leaking: UNKNOWN
│    ↓ ActivityThread$ActivityClientRecord.activity
│                                          ~~~~~~~~
╰→ com.impact.ribony.activities.MainNavigationActivity
​     Leaking: YES (Activity#mDestroyed is true and ObjectWatcher was watching this)
​     key = 4f1783be-bb8f-45df-96bb-e961b3277a1a
​     watchDurationMillis = 5196
​     retainedDurationMillis = 190
, retainedHeapByteSize=1213860, pattern=instance field android.app.ActivityThread$ActivityClientRecord#nextIdle, description=Android AOSP sometimes keeps a reference to a destroyed activity as a nextIdle client record in the android.app.ActivityThread.mActivities map. Not sure what's going on there, input welcome.)

有趣的是,如果我将onCreate更改为以下内容,那么我的活动就不会泄漏。 (我从subscriptionObservers方法中删除了goToWelcomeScreen调用。)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_navigation);
    bottomNavigationView = findViewById(R.id.bottom_navigation);
    subscribeObservers();
    goToWelcomeScreen();
}

什么会导致此问题?我该如何解决?

谢谢

2 个答案:

答案 0 :(得分:1)

LeakCanary将其标识为“库泄漏”,这意味着它看起来像是Android Framework中的已知泄漏。请参见您粘贴的跟踪中的描述:

  

Android AOSP有时会在android.app.ActivityThread.mActivities映射中保留对销毁活动的引用作为nextIdle客户端记录。不知道发生了什么,请输入欢迎。

听起来您已找到解决问题的方法,这是迈出的重要一步。看起来如果您在onCreate()期间切换活动没有问题,但是实时数据引入了延迟,导致这种情况的发生。

答案 1 :(得分:0)

首先按照以下建议更改goToWelcomeScreen方法。

protected void goToWelcomeScreen() {
    Intent intent = new Intent(this, WelcomeActivity.class);
    startActivity(intent);        
    finish();
}

开始下一个活动后,通话结束。 您的活动泄漏的原因可能是因为该活动似乎已经完成,但是实时数据观察者仍然处于活动状态。您必须将ViewModels与实时数据一起使用,并将活动附加到viewmodel的生命周期中。

这样做可以确保所有活动的活动观察者与活动一起被销毁。

情况2:在第二种情况下,活动没有泄漏,因为到执行sessionManager.getAuthedUser()时,您的活动已经完成,然后再附加观察者(因为goToWelcomeActivity届时将被执行)。

希望它有助于清除您的疑问,干杯!