I'm using rxAndroid with an MVVM architecture. In my Activity, I need to bind my streams then fetch data from the server, which will eventually call the downstream.
My reasoning is the following:
- I need to unsubscribe my streams in onStop() (onDestroy can cause memory leaks).
-> therefore I need to subscribe to them in onStart() (otherwise coming back from background doesn't recreate the stream).
-> therefore I need to fetch my data in or after onStart(), because the stream has to be bound before I can start calling the upstream.
But in this case, every time I come back to the app after background, it's going to call the fetch method, which is not a behavior I want. Ideally I'd like to call the fetch method once, for example in onCreate().
How can I nicely deal with this problem? I've tried finding solutions on SO and other websites but no luck.
So what you are saying is you ONLY want the stream recreated if it hadn't completed yet when the Activity was destroyed and restarted? In this case consider a BehaviorSubject.
It would live outside of the context of the Activity (be sure not to hold a reference to any Activity in the subject, a weak reference would be okay.)
It will deliver the last fetched result when you reconnect to it from the new activity.
Related
Consider this scenario : There are two activities Activity A do network call and display count of tasks and the Activity B fetch tasks from database, or from same network API call if data is not available. While A is busy in network call user can traverse to Activity B. I am using Retrofit2 and Rxjava2.
My question is when Activity A is doing network call and user go to Activity B, and still the network call is running then, I want the object of Observable<> created on Activity A,also on Activity B because if I do not get callback then another network call for same task would be done on Activity B which is not feasible.
Anyone have idea How I can persist object of Observable<> created on Activity A ,by using this observable object I will get callback on Activity A as well as on Activity B and do some functionality.
Seems what you like is to do work in the background, that will be accessible to a different parts of your app (Activity), in this case you might want to consider using an AndroidService.
In short create a Service from ActivityA that will trigger fetching the data, then in the ActivityB connect to this Service to get an Observable with the data fetched in ActivityA.
For persisting the data and avoid making 2 calls to the same API, you can use operators like cache() that will multicast your Observable and cache the data, and then it can be available to ActivityB as well.
Another option as suggested is to hold a static reference to the Observable or reference at Application level, that will be available to your entire app, but then you should be careful with leaks as detailed by #Blackbelt.
My question is when Activity A is doing network call and user go to
Activity B, and still the network call is running then,
You should unsubscribe the observable in order to cancel the network call. If you keep a strong reference to it, you will leak the activity for the time the network call runs. You should decouple this logic
I want the
object of Observable<> created on Activity A,also on Activity B
because if I do not get callback then another network call for same
task would be done on Activity B which is not feasible
It is feasible. You could have a singleton, or better use Dagger to do that, that handles the network call, and emits the result to a BehaviourSubject to which you will subscribe. The nice thing of BehaviourSubject is that the latest value will always be emitted
to new subscribers.
I currently store my app data for an Activity in a Parcelable object. On orientation change, I save it and load it by using onSaveInstanceState and onRestoreInstanceState.
I want to save the data to the database when the user exits the activity.
And, I want to minimize the database calls. So,
Where should I write the code to save the data to database? Is it onPause(), onResume(), onStop() or onDestroy()?
If you're really talking about best practices, then none of the above.
An Activity is View-tier object. Some might argue that it is a hybrid Controller and View. In either case, it's not a Model or Business-tier object.
If your data is important enough to write to a database, then I'm guessing that it's not view state, it's probably domain data. So, the best practice would be to let the Model/Business tier (which is completely decoupled from the Activity) handle it. And given the nature of mobile apps, I'd write to the database (asynchronously, of course) whenever the data changes, without regards to the lifecycle of the various Android components.
I currently store my app data for an Activity in a Parcelable object
Since the rest of your question is about database I/O, please note that Parcelable has nothing to do with database I/O.
I want to save the data to the database when the user exits the activity.
I would recommend that you save the data when the data changes, rather than wait and risk losing that data (e.g., app crashes).
Is it onPause(), onResume(), onStop() or onDestroy()?
It is not onResume(). That lifecycle method is called as part of activity coming onto the screen, not when the activity is leaving.
It is not onDestroy(), as there is no guarantee that onDestroy() will be called.
Either of the other two are reasonable. The primary difference is visibility:
If an activity takes over the foreground, but that activity is themed like a dialog or otherwise allows your activity to peek through, you are only paused
If an activity takes over the foreground, and your activity is no longer visible, you are paused and then stopped
A correction to the accepted answer: (I am surprised it is Mark Murphy!)
From the android documentation on activity lifecycle:
onPause() execution is very brief and does not necessarily afford
enough time to perform save operations. For this reason,
you should not use onPause() to save application or user
data, make network calls, or execute database transactions;
I have my MainActivity which gives the user a selection of pages to open, all of which involve downloading some data from the internet and displaying it. To save the user waiting when they choose their page I've made an AsyncTask as a subclass of MainActivity which produces an object DATAwhen the download is complete.
How would I pass DATA on to the SecondActivity in the following circumstances:
The user chooses the SecondActivity before the AsyncTask download has completed.
The download completes before the user chooses the SecondActivity.
the AsyncTask doesn't have to be a sub-class of MainActivity its just been tidy to do it that way so far,
thanks for the help!
Here's one way to do this:
Create a reference to your data in your Application. The Android Application is a good place to store global data. Next, populate the data via your AsyncTask (Watch out for the pitfalls of using an AsyncTask). You can now access your data via a call similar to this: ((MyApplication)getApplication).mydata
As you mentioned, two scenarios can come up. Either the data has been populated, or not. To handle this, use an observer that observes changes to the data. Have SecondActivity register as an observer when the data is null. When the data is available your SecondActivity's update method will get called and you can do whatever you please with it. Finally, make sure to unregister from being an observer.
Hope this helps.
Passing information directly between activities works only if it is Parcellable (via Intent). Almost anything could be made Parcellable but it is not always a good idea especially when the amount of data is large.
The next problem is that your AsyncTask most likely keeps the Context of your first activity alive when it is running longer than the activity lasts. Activity instances are quite often recreated when you rotate the device and naive implementations tend to start another asynctask in the new instance and end up with multiple tasks that download the same data. You would need to pass the reference of a running task between instances of the same Activity.
The simplest solution is probably to create a singleton (or a Service) accessible from both activities that hosts the AsyncTask & loads the data. If it requires a Context use getApplicationContext() since that's safe to use outside the lifetime of Activites.
Activities could register themselves as listeners for "data loaded" events while they are active.
I've recently struggled with AsyncTask and had difficulty having the UI behave while the task was running in the background. While there are comments around that services aren't really appropriate for the sort of thing you're describing, I've found them much easier to work with. You might check intentService as a middle ground. Good tut's can be found here and, specifically concerning intentService, here.
The documentation suggests that the data should be committed/read in onPause()/onResume().
Yet when the application is no longer in the foreground, its data structures remain intact, which suggests that one could delay committing/reading data until the application is no longer visible, i.e. in onStop()/onStart(). Particularly since onStop() is guaranteed to be called before onDestroy().
Is it perhaps the case that either approach is suitable? Is the documentation giving here merely a guideline?
Update
Suppose your application needed to save relatively substantial data, say edits to a large image. One would then surely not write/read in onPause()/onResume(), lest the user experience become sluggish. One would in that case choose instead to write/read in onStop()/onStart(). Is that true?
The problem with using onStop is that you have no guarantees on when it will be called since the only sure thing is that it will be called before onDestroy. If you wait until onStop to commit your data it may be to late for another activity to show/use any of those changes. Same thing applies to onStart, your activity may not need to be restarted if it was just in the background so you'll have stale data. Using onResume and onPause guarantees that your data will always be current, commits are made as soon as the activity goes to the background and new data is loaded as soon as it becomes visible.
Yes, it is just a guideline (and generally a good one). It is up to you exactly when you want to commit changes. I personally like to create Store Objects that allow a simplification of Databases or SharedPreferences, and when a change is made, I commit those changes immediately. For simple data storage, this will be quick and invisible to the user. For large data sets, this may take more time, and you may wish to make those writes on a time interval, as well as in onPause.
As for when to read - you can read whenever, but again longer reads will often affect the user experience, unless you have taken care of it in another thread, such as with an AsyncTask.
To Further answer your update:
It depends on the developer, however I would write in onPause() and if necessary, read in a separate thread, probably initialized with onResume(). I may also write data out on a scheduled interval using a Timer thread, depending on how it would affect the user experience for the current session, and if it would be catastrophic for the phone to turn off and lose all data before onPause() is called.
The true answer to this is, onPause is the only method you are guaranteed to get called before Android can destroy your process. If a user leaves your app to take a phone call, and Android decides to close your process, its entirely legal for you to only get an onPause call. If you hadn't saved your state there, when the user hits the back button, you will end up recreating your activity in a different state than the user left it in.
I'm building a new android app which uses data from a server through different activities.
currently I have 2 activities, the main one connects to the server and creates the singleton object (which is shared for all activities) using the data from the server.
obviously I have an instance of the data held in the singleton class, and get the object from a static method.
I know that this is not the best way to implement a shared data between activities - I can pass them through intents (but I'm planning to create more 3-4 activities which uses the same data so why to pass them when you can have them in a static context global for the app).
What this implementation got me into is this problem:
when the user switches to another app at the second activity, and my app stays for some time on background, android frees the memory used by the app, and when returning to it, I get null pointer exception when using one of the fields of the singleton object!
I solved the problem by returning to the main activity (if the object is null) to recreate the data, but this makes the activity reconnect every time it's destroyed (it's not optimal since the data on the server doesn't change much).
I know that I have to save the server data onDestroy and to recreate the object every time I return to the activity but this must happen on every activity, and the data is about 4-5KB meaning it needs to be written to a file, and parsing it takes time (accessing phones SD card).
I'm starting to think on using Parcelable object to share the data through intents - I think android can save and restore the data of the intents automatically and in a optimal way.
(ref: http://bimbim.in/post/2010/09/27/Android-Passing-object-from-one-activity-to-another.aspx)
The question is, will intents solve my problem?
Is there another ways? Better, faster ways?
Thanx!
I am sure the problem relates to the life cycle of activities. When Android kills your activity, you can save your data as a bundle by using your main activity's onRestoreInstanceState() method, so when the activity gets recreated again, you can restore your data on the activity's onCreate() method (see here as an example). You could also use SharedPreferences in your activity's onResume() and onPause() methods. With both approaches, the idea is the same: that you save your object/data temporarily on disk, and restore it later.