从实时数据更改 activity 导致内存泄漏
Changing activity from live data is causing memory leak
我的代码:
@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 方法我的 activity 正在泄漏。这里泄漏日志:
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 更改为以下,那么我的 activity 不会泄漏。 (我从 subscribeObservers 方法中删除了 goToWelcomeScreen 调用。)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_navigation);
bottomNavigationView = findViewById(R.id.bottom_navigation);
subscribeObservers();
goToWelcomeScreen();
}
什么会导致这个问题?我该如何解决?
谢谢
首先按照下面的建议更改您的 goToWelcomeScreen 方法。
protected void goToWelcomeScreen() {
Intent intent = new Intent(this, WelcomeActivity.class);
startActivity(intent);
finish();
}
下次开始后调用 finish activity。
您的 activity 泄漏的原因可能是因为 activity 似乎已完成,但实时数据观察器仍处于活动状态。您必须将 ViewModel 与实时数据一起使用,并将您的 activity 附加到视图模型的生命周期。
这样做可以确保所有活跃的 activity 观察者与 activity 一起被摧毁。
案例 2:在第二种情况下,activity 没有泄漏,因为在执行 sessionManager.getAuthedUser() 时,您的 activity 在附加观察者之前已经完成(因为goToWelcomeActivity 将在那时执行)。
希望它能帮助您消除疑虑,干杯!
这被 LeakCanary 识别为 "library leak",这意味着它看起来像是 Android 框架中的已知泄漏。查看您粘贴的跟踪中的描述:
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() 期间切换活动,则不会出现问题,但实时数据引入了导致这种情况发生的延迟。
我的代码:
@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 方法我的 activity 正在泄漏。这里泄漏日志:
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 更改为以下,那么我的 activity 不会泄漏。 (我从 subscribeObservers 方法中删除了 goToWelcomeScreen 调用。)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_navigation);
bottomNavigationView = findViewById(R.id.bottom_navigation);
subscribeObservers();
goToWelcomeScreen();
}
什么会导致这个问题?我该如何解决?
谢谢
首先按照下面的建议更改您的 goToWelcomeScreen 方法。
protected void goToWelcomeScreen() {
Intent intent = new Intent(this, WelcomeActivity.class);
startActivity(intent);
finish();
}
下次开始后调用 finish activity。 您的 activity 泄漏的原因可能是因为 activity 似乎已完成,但实时数据观察器仍处于活动状态。您必须将 ViewModel 与实时数据一起使用,并将您的 activity 附加到视图模型的生命周期。
这样做可以确保所有活跃的 activity 观察者与 activity 一起被摧毁。
案例 2:在第二种情况下,activity 没有泄漏,因为在执行 sessionManager.getAuthedUser() 时,您的 activity 在附加观察者之前已经完成(因为goToWelcomeActivity 将在那时执行)。
希望它能帮助您消除疑虑,干杯!
这被 LeakCanary 识别为 "library leak",这意味着它看起来像是 Android 框架中的已知泄漏。查看您粘贴的跟踪中的描述:
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() 期间切换活动,则不会出现问题,但实时数据引入了导致这种情况发生的延迟。