I've been using Realm with UI thread writes until I found out the UI was unusable at some point with significant database volume.
I'm now trying to figure out how to properly orchestrate asynchronous writes in app with activities and fragments showing data list and screens to enter the data.
At this moment, my problem is:
I have an Activity A, with its own Realm instance, listing data and listening to realm changes to update itself.
If I want to add more data, I create an Activity B with its own Realm instance.
I've started listening to the onSuccess callback of the transaction to refresh() the Realm instance in order to trigger Realm listeners and have Activity A update itself. Unfortunately it doesn't work when I quickly go back to activity A because the Realm of Activity B is closed and onSuccess is not getting called.
At this point I'm not sure how should I organize all this. I see the option where I don't create Activity B and use fragments but having to do this everywhere in my app doesn't feel great and might be error prone.
What's the best practice for this use case?
Thanks a lot!
Related
Currently i want to create an app with some sort of registration and payment flow.
I use the MaterialStepper library to have one Activity with Fragments each representing one step in the flow.
The Activity has a Android Architecture Component ViewModel and the ViewModel contains several properties for the Fragment. I use LiveData and two-way Databinding for my input fields.
Some data is reused in several Fragments thats why i used only one ViewModel for several fragments.
When the application is in foreground it works like expected, Fragments get re-created and the fields keep their values.
My problem is now when I pause the activity and resume it later. The ViewModel itself can get recreated as well and therefore looses its data.
What is a good approach to avoid the loss of data in that situation?
I read in an article that you should store some values in onSaveInstanceState (e.g. a searchquery to recreate the ViewModel). But isn't that way to much in my situation for ~30 input fields?)
Is a Room database a good approach to insert/update values in the database when the user edits the input fields and observe that LiveData objects instead? (unfortunately i have no experience with Room yet)
I would be glad about any help or examples (:
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 am working on an android app to display the popular movies. I am able to fetch the result from the themoviedb.org API which gives me different values like movie-name, movie-poster-id, movie description, etc. Now I want to make another API call to fetch the posters. I already have one AsyncTask fetching the movie information, should I create another AsyncTask for images and call them one after the other ? Or there's a better way ?
AsyncTask may not be the best solution; if the device rotates the activity is destroyed and when the async task finishes, it no longer has an activity to return the data to.
If you want to use async tasks, consider using a fragment with setRetainInstance(true) to do the network call. Your activity would launch the fragment and, if destroyed, it would remove its listener from the fragment and then when recreated, it would attach its listener again - the fragment continues to run regardless of the activity life cycle.
That said, you are probably better off with other solutions, like an IntentService or a full Service, depending on your flow.
Look at Volley and Retrofit libraries to help you with the network calls as well.
I saw some articles about CursorLoader like this, but I still don't understand the particular purpose of using it.
I developed apps with SQL and cursor retrieving. The point is it was very fast. I queried and parsed cursor with >500 record and 8 columns by a few millisecond. So didn't notice any delay event on old phones. So why do I need to use it?
A CursorLoader is used ostensibly to query a ContentProvider with LoaderManager.LoaderCallbacks<Cursor>.
There are two things that you need to keep in mind for understanding the CursorLoader:
It loads data on a separate thread.
It monitors the underlying data source for updates, re-querying when changes are detected.
Now coming to the LoaderManager. Simply stated, the LoaderManager is responsible for managing one or more Loaders associated with an Activity or Fragment. Each Activity and each Fragment has exactly one LoaderManager instance that is in charge of starting, stopping, retaining, restarting, and destroying its Loaders. These events are sometimes initiated directly by the client, by calling initLoader(), restartLoader(), or destroyLoader(). Just as often, however, these events are triggered by major Activity/Fragment lifecycle events. For example, when an Activity is destroyed, the Activity instructs its LoaderManager to destroy and close its Loaders (as well as any resources associated with them, such as a Cursor).
The LoaderManager does not know how data is loaded, nor does it need to. Rather, the LoaderManager instructs its Loaders when to start/stop/reset their load, retaining their state across configuration changes and providing a simple interface for delivering results back to the client.
So you see, all this is not easily possible when you use a simple AsyncTask and query an SQLite database. This is why the framework provides CursorLoader and LoaderManager:
To perform queries on a separate thread.
To monitor the data source for changes and update the UI.
To integrate easily with the life cycle of Activity and Fragment.
The practical purpose is simply how Android handles UI elements (that is on the main thread). Basically, anything that may be a long running process, run it in a background thread so you don't lockup the main thread. This can't be said enough. After Gingerbread this has been more enforced by Android itself. Check out SQL helper. To get to the point in regards to opening an SQLite connection and its "speed":
Because they can be long-running, be sure that you call getWritableDatabase() or getReadableDatabase() in a background thread, such as with AsyncTask or IntentService.
By using CursorLoader, it makes your life easier if you need ContentResolver and are using SQLite DB. More importantly, it runs on the background. Just because you've never seen the DB lock up doesn't mean it doesn't happen. Better safe than sorry, and the main thread will thank you :)
Use the CursorLoader from Support Library to run asynchronous queries in the background. In this way, you ensure that data loading does not cause “Application Not Responding” messages.
A CursorLoader runs a asynchronous query in the background against a ContentProvider and then it returns the result back to the Activity or the Fragment from where it is called.
The main advantage is that it helps the user to interact with Activity or Fragment while the query is still running in the background.
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.