I've read the warnings about keeping long-lived Realm instances on background non-Looper threads. I've also seen the suggestion that opening and closing Realm instances quickly is not the best idea. Given those constraints (if either is invalid, let me know), I'm trying to identify the best way to use Realm with websockets, where websocket events contain information that requires access to the Realm.
Particularly, what is the right way to go between these options (or something else entirely?):
Open and close the Realm on every event
Open the Realm at the start of the thread, periodically (every 30s or so) begin and commit transaction on the thread to bring the Realm up to date.
Allocate a Looper for the thread that handles websocket messages. Create the Realm instance once on the thread, and leave it open for the thread's lifetime, using the Looper to keep it up to date.
Other things worth noting:
The client will already have a Realm open on the UI thread. So, as far as I understand, this means the background threads at least do not need to pay the price of schema validation.
There's no way to predict the frequency of websocket events. In the typical case, there may be no more than one or two events per second. However, in the case that a large operation happens on the server which changes a bunch of objects, the client may receive hundreds or thousands of websocket events fairly rapidly.
Open and close the Realm on every event
Personally, I find that to be reliable according to the people who say "their Realm is out of date", so this only makes sense if you actually call refresh() after Realm.getDefaultInstance().
Open the Realm at the start of the thread, periodically (every 30s or so) begin and commit transaction on the thread to bring the Realm up to date.
Periodic updates would cause either needless transactions, or be "unaware of change" every now and then.
Also, you actually don't need to commit the transaction to force an update, because beginTransaction() already brings the Realm to the new version.
realm.beginTransaction();
realm.cancelTransaction();
This would be sufficient on its own for that - but technically, this is a workaround to replace using refresh() (not part of public API), with also blocking the thread if a transaction is open on another thread.
Allocate a Looper for the thread that handles websocket messages. Create the Realm instance once on the thread, and leave it open for the thread's lifetime, using the Looper to keep it up to date.
If you want to ensure at all times that the current thread is up to date reliably and also without blocking other threads in the process, then this is the way to go.
So the two ways are either:
1.) keep a newSingleThreadedExecutor() that does queries to the Realm only inside a transaction
2.) keep a HandlerThread alive for as long as needed, and execute the web socket + query stuff there
(Personally, I've only ever used HandlerThread with RxJava + AndroidSchedulers.from(handlerThread.getLooper())).
Related
I am building a fragment in Android which displays some data kept on a server. As long as the user is on this fragment, I would like to poll the server every x seconds. Additionally, I would like to stop this procedure once the user navigates away from this fragment. Is the optimum solution to this problem to use coroutines or a thread? Thank you for any assistance!
Polling is never a good option as you will keep on wasting resources while the server might have no new data to supply. However, if you are still keen on doing that, you can use any of the choices, i.e. coroutines and thread. In thread, you'd have to manage its lifecycle yourself whereas in coroutine you don't have to worry about anything as CoroutineScope takes care of all that for you. One incentive of going with Coroutines would be the supply of operators you can use on your flows, map and switchMap for instance.
A better solution would be to make use of SNS or firebaseRemoteMessagingService that can notify your application client, upon which you can request the server.
Consider a simple activity containing two fragments:
RecordListFragment: A RecyclerView bound to LiveData<Record> via a #Query and a button which calls a #Delete query for a Record
RecordEditFragment: Several widgets to change the record and a save button which calls a #Insert or #Update query for a Record
The documentation for RoomDatabase.Builder#allowMainThreadQueries states:
Room ensures that Database is never accessed on the main thread because it may lock the main thread and trigger an ANR. If you need to access the database from the main thread, you should always use async alternatives or manually move the call to a background thread.
Every tutorial/blog/example I found states the same, you should not block the UI thread with database work. My instinct disagrees: There's nothing more I want when inserting or deleting data from a database than having an easy way to block the user from interfering.
If all database access happens on a worker thread one possible order of events that comes to mind is:
The user deletes Record a on the RecordListFragment
The user manages to enter the RecordEditFragment for Record a
The background thread executes the deletion
The user tries to save/update the non-existent Record a
Boom
Another scenario is:
The user updates an existing Record a from the RecordEditFragment, transitioning to RecordListFragment
Before the list has a chance to update the user reenters the RecordEditFragment for Record a
The RecordEditFragment opens with old data
The background thread executes the save
Boom
These are classic race conditions when working asychronously and I cannot imagine that this is actually best practice on Android but I cannot find any documentation that states otherwise. Are these scenarios actually possible or does Android prevent stuff like this from happening? Why not just block the UI for a simple task like inserting a record to not have any possibility of a race condition?
You can block the user from interfering with tasks without "blocking the UI".
You said:
There's nothing more I want when inserting or deleting data from a database than having an easy way to block the user from interfering
You can definitely change the UI so that it won't let the user do something while you have background processes or whatever you want running. That is not what "blocking the UI" is. Blocking the UI means blocking the UI thread from being able to operate.
Modern Android development, including built in functionality with Kotlin Coroutines, in some ways prevents you from blocking the UI thread by not even compiling until you fix your code. But it's possible that you could be supporting older versions of Android or else not using those language features, so it is still up to you to not write code that will run slowly on the UI thread. This makes it so that your UI seems to be responsive even if the things going on in the other threads might be taking a while.
So, if you need to write to RoomDB or run a network request, that should go on another thread like the IO thread.
See https://developer.android.com/guide/components/processes-and-threads
for more information regarding Android processes and threads (specifically read the sectino about Threads), or for more specific information about how Kotlin Coroutines help check out https://developer.android.com/kotlin/coroutines-adv
Especially, note the following quotes from the Android developer link above:
When the thread is blocked, no events can be dispatched, including drawing events ...
... the Android UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:
Do not block the UI thread
Do not access the Android UI toolkit from outside the UI thread
So go ahead and fire off that background work on another thread, and change your UI to show that it's doing something until that other thread completes. Then you have prevented the user from interfering and you've also made the app work in a way that doesn't appear broken.
The documentation for Android's SQLite interfaces mention that database accesses should be performed from an IntentService as they are potentially long-running operations, so the GUI thread should not block on them.
The IntentService is shut down as soon as no further Intents are queued for it, which would happen basically after every request, so the database handles are built up and destroyed for each query as well, which seems wasteful.
Is there a way to keep an IntentService around longer, or somehow otherwise avoid a race between the GUI thread posting more Intents and the service answering them?
Should I just make my query Intents contain a list of queries that should all be performed, or would that cause other problems with message sizes?
The documentation for Android's SQLite interfaces mention that database accesses should be performed from an IntentService as they are potentially long-running operations, so the GUI thread should not block on them.
I/O of all forms should be performed on background threads, so as not to block the main application thread. IntentService itself is not a great choice, given changes on Android 8.0+.
A more typical approach nowadays is to have database access be managed by a singleton repository (whether a manually-created singleton or a singleton supplied to you via a dependency injection framework). The repository can use any number of approaches to provide a reactive API while doing the I/O on a background thread, including:
RxJava
LiveData and ordinary threads, executors, etc.
Kotlin coroutines
If you use Room as your database access layer, it gives you all three of those options "for free". Some other ORMs offer similar capabilities.
Is there a way to keep an IntentService around longer, or somehow otherwise avoid a race between the GUI thread posting more Intents and the service answering them?
Background services can only run for one minute. If your concern is the overhead in opening the database, use a singleton repository, and only open it once per process invocation. It's also entirely possible that you do not need a service; if you have a foreground UI, a service may be pointless.
Should I just make my query Intents contain a list of queries that should all be performed...?
Um, possibly, but again, using a service here may not be necessary and definitely makes the problem more complex.
So: use a background thread for I/O. That does not have to involve a service.
I've an android app where I'm retrieving data into a Fragment. And I believe that Firebase manages its asynchronous calls. But still I've doubt the if we need to write the Firebase code in the background thread or not?.
If we need to write it into the background thread then can you please tell which operations takes more time. eg:
mDatabase = FirebaseDatabase.getInstance().getReference().child("Blog");
I think that performing this on the main UI thread may become risk full because setting connection between database may sometime take large time.
The Firebase Database client performs all network and disk operations off the main thread.
The Firebase Database client invokes all callbacks to your code on the main thread.
So network and disk access for the database are no reason to spin up your own threads or use background tasks. But if you do disk, network I/O or CPU intensive operations in the callback, you might need to perform those off the main thread yourself.
If you're pulling down a large-ish collection of data from the database, and you want to convert that all into a JavaBean type collection, you may want to offload that onto another thread as the size of data its use of reflection may cause too much work for the main thread. The only way to know about this for sure is to benchmark it yourself. Generally speaking, you get 16ms to do things on the main thread before you start dropping from the optimal rendering speed of 60 frames per second.
I recently tweeted a diff on a project of mine where I refactored a pattern for sending database listener to an Executor for background processing. However, your app may not call for this kind of complexity. It was good for my app, however. https://twitter.com/CodingDoug/status/773277680867258368
Firebase runs all of its callbacks asynchronously as documented https://www.firebase.com/docs/android/guide/retrieving-data.html . This is done through a web socket layer.
If for example, you need to do a large amount of data processing on the result of the Firebase data update - you should probably spin up an AsyncTask to prevent the UI from blocking. This isn't any different from how you would normally approach data processing before being presented to the UI.
The Firebase documentation covers how data is handled and the reason why you do not need to execute any background reads. You should probably spend some time reading the documentation.
I am in the middle of developing an android application and I have stumbled across something that I don't really know the best way to solve.
What I wwant to achieve is, when a user logs into the application, I want to start a thread if the device is connected to a network(what kind of network doesn't matter)
The thread should perform an action every 10 minutes.
What this thread needs to do is, loop trough a list, a queue to be more exact.
This queue will have objects, and based on the objects in the queue when there is a connection available, execute.
The queue will be filled trough the flow of the application.
For example filling in a questionary.
The answers need to be synched to the server. Every question can include pictures takebn from the camera etc, so I want to save certain data as an object, put them in a queue, and have a thread handle the http requests. This way the UI won't be blocked. It's of great importance to sync whenever possible.
What I want to avoid is having another process run aside from my own APP. That's why I haven't used a service. Or do I missunderstand the concept of services as a whole?
Are there specific queue objects or lists?
I want to loop trough the queue list that can be filled at anytime while the program is alive, with a thread.sleep like method when the list is completely empty.
Please leave me hints and tips on what way to go with this.
A service isn't it's own process... from the Documentation: "A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of."
A service really is the best choice for what you're talking about. You spawn your own thread in the service that then does the following: check your queue for objects and send any to the server (since you're already not on the UI thread, you can do this without spawning yet another thread if you want). If the queue is empty, use a Timer to schedule another invocation of your upload method.