Reputation: 4343
My code:
@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);
}
This is my session manager method:
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;
}
As you can see if i run goToWelcomeScreen method in observer my activity is leaking. Here leak logs:
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.)
An interesting thing is if i change my onCreate to below then my activity is not leaking. (I removed goToWelcomeScreen call from subscribeObservers method.)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_navigation);
bottomNavigationView = findViewById(R.id.bottom_navigation);
subscribeObservers();
goToWelcomeScreen();
}
What can cause to this problem? How can I resolve it?
Thanks
Upvotes: 0
Views: 1174
Reputation: 8349
This is identified by LeakCanary as a "library leak", which means it looks like a known leak in the Android Framework. See the description in the trace you pasted:
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.
It sounds like you found a way to make the problem go away, which is a great first step. Looks like if you're switching activities during onCreate() you don't have the issue, but the live data is introducing a delay that's causing this to happen.
Upvotes: 1
Reputation: 16
First make changes to your goToWelcomeScreen method as suggested below.
protected void goToWelcomeScreen() {
Intent intent = new Intent(this, WelcomeActivity.class);
startActivity(intent);
finish();
}
Call finish after starting next activity. The reason your activity is leaking is probably because the activity seems to be finished but the live data observers are still active. You've to use ViewModels with live data and attach your activity to the lifecycle of viewmodel.
Doing that ensures that all your active activity observers are destroyed along with the activity.
Case 2: In the second case the activity is not leaking because by the time your sessionManager.getAuthedUser() gets executed your activity is already finished before attaching the observer(since goToWelcomeActivity gets executed by then).
Hope it helped clear your doubts, Cheers!
Upvotes: 0