Android Context question
tleyden opened this issue · 8 comments
From Jesse Anderson via email:
We have a question about the Activity used with the AndroidContext. Here is an example line:
Could the Activity be leaked as a result of passing it as an argument to the AndroidContext? Is this kept as a member of the Manager object?
From what I can tell the lifecycles of the GrocerySync MainActivity and the CBLite Manager are aligned OK. I dug into the code trying figure out if the CBLite Manager could be holding onto an activity that has been destroyed, but I'm not seeing that possibility.
I know for TodoLite-Android I think we use the application context rather than the activity context, and maybe there was a reason for that.
My thoughts about this
- CBL Manager retain context which is given as constructor parameter
- In case of GrocerySync's MainActivity, CBL Manager is member variable of MainActivity. So MainActivity should not be destroyed before CBL Manager is destroyed.
- It could cause leak if given context is different context (eg DetailActivity).
- In case of creating new Android project from Android Studio, I have never seen Application class is generated as an initial (foundation) class. So I don't recommend that Manager is maintained in Application class
Three approaches
- Give MainActivity instance which should be registered as Main Activity in AndroidManifest.xml. And Manager's reference should be managed by MainActivity.
- Give Application instance from MainActivity.getApplication(). And And Manager's reference should be managed by MainActivity.
- In case customer want to use CBL from specific Activity, give specific Activity to Manager's constructor, and Manager's reference should be managed by same Activity.
For a simple single activity application, keeping the manager inside the database should be fine. If your application tends to have multiple activities, keeping the manager inside the Main Activity is not always safe - there is no guarantee that the Main Activity will not be destroyed when it was push onto the back stack.
If you have more than activities (assume that more than one activity beside the main activity also interact with the database), I would suggest to keep the manager outside an Activity. You could make a Singleton class to manage your manager as well as your replicators if available and you could get the application context by calling Context.getApplicationContext().
The reason that I use Application to manage the manager, database, and replicators is that I could get a benefit of the application's life cycle methods (similar to iOS's AppDelegate) to initialize those components but you could achieve the same functionality with a singleton class.
I have considered about best practice of initialize Manager object since I observed a programmer at Hackathon tried to initialize Manager object in Button.onClick event handler. @pasin and I discussed about this last week.
I did some research about how android maintain Activities. In case of multiple activities, when new activity becomes active/visible, original (parent) activity is put in back stack with stop state, not destroyed. see http://developer.android.com/guide/components/tasks-and-back-stack.html . So, if Manager object is created and maintained by MainActivity, it is safe.
In case user presses back button to enter home screen from application, MainActivity might be destroyed. So, next time user starts application, MainActivity could be initialized and also Manager. This is expected behavior.
Application can create multiple MainActivity instances if developer wants. This case multiple Manager instances are created. This is programmer's responsibility.
If developer wants to use Manager as a singleton, instantiating Manager with Application is good approach as @pasin mentioned in previous comments. Also, if application should run in background mode, this could be also good approach. (I did not yet research about background mode.)
Following is my assumption. It seems Google tries to hide Application class. My assumption is that Google does not want developer to store many objects in Application class which could be on memory even though Application is invisible/inactive, because Android device's resource is limited. Based on my assumption, MainActivity (Launcher Activity) is good place to initialize and store Manager object in general.
@hideki, from the "Tasks and Back Stack" document in the 'Saving Activity State' section:
"When the system stops one of your activities (such as when a new activity starts or the task moves to the background), the system might destroy that activity completely if it needs to recover system memory. When this happens, information about the activity state is lost. If this happens, the system still knows that the activity has a place in the back stack, but when the activity is brought to the top of the stack the system must recreate it (rather than resume it). In order to avoid losing the user's work, you should proactively retain it by implementing the onSaveInstanceState() callback methods in your activity."
I experienced this kind of state in the past when I developed an android application so keeping in the MainActivity is not safe from being destroyed.
@pasin If there is risk that Activities in Back stack could be destroyed, creating Manager with Application could be safer approach in any cases. In the future, it is good idea to provide sub-class of Application which initializes Manager object as part of CBL Framework.
I'm not sure if we should provide the Application subclass. Based on the Android Application class doc (http://developer.android.com/reference/android/app/Application.html), creating a singleton class to manage global states or objects is preferable to subclassing the Application so it really depends on a developer's preferred choice. One thing that we should do is to provide 'Best Practice and available alternative solutions' in our Android document.
I've changed all of the Couchbase Mobile class' Android code to use getApplicationContext()
. I'd also recommend that you update the CBM GitHub tutorials to use getApplicationContext()
too. They serve as the source of copy/paste.