In my Android app, I have a local DB (SQLite) that the app needs to query for data. The query may take a long time (>5 seconds) so I am avoiding using the main UI thread for that. The returned data may also be rather large (> 1 MB).
Between AsyncTasks, Threads, ExecutorService, and IntentService, (and maybe more) what's the best class/API to use in Android to do this? Are they all more or less the same in terms of what they offer? Or is there a clear winner and a better suited class for database access?
The answer, I'm sure you can predict, is "it depends".
If you are writing a set of related calls (for example that will sync your database using an API), then I would want to use an IntentService since it's a long running operation not directly tied to your user interface.
If it's a one-time operation hitting the database to get a result set, I would lean towards the loader framework/CursorLoader to fetch data for your UI. AsyncTask is an option, although it's really a more general purpose threading mechanism. Also, consider an ORM framework that does threading work or maintains a queue of work items for you.
I can't really think of a case where managing a Thread directly would be the best option. It's very low level and lots of room for error when there are nicer abstractions over it.
Related
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 think its well known that in list of worst-documented topics, SyncAdapter shines bright like a diamond !
acording to http://udinic.wordpress.com/2013/07/24/write-your-own-android-sync-adapter/ SyncAdapter brings 4 main benefits :
A) Battery efficiency
B) Interface C) Content awareness D) Retry mechanism;
if in any case there's a need to sync an sqlite DB with remote SQL DB, and none of these benefits is needed, what other alternatives are there**?** its easy to manage a service in-between the DBs with php, I did that for Uploading part of syncing process,but for the downloading part I feel silly if I use the query filling method,cause in near future remote db might get large and larger.the only solution that comes to my mind is to write my own sync activity/service, but I dont know how to access the last update date to SQLite db/table (other than specifying a _date in every table,) to check if it is necessary to sync again ? I feel my head is between two places!
You are mixing the problem.
1- Do you really have to use sync Adapter ??? So if yes, you are gonna have a Sync call per table and no needs to save the last call date. Android will do it for you. Just setup your sync timers properly
2- other solution is to do a simple AsyncTask and do your job here. (For exemple if you have to do it only once per week)
For your date problem, the thing is if you really wants to know if you are up to date you got many solutions. On your server save the date, or increment a version and compare these when you call a sync from your device to know if you have to sync or not.
An other solution is to simply just refresh your db wherever it is updated or not(for exemple you got a small db, so no need to create an optimized system).
I faced the same problem months ago and hoped this helped you.
You might want to consider this article:
https://www.bignerdranch.com/blog/choosing-the-right-background-scheduler-in-android/
It makes it clear how syncadapter is a good choice as a result of lesser convenient options when needing to utilize the battery well and go out to the network.
I don't recommend Asyntask for theses reasons:
http://blog.danlew.net/2014/06/21/the-hidden-pitfalls-of-asynctask/
If syncadapter is really not working for you there is
android's best practices which suggests to use an IntentService and WakefulBroadcastReceiver with partial wake lock when doing long-running operations. It says "the Android framework offers several classes that help you off-load operations onto a separate thread that runs in the background. The most useful of these is IntentService."
https://developer.android.com/training/run-background-service/index.html
https://developer.android.com/training/scheduling/wakelock.html
there must be some truth to it since they wrote it.
Android Jetpack includes WorkManager which is a valid alternative to syncadapters.
Main features:
Schedule a job according to network availablity or device charging status
Backward compatiblity up to api 14
Ensures task execution, even if the app or device restarts
Intended for deferrable tasks (E.g periodically syncing application data with a server)
In alternative, something similar is Android-Job library by Evernote
For what I understand, the Loader framework is geared towards accessing data stored locally in a ContentProvider / SQLite database. We have the CursorLoader class that handles this use case quite well.
But I wonder if it's practical to use the Loader framework to write classes extending Loader / AsyncTaskLoader to access remote web services (e.g. a REST web service)? I always thought that this framework is a bit too rigid and confusing (lack of proper documentation) for this use case. I prefer handling REST calls in more regular way, using AsyncTasks / Services. But recently I've found some articles that used AsyncTaskLoaders and began to wonder.
So why would anyone use Loaders to access Web Services? The only advantage I see here is that Loaders retain their results automatically. There's no Cursor here to manage afterwards.
Realistically, you probably want to use a networking library like Volley. This has some nice features like request batching and image caching. Nonetheless, for the sake of argument lets compare Service, Loaders and AsyncTask.
Services are the way to go if you want to allow the loading to continue while changing Activities or backgrounding your application. Or, if you want to export your service so multiple applications can use it. Otherwise, use a Loader or AsyncTaskLoader.
Loaders have a few advantages over AsyncTasks.
They are less likely to cause crashes by executing code after the Activity has finished, since they are aware of the android lifecycle.
The design discourages having references to Views or Activities. This reduces the likelihood of forcing the Activity to stay in memory after it has already finished.
Monitor the data source for changes and trigger callbacks when they occur
They have built in caching that can be useful after rotations. For Cursors, the CursorLoader automatically reconnects at the correct position to the last Cursor loaded
However, they also have disadvantages
The API is extremely more cumbersome than AsyncTask. Especially if you care about compatibility with older versions of Android
You are already storing UI state inside onSaveInstanceState(), so using the Loader's causes you to save state in multiple ways. This can be confusing to read and understand. Especially if you end up mixing retained fragments into the mix.
The Loader caches the loaded result, not the UI state that you actually need
I'm assuming you are just reading from web services, not writing. If you are performing updates to a web service and you need to see the service's response, then this changes things. Using an AsyncTask could prevent you from getting the response if the it is received during a rotation.
There are cases where Loader is suitable for webservices: When your server can send push notifications back to client to notify that data is changed.
Background
I've heard that there are some new solutions for loading data in the background which are more recommended than AsyncTask (like loaders).
The problem
AsyncTasks are great and easy to use. However, it has some limitations:
The class itself has to be modified since it's limited by the number of pending tasks (about 256 or so). Of course, in a listView's adapter, I always cancel a task if it's not needed(for example when I need to update a view that was used for a different item).
I also have to cancel them all (or handle in a different way) when the activity/fragment is being re-created.
Because of 1&2, I need to manage them and have a reference to all of them
AsyncTask uses a queue of tasks, and sometimes I need to use a stack instead, so I had to create my own class of AsyncTask that uses a stack instead.
The question
Are there alternatives for AsyncTask?
I know this was asked in some posts before (like here), but I was thinking if there is a new general way to load data in the background which replaces the asyncTask.
About Loaders, I think the idea is that they are used for databases and contentProviders, but can they also be used for loading (for example) data from the Internet (like images files) ?
There is also a nice sample made by google (here, called "bitmapFun"), which according to what I see uses AsyncTask (and even extend it, maybe because of the same reasons I've mentionsed) . But maybe I'm missing there something too?
Maybe you should consider reviewing your approach, the need you have for performing several updates depending on the view and cancel all the pending tasks from the previous views gives the impression that you are performing the load of data individually for every view that needs to be created.
In a list view with a list adapter, the usual approach is to load a portion of the data (either as list of ValueObject or as Cursor from multiple database rows) paginated on demand or in one goal, not item by item. So if you wish to update the next page, you basically perform one single operation, either using AsyncTask or Loaders to fetch the new items to the model then making it available for the UI to display them. This way, you will be applying MVC, and you won't have several pending tasks to cancel and control, and your structure would be more solid and easier to manage.
About the alternatives, If you're dealing with database, the most straightforward way is to use the CursorLoader, i.e. the loaders instead of AsyncTask, but if you're dealing with data that comes from the network or filesystem, you're kinda free to choose from the variety of other options available. AsyncTask is much more simpler to use, mostly recommended for simple things or one shot queries. But you can also use Loaders for such tasks as well, see AsyncTaskLoader.
Yes.
Loaders are managed AsyncTasks. If you are not using a Loader, you are probably missing the management that they require.
AsyncTasks (and Loaders) are a pretty bad way to get stuff that is off the device. To get data from a remote server look into using an IntentService. See: https://www.youtube.com/watch?v=xHXn3Kg2IQE
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask. See the http://developer.android.com/reference/android/os/AsyncTask.html for more info.
An alternative to asynctask is robospice.https://github.com/octo-online/robospice.
You can get started with robopice here. https://github.com/octo-online/robospice/wiki/Starter-Guide.
A sample of robospice at https://play.google.com/store/apps/details?id=com.octo.android.robospice.motivations&feature=search_result.
Some of the features of robospice.
1.executes asynchronously (in a background AndroidService) network requests (ex: REST requests using Spring Android).
2.is strongly typed ! You make your requests using POJOs and you get POJOs as request results.
3.enforce no constraints neither on POJOs used for requests nor on Activity classes you use in your projects.
4.caches results (in Json with both Jackson and Gson, or Xml, or flat text files, or binary files, even using ORM Lite).
5.notifies your activities (or any other context) of the result of the network request if and only if they are still alive
6.no memory leak at all, like Android Loaders, unlike Android AsyncTasks notifies your activities on their UI Thread.
7.uses a simple but robust exception handling model.
I have an application that is doing a LOT of sqllite transactions, I currently have a bit of a hang because I am doing the sqllite actions on the UI thread... yes bad...
so I made each item have a thread and execute on it assuming sqllite api was smart enough to FIFO them.. nope ... now I get database is locked exceptions
this says it should work
without completely rewriting my code, and having a list of transactions queue up and execute them all on the same thread (many different classes, would be kind of a pain)
is there a way for me to check, and not execute a thread unless there isnt a lock? a lock check per se, or something similar that would get this to work, is efficient and isn't a huge rewrite?
Thanks
My answer that you quoted seems to be confusing. You don't have to do anything special when you are accessing the same Android database using the same database object with multiple threads. Under the covers, Sqlite has it's own locking to guarantee that the database will not be corrupted. To quote my answer;
Sqlite under Android is single threaded. Even if multiple threads were using the same database connection, my understanding is that they would be blocked from running concurrently. There is no way to get around this limitation
It has it's own locking which serializes the requests. This means that adding multiple threads will not increase the performance of the database unfortunately.
As my other answer mentions, you cannot use multiple database objects to the same database from multiple threads since there is no locking and you will corrupt your database.