understanding leaks in android MVP
up vote
0
down vote
favorite
I've read some similar questions on here but due to the lack of code presented I'm not certain my question describes the same scenarios.
I hope that the following snippets and questions will help others clarify what and when something is leaked in this MVP implementation: https://github.com/frogermcs/GithubClient/tree/1bf53a2a36c8a85435e877847b987395e482ab4a
BaseActivity.java:
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActivityComponent();
}
protected abstract void setupActivityComponent();
}
SplashActivityModule.java:
@Module
public class SplashActivityModule {
private SplashActivity splashActivity;
public SplashActivityModule(SplashActivity splashActivity) {
this.splashActivity = splashActivity;
}
@Provides
@ActivityScope
SplashActivity provideSplashActivity() {
return splashActivity;
}
@Provides
@ActivityScope
SplashActivityPresenter
provideSplashActivityPresenter(Validator validator, UserManager
userManager, HeavyLibraryWrapper heavyLibraryWrapper) {
return new SplashActivityPresenter(splashActivity, validator,
userManager, heavyLibraryWrapper);
}
}
SplashActivityPresenter is injected inside SplashActivity.java:
public class SplashActivity extends BaseActivity {
...
@Inject
SplashActivityPresenter presenter;
@Override
protected void setupActivityComponent() {
GithubClientApplication.get(this)
.getAppComponent()
.plus(new SplashActivityModule(this))
.inject(this);
}
SplashActivityPresenter.java:
public class SplashActivityPresenter {
public String username;
private SplashActivity splashActivity;
private Validator validator;
private UserManager userManager;
private HeavyLibraryWrapper heavyLibraryWrapper;
public SplashActivityPresenter(SplashActivity splashActivity,
Validator validator, UserManager userManager,
HeavyLibraryWrapper heavyLibraryWrapper) {
this.splashActivity = splashActivity;
this.validator = validator;
this.userManager = userManager;
this.heavyLibraryWrapper = heavyLibraryWrapper;
//This calls should be delivered to ExternalLibrary right after it will be initialized
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
}
public void onShowRepositoriesClick() {
if (validator.validUsername(username)) {
splashActivity.showLoading(true);
userManager.getUser(username).subscribe(new
SimpleObserver<User>() {
@Override
public void onNext(User user) {
splashActivity.showLoading(false);
splashActivity.showRepositoriesListForUser(user);
}
@Override
public void onError(Throwable e) {
splashActivity.showLoading(false);
splashActivity.showValidationError();
}
});
} else {
splashActivity.showValidationError();
}
}
}
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the observer's callbacks since the activity is destroyed.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity onDestroy()).
- Someone told me that doing (3) is not enough, and that inside
onDestroy()
we must also set the activity instance tonull
. I disagree because unsubscribing the subscription cancels the request, preventing the callbacks(likeonNext(User)
) that reference the activity from being invoked. - He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the activity references it. I disagree because a new presenter is created per rotation and initialized to the injected presenter when BaseActivity onCreate invokes setupActivityComponent. The old presenter is automatically garbage collected as a member of SplashActivity.
Can someone respond to the points I outlined above so I can confirm my understanding or learn where I may be wrong? Thank you
java android memory-leaks dagger-2 mvp
add a comment |
up vote
0
down vote
favorite
I've read some similar questions on here but due to the lack of code presented I'm not certain my question describes the same scenarios.
I hope that the following snippets and questions will help others clarify what and when something is leaked in this MVP implementation: https://github.com/frogermcs/GithubClient/tree/1bf53a2a36c8a85435e877847b987395e482ab4a
BaseActivity.java:
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActivityComponent();
}
protected abstract void setupActivityComponent();
}
SplashActivityModule.java:
@Module
public class SplashActivityModule {
private SplashActivity splashActivity;
public SplashActivityModule(SplashActivity splashActivity) {
this.splashActivity = splashActivity;
}
@Provides
@ActivityScope
SplashActivity provideSplashActivity() {
return splashActivity;
}
@Provides
@ActivityScope
SplashActivityPresenter
provideSplashActivityPresenter(Validator validator, UserManager
userManager, HeavyLibraryWrapper heavyLibraryWrapper) {
return new SplashActivityPresenter(splashActivity, validator,
userManager, heavyLibraryWrapper);
}
}
SplashActivityPresenter is injected inside SplashActivity.java:
public class SplashActivity extends BaseActivity {
...
@Inject
SplashActivityPresenter presenter;
@Override
protected void setupActivityComponent() {
GithubClientApplication.get(this)
.getAppComponent()
.plus(new SplashActivityModule(this))
.inject(this);
}
SplashActivityPresenter.java:
public class SplashActivityPresenter {
public String username;
private SplashActivity splashActivity;
private Validator validator;
private UserManager userManager;
private HeavyLibraryWrapper heavyLibraryWrapper;
public SplashActivityPresenter(SplashActivity splashActivity,
Validator validator, UserManager userManager,
HeavyLibraryWrapper heavyLibraryWrapper) {
this.splashActivity = splashActivity;
this.validator = validator;
this.userManager = userManager;
this.heavyLibraryWrapper = heavyLibraryWrapper;
//This calls should be delivered to ExternalLibrary right after it will be initialized
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
}
public void onShowRepositoriesClick() {
if (validator.validUsername(username)) {
splashActivity.showLoading(true);
userManager.getUser(username).subscribe(new
SimpleObserver<User>() {
@Override
public void onNext(User user) {
splashActivity.showLoading(false);
splashActivity.showRepositoriesListForUser(user);
}
@Override
public void onError(Throwable e) {
splashActivity.showLoading(false);
splashActivity.showValidationError();
}
});
} else {
splashActivity.showValidationError();
}
}
}
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the observer's callbacks since the activity is destroyed.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity onDestroy()).
- Someone told me that doing (3) is not enough, and that inside
onDestroy()
we must also set the activity instance tonull
. I disagree because unsubscribing the subscription cancels the request, preventing the callbacks(likeonNext(User)
) that reference the activity from being invoked. - He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the activity references it. I disagree because a new presenter is created per rotation and initialized to the injected presenter when BaseActivity onCreate invokes setupActivityComponent. The old presenter is automatically garbage collected as a member of SplashActivity.
Can someone respond to the points I outlined above so I can confirm my understanding or learn where I may be wrong? Thank you
java android memory-leaks dagger-2 mvp
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I've read some similar questions on here but due to the lack of code presented I'm not certain my question describes the same scenarios.
I hope that the following snippets and questions will help others clarify what and when something is leaked in this MVP implementation: https://github.com/frogermcs/GithubClient/tree/1bf53a2a36c8a85435e877847b987395e482ab4a
BaseActivity.java:
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActivityComponent();
}
protected abstract void setupActivityComponent();
}
SplashActivityModule.java:
@Module
public class SplashActivityModule {
private SplashActivity splashActivity;
public SplashActivityModule(SplashActivity splashActivity) {
this.splashActivity = splashActivity;
}
@Provides
@ActivityScope
SplashActivity provideSplashActivity() {
return splashActivity;
}
@Provides
@ActivityScope
SplashActivityPresenter
provideSplashActivityPresenter(Validator validator, UserManager
userManager, HeavyLibraryWrapper heavyLibraryWrapper) {
return new SplashActivityPresenter(splashActivity, validator,
userManager, heavyLibraryWrapper);
}
}
SplashActivityPresenter is injected inside SplashActivity.java:
public class SplashActivity extends BaseActivity {
...
@Inject
SplashActivityPresenter presenter;
@Override
protected void setupActivityComponent() {
GithubClientApplication.get(this)
.getAppComponent()
.plus(new SplashActivityModule(this))
.inject(this);
}
SplashActivityPresenter.java:
public class SplashActivityPresenter {
public String username;
private SplashActivity splashActivity;
private Validator validator;
private UserManager userManager;
private HeavyLibraryWrapper heavyLibraryWrapper;
public SplashActivityPresenter(SplashActivity splashActivity,
Validator validator, UserManager userManager,
HeavyLibraryWrapper heavyLibraryWrapper) {
this.splashActivity = splashActivity;
this.validator = validator;
this.userManager = userManager;
this.heavyLibraryWrapper = heavyLibraryWrapper;
//This calls should be delivered to ExternalLibrary right after it will be initialized
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
}
public void onShowRepositoriesClick() {
if (validator.validUsername(username)) {
splashActivity.showLoading(true);
userManager.getUser(username).subscribe(new
SimpleObserver<User>() {
@Override
public void onNext(User user) {
splashActivity.showLoading(false);
splashActivity.showRepositoriesListForUser(user);
}
@Override
public void onError(Throwable e) {
splashActivity.showLoading(false);
splashActivity.showValidationError();
}
});
} else {
splashActivity.showValidationError();
}
}
}
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the observer's callbacks since the activity is destroyed.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity onDestroy()).
- Someone told me that doing (3) is not enough, and that inside
onDestroy()
we must also set the activity instance tonull
. I disagree because unsubscribing the subscription cancels the request, preventing the callbacks(likeonNext(User)
) that reference the activity from being invoked. - He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the activity references it. I disagree because a new presenter is created per rotation and initialized to the injected presenter when BaseActivity onCreate invokes setupActivityComponent. The old presenter is automatically garbage collected as a member of SplashActivity.
Can someone respond to the points I outlined above so I can confirm my understanding or learn where I may be wrong? Thank you
java android memory-leaks dagger-2 mvp
I've read some similar questions on here but due to the lack of code presented I'm not certain my question describes the same scenarios.
I hope that the following snippets and questions will help others clarify what and when something is leaked in this MVP implementation: https://github.com/frogermcs/GithubClient/tree/1bf53a2a36c8a85435e877847b987395e482ab4a
BaseActivity.java:
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActivityComponent();
}
protected abstract void setupActivityComponent();
}
SplashActivityModule.java:
@Module
public class SplashActivityModule {
private SplashActivity splashActivity;
public SplashActivityModule(SplashActivity splashActivity) {
this.splashActivity = splashActivity;
}
@Provides
@ActivityScope
SplashActivity provideSplashActivity() {
return splashActivity;
}
@Provides
@ActivityScope
SplashActivityPresenter
provideSplashActivityPresenter(Validator validator, UserManager
userManager, HeavyLibraryWrapper heavyLibraryWrapper) {
return new SplashActivityPresenter(splashActivity, validator,
userManager, heavyLibraryWrapper);
}
}
SplashActivityPresenter is injected inside SplashActivity.java:
public class SplashActivity extends BaseActivity {
...
@Inject
SplashActivityPresenter presenter;
@Override
protected void setupActivityComponent() {
GithubClientApplication.get(this)
.getAppComponent()
.plus(new SplashActivityModule(this))
.inject(this);
}
SplashActivityPresenter.java:
public class SplashActivityPresenter {
public String username;
private SplashActivity splashActivity;
private Validator validator;
private UserManager userManager;
private HeavyLibraryWrapper heavyLibraryWrapper;
public SplashActivityPresenter(SplashActivity splashActivity,
Validator validator, UserManager userManager,
HeavyLibraryWrapper heavyLibraryWrapper) {
this.splashActivity = splashActivity;
this.validator = validator;
this.userManager = userManager;
this.heavyLibraryWrapper = heavyLibraryWrapper;
//This calls should be delivered to ExternalLibrary right after it will be initialized
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
}
public void onShowRepositoriesClick() {
if (validator.validUsername(username)) {
splashActivity.showLoading(true);
userManager.getUser(username).subscribe(new
SimpleObserver<User>() {
@Override
public void onNext(User user) {
splashActivity.showLoading(false);
splashActivity.showRepositoriesListForUser(user);
}
@Override
public void onError(Throwable e) {
splashActivity.showLoading(false);
splashActivity.showValidationError();
}
});
} else {
splashActivity.showValidationError();
}
}
}
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the observer's callbacks since the activity is destroyed.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity onDestroy()).
- Someone told me that doing (3) is not enough, and that inside
onDestroy()
we must also set the activity instance tonull
. I disagree because unsubscribing the subscription cancels the request, preventing the callbacks(likeonNext(User)
) that reference the activity from being invoked. - He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the activity references it. I disagree because a new presenter is created per rotation and initialized to the injected presenter when BaseActivity onCreate invokes setupActivityComponent. The old presenter is automatically garbage collected as a member of SplashActivity.
Can someone respond to the points I outlined above so I can confirm my understanding or learn where I may be wrong? Thank you
java android memory-leaks dagger-2 mvp
java android memory-leaks dagger-2 mvp
asked Nov 10 at 19:13
HukeLau_DABA
1,19842144
1,19842144
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
accepted
I'll do my best to answer these as accurately as possible (welcome edits if inaccurate), but this answer is good reading: https://stackoverflow.com/a/10968689/4252352
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the
observer's callbacks since the activity is destroyed.
A : This is correct, however will be short term memory leak until the resources are cleared after dispose. However this should not be relied upon, if you had a Flowable/Observable it may never dispose and clear resources. It is important to note that all lambdas in a Rx chain (usually operators like map
, filter
etc) that don't reference (capture) the enclosing class are leak free.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
A. Correct, you never have an active subscription.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity
onDestroy())
A This should stop the issue. However a better approach, and in MVP the View
should be an abstraction/interface and your Presenter
should have entry and exit points for the view and not on the constructor i.e bind(view : View)
and unbind()
<-- cleanup here, a presenter should not be aware of specific android hook callbacks. This has massive advantages, not only from a OOP (program to interfaces not implementations) aspect, but also testing.
- Someone told me that doing (3) is not enough, and that inside onDestroy() we must also set the activity instance to null. I disagree
because unsubscribing the subscription cancels the request, preventing
the callbacks(like onNext(User)) that reference the activity from
being invoked.
A. I'd would firstly ask then to clarify their reasoning. As your Presenter
is scoped to the Activity
(they both have the same lifecycle) unsubscribing should be enough. However if your Presenter
has a longer lifecycle than the Activity
it would be necessary to remove the reference (this may have been the rationale of the person who you spoke to).
- He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the
activity references it. I disagree because a new presenter is created
per rotation and initialized to the injected presenter when
BaseActivity onCreate invokes setupActivityComponent. The old
presenter is automatically garbage collected as a member of
SplashActivity.
A. The Presenter
is leaked, if the Activity
is leaked (along with everything the activity references!)
Thanks! "It is important to note that all anonymous classes in a Rx chain (usually operators like map, filter etc) that don't reference (capture) the enclosing class are leak free." Suppose we have this in an activity: Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { return s + " -Dan"; } }) .subscribe(s -> System.out.println(s)); As an anon class, it holds a reference to the enclosing Activity so isnt there an implicit leak even if we arent dir referencing it?
– HukeLau_DABA
Nov 11 at 19:13
I should have clarified that with operatorsmap
,filter
etc are lambdas, which when non-capturing don't hold an implicit reference - normal inner classes / non lambdas will hold the implicit reference always - I was trying to distinguish between the operators (which tend to be functional interfaces) and subscribers, which in your example is not. Follow up reading if you're interested found on this answer stackoverflow.com/a/28447015/4252352
– Mark Keen
Nov 11 at 21:02
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
accepted
I'll do my best to answer these as accurately as possible (welcome edits if inaccurate), but this answer is good reading: https://stackoverflow.com/a/10968689/4252352
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the
observer's callbacks since the activity is destroyed.
A : This is correct, however will be short term memory leak until the resources are cleared after dispose. However this should not be relied upon, if you had a Flowable/Observable it may never dispose and clear resources. It is important to note that all lambdas in a Rx chain (usually operators like map
, filter
etc) that don't reference (capture) the enclosing class are leak free.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
A. Correct, you never have an active subscription.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity
onDestroy())
A This should stop the issue. However a better approach, and in MVP the View
should be an abstraction/interface and your Presenter
should have entry and exit points for the view and not on the constructor i.e bind(view : View)
and unbind()
<-- cleanup here, a presenter should not be aware of specific android hook callbacks. This has massive advantages, not only from a OOP (program to interfaces not implementations) aspect, but also testing.
- Someone told me that doing (3) is not enough, and that inside onDestroy() we must also set the activity instance to null. I disagree
because unsubscribing the subscription cancels the request, preventing
the callbacks(like onNext(User)) that reference the activity from
being invoked.
A. I'd would firstly ask then to clarify their reasoning. As your Presenter
is scoped to the Activity
(they both have the same lifecycle) unsubscribing should be enough. However if your Presenter
has a longer lifecycle than the Activity
it would be necessary to remove the reference (this may have been the rationale of the person who you spoke to).
- He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the
activity references it. I disagree because a new presenter is created
per rotation and initialized to the injected presenter when
BaseActivity onCreate invokes setupActivityComponent. The old
presenter is automatically garbage collected as a member of
SplashActivity.
A. The Presenter
is leaked, if the Activity
is leaked (along with everything the activity references!)
Thanks! "It is important to note that all anonymous classes in a Rx chain (usually operators like map, filter etc) that don't reference (capture) the enclosing class are leak free." Suppose we have this in an activity: Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { return s + " -Dan"; } }) .subscribe(s -> System.out.println(s)); As an anon class, it holds a reference to the enclosing Activity so isnt there an implicit leak even if we arent dir referencing it?
– HukeLau_DABA
Nov 11 at 19:13
I should have clarified that with operatorsmap
,filter
etc are lambdas, which when non-capturing don't hold an implicit reference - normal inner classes / non lambdas will hold the implicit reference always - I was trying to distinguish between the operators (which tend to be functional interfaces) and subscribers, which in your example is not. Follow up reading if you're interested found on this answer stackoverflow.com/a/28447015/4252352
– Mark Keen
Nov 11 at 21:02
add a comment |
up vote
0
down vote
accepted
I'll do my best to answer these as accurately as possible (welcome edits if inaccurate), but this answer is good reading: https://stackoverflow.com/a/10968689/4252352
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the
observer's callbacks since the activity is destroyed.
A : This is correct, however will be short term memory leak until the resources are cleared after dispose. However this should not be relied upon, if you had a Flowable/Observable it may never dispose and clear resources. It is important to note that all lambdas in a Rx chain (usually operators like map
, filter
etc) that don't reference (capture) the enclosing class are leak free.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
A. Correct, you never have an active subscription.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity
onDestroy())
A This should stop the issue. However a better approach, and in MVP the View
should be an abstraction/interface and your Presenter
should have entry and exit points for the view and not on the constructor i.e bind(view : View)
and unbind()
<-- cleanup here, a presenter should not be aware of specific android hook callbacks. This has massive advantages, not only from a OOP (program to interfaces not implementations) aspect, but also testing.
- Someone told me that doing (3) is not enough, and that inside onDestroy() we must also set the activity instance to null. I disagree
because unsubscribing the subscription cancels the request, preventing
the callbacks(like onNext(User)) that reference the activity from
being invoked.
A. I'd would firstly ask then to clarify their reasoning. As your Presenter
is scoped to the Activity
(they both have the same lifecycle) unsubscribing should be enough. However if your Presenter
has a longer lifecycle than the Activity
it would be necessary to remove the reference (this may have been the rationale of the person who you spoke to).
- He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the
activity references it. I disagree because a new presenter is created
per rotation and initialized to the injected presenter when
BaseActivity onCreate invokes setupActivityComponent. The old
presenter is automatically garbage collected as a member of
SplashActivity.
A. The Presenter
is leaked, if the Activity
is leaked (along with everything the activity references!)
Thanks! "It is important to note that all anonymous classes in a Rx chain (usually operators like map, filter etc) that don't reference (capture) the enclosing class are leak free." Suppose we have this in an activity: Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { return s + " -Dan"; } }) .subscribe(s -> System.out.println(s)); As an anon class, it holds a reference to the enclosing Activity so isnt there an implicit leak even if we arent dir referencing it?
– HukeLau_DABA
Nov 11 at 19:13
I should have clarified that with operatorsmap
,filter
etc are lambdas, which when non-capturing don't hold an implicit reference - normal inner classes / non lambdas will hold the implicit reference always - I was trying to distinguish between the operators (which tend to be functional interfaces) and subscribers, which in your example is not. Follow up reading if you're interested found on this answer stackoverflow.com/a/28447015/4252352
– Mark Keen
Nov 11 at 21:02
add a comment |
up vote
0
down vote
accepted
up vote
0
down vote
accepted
I'll do my best to answer these as accurately as possible (welcome edits if inaccurate), but this answer is good reading: https://stackoverflow.com/a/10968689/4252352
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the
observer's callbacks since the activity is destroyed.
A : This is correct, however will be short term memory leak until the resources are cleared after dispose. However this should not be relied upon, if you had a Flowable/Observable it may never dispose and clear resources. It is important to note that all lambdas in a Rx chain (usually operators like map
, filter
etc) that don't reference (capture) the enclosing class are leak free.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
A. Correct, you never have an active subscription.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity
onDestroy())
A This should stop the issue. However a better approach, and in MVP the View
should be an abstraction/interface and your Presenter
should have entry and exit points for the view and not on the constructor i.e bind(view : View)
and unbind()
<-- cleanup here, a presenter should not be aware of specific android hook callbacks. This has massive advantages, not only from a OOP (program to interfaces not implementations) aspect, but also testing.
- Someone told me that doing (3) is not enough, and that inside onDestroy() we must also set the activity instance to null. I disagree
because unsubscribing the subscription cancels the request, preventing
the callbacks(like onNext(User)) that reference the activity from
being invoked.
A. I'd would firstly ask then to clarify their reasoning. As your Presenter
is scoped to the Activity
(they both have the same lifecycle) unsubscribing should be enough. However if your Presenter
has a longer lifecycle than the Activity
it would be necessary to remove the reference (this may have been the rationale of the person who you spoke to).
- He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the
activity references it. I disagree because a new presenter is created
per rotation and initialized to the injected presenter when
BaseActivity onCreate invokes setupActivityComponent. The old
presenter is automatically garbage collected as a member of
SplashActivity.
A. The Presenter
is leaked, if the Activity
is leaked (along with everything the activity references!)
I'll do my best to answer these as accurately as possible (welcome edits if inaccurate), but this answer is good reading: https://stackoverflow.com/a/10968689/4252352
- If the user rotates the screen while the username is being fetched we are leaking the activity instance being referenced inside the
observer's callbacks since the activity is destroyed.
A : This is correct, however will be short term memory leak until the resources are cleared after dispose. However this should not be relied upon, if you had a Flowable/Observable it may never dispose and clear resources. It is important to note that all lambdas in a Rx chain (usually operators like map
, filter
etc) that don't reference (capture) the enclosing class are leak free.
- If the user rotates the screen without an in-progress fetch, the activity instance is not leaked.
A. Correct, you never have an active subscription.
- In order to fix this leak(1), we need to store the subscription and unsubscribe it in presenter.onDestroy() (called from SplashActivity
onDestroy())
A This should stop the issue. However a better approach, and in MVP the View
should be an abstraction/interface and your Presenter
should have entry and exit points for the view and not on the constructor i.e bind(view : View)
and unbind()
<-- cleanup here, a presenter should not be aware of specific android hook callbacks. This has massive advantages, not only from a OOP (program to interfaces not implementations) aspect, but also testing.
- Someone told me that doing (3) is not enough, and that inside onDestroy() we must also set the activity instance to null. I disagree
because unsubscribing the subscription cancels the request, preventing
the callbacks(like onNext(User)) that reference the activity from
being invoked.
A. I'd would firstly ask then to clarify their reasoning. As your Presenter
is scoped to the Activity
(they both have the same lifecycle) unsubscribing should be enough. However if your Presenter
has a longer lifecycle than the Activity
it would be necessary to remove the reference (this may have been the rationale of the person who you spoke to).
- He also told me that while (3) and (4) prevent the ACTIVITY leaking, the PRESENTER is leaked during rotation ALSO since the
activity references it. I disagree because a new presenter is created
per rotation and initialized to the injected presenter when
BaseActivity onCreate invokes setupActivityComponent. The old
presenter is automatically garbage collected as a member of
SplashActivity.
A. The Presenter
is leaked, if the Activity
is leaked (along with everything the activity references!)
edited Nov 11 at 22:35
answered Nov 11 at 15:51
Mark Keen
4,60941641
4,60941641
Thanks! "It is important to note that all anonymous classes in a Rx chain (usually operators like map, filter etc) that don't reference (capture) the enclosing class are leak free." Suppose we have this in an activity: Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { return s + " -Dan"; } }) .subscribe(s -> System.out.println(s)); As an anon class, it holds a reference to the enclosing Activity so isnt there an implicit leak even if we arent dir referencing it?
– HukeLau_DABA
Nov 11 at 19:13
I should have clarified that with operatorsmap
,filter
etc are lambdas, which when non-capturing don't hold an implicit reference - normal inner classes / non lambdas will hold the implicit reference always - I was trying to distinguish between the operators (which tend to be functional interfaces) and subscribers, which in your example is not. Follow up reading if you're interested found on this answer stackoverflow.com/a/28447015/4252352
– Mark Keen
Nov 11 at 21:02
add a comment |
Thanks! "It is important to note that all anonymous classes in a Rx chain (usually operators like map, filter etc) that don't reference (capture) the enclosing class are leak free." Suppose we have this in an activity: Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { return s + " -Dan"; } }) .subscribe(s -> System.out.println(s)); As an anon class, it holds a reference to the enclosing Activity so isnt there an implicit leak even if we arent dir referencing it?
– HukeLau_DABA
Nov 11 at 19:13
I should have clarified that with operatorsmap
,filter
etc are lambdas, which when non-capturing don't hold an implicit reference - normal inner classes / non lambdas will hold the implicit reference always - I was trying to distinguish between the operators (which tend to be functional interfaces) and subscribers, which in your example is not. Follow up reading if you're interested found on this answer stackoverflow.com/a/28447015/4252352
– Mark Keen
Nov 11 at 21:02
Thanks! "It is important to note that all anonymous classes in a Rx chain (usually operators like map, filter etc) that don't reference (capture) the enclosing class are leak free." Suppose we have this in an activity: Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { return s + " -Dan"; } }) .subscribe(s -> System.out.println(s)); As an anon class, it holds a reference to the enclosing Activity so isnt there an implicit leak even if we arent dir referencing it?
– HukeLau_DABA
Nov 11 at 19:13
Thanks! "It is important to note that all anonymous classes in a Rx chain (usually operators like map, filter etc) that don't reference (capture) the enclosing class are leak free." Suppose we have this in an activity: Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { return s + " -Dan"; } }) .subscribe(s -> System.out.println(s)); As an anon class, it holds a reference to the enclosing Activity so isnt there an implicit leak even if we arent dir referencing it?
– HukeLau_DABA
Nov 11 at 19:13
I should have clarified that with operators
map
, filter
etc are lambdas, which when non-capturing don't hold an implicit reference - normal inner classes / non lambdas will hold the implicit reference always - I was trying to distinguish between the operators (which tend to be functional interfaces) and subscribers, which in your example is not. Follow up reading if you're interested found on this answer stackoverflow.com/a/28447015/4252352– Mark Keen
Nov 11 at 21:02
I should have clarified that with operators
map
, filter
etc are lambdas, which when non-capturing don't hold an implicit reference - normal inner classes / non lambdas will hold the implicit reference always - I was trying to distinguish between the operators (which tend to be functional interfaces) and subscribers, which in your example is not. Follow up reading if you're interested found on this answer stackoverflow.com/a/28447015/4252352– Mark Keen
Nov 11 at 21:02
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53242509%2funderstanding-leaks-in-android-mvp%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown