Android Database onUpgrade blocking UI Thread - android

My app has a pretty big database, especially one of the tables - it has like 2 million entities. In the next app release we are doing a huge update - dropping one of the columns and replacing it with another one. For the biggest table this takes ~ 13 sec.
The problem is even though i have put this update in a background thread(still it is in the onUpgrade method of the SQLLiteDatabase), it still blocks the UI thread and causes an android os popup to appear - prompting to either kill the app or wait.
Is the onUpgrade method actually blocking the UI thread, or am I doing something wrong?

Use StrictMode API to detect the exact cause of the UI thread blocking.
I would recommend you to a service and initiate this db upgrade if it's going to be huge one, also provide a loading screen if you don't want see the UI immediately.
Hope this helps.

There was actually another database process that was blocking the UI thread. I managed to identify the problem using the StrictMode API. Thanks #albeee

Related

Can asynchronous LiveData/Room queries lead to race conditions?

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.

Should Content resolver insert happen in a background thread?

I am unsure if there is a need to do processing in a background thread for content resolver insert functionality. I did it as a part of UI thread and the application runs file. But need to know if it is right calling inserts directly on ui thread.
I'd recommend against it. Database writes are slow, and should be performed off the UI thread where possible. It may not be a problem for you now, but you may find that as your database grows or the nature of your application changes and you start writing more data, your UI thread will start to lag behind.
You may also find that this kind of operation in its current form might not run so smoothly on lower-end devices.
In addition to these issues, if you plan to share your database between multiple threads, concurrent writes can and will be blocked by the database lock to assure data integrity; this in turn may leave your UI thread hanging.
So, although it may not be a problem right now, you can save yourself a lot of time in the future by implementing and working with a design pattern that does ensure your transactions occur off the UI thread.
Whatever you do, make sure you give yourself a pat on the back for making this consideration in the first place!

Why is the UI thread blocked when work is done on a separate thread?

In my app, I have a ViewPager with +/- 10 pages. When the app is first opened, all the pages are instantiated and immediately begin to load data to display. Each page (which are fragments) creates an AsyncTask to query a database and populate itself with the appropriate data. Here's the problem: even though the work is being done on separate threads, the UI stops updating during the database queries (which are done sequentially, and take 1-3 seconds total). This happens both on my Nexus 5 and a crappy old Samsung phone, so I know the problem is not that the hardware just can't keep up.
So ultimately, I'm wondering why the UI thread is blocked by work done on a background thread. My understanding of threading was that doing work on one would not block the other for an extended period of time. If my understanding is wrong, please explain how. Thanks in advance.
I don't think code is required here, but if it is, let me know and I will post the relevant portions.
It stops animating immediately after the first database query begins and starts animating again immediately after the last database query completes
It is possible, then, you are not doing the work on a background thread that you think you are. You may be doing the work on the main application thread.
Traceview can help you identify what you are doing on the various threads, and StrictMode can help you with obvious problems (disk I/O and network I/O on the main application thread).
In this case, you may be getting caught by how you are doing your work:
Each page (which are fragments) creates an AsyncTask to query a database and populate itself with the appropriate data.
If you are doing your query in doInBackground() but are not touching the resulting Cursor also in doInBackground(), the query actually wasn't done yet. The Cursor is a SQLiteCursor, and it lazy-executes the query when the data is first used. This is another one of those "really cool ideas that just plain suck in how we do things nowadays". A workaround is to call getCount() on the Cursor while you are in doInBackground(), to ensure that the query actually is executed on the background thread.

Android SQLite database transaction/locking changes in Jellybean

I have an SQLite database, which receives some heavy use in my app. It is accessed from:
Various activities, to load data into the UI via ASyncTasks (select statements only)
Several background processes (only one of which can run at a time), which do many inserts/updates
I call getWritableDatabase() once and use this everywhere. Each of the background processes uses an IMMEDIATE (i.e. non-locking) transaction, which speeds some of them up from minutes to seconds.
This all worked perfectly pre-Jellybean; the background processes had the speed benefit of the transaction, but this did not block Activites from loading data via ASyncTasks.
Post-Jellybean, each ASyncTask loading data for the GUI is blocked while a transaction is in progress, so often does not return for several seconds. This makes navigation slow and frustrating. I've isolated the blocking to the rawQuery database call.
I can't find any documentation which specifies exactly what changed in Jellybean, other than several methods being deprecated - http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#isDbLockedByOtherThreads() - with comments like "There is no longer the concept of a database lock, so this method always returns false."
Is there a simple way to mimic the pre-Jellybean behaviour?
It seems that pre-Jelly Bean async tasks were running in parallel all of the time. Now you've got an Executor under the covers running async tasks serially. Good news is, you can specify how your tasks run by branching based on OS version.
Read this article
I've found the solution to my own question. I'm still not sure exactly what Google have changed in Jellybean to stop what worked before working now - but the solution is to use enableWriteAheadLogging().
I had tried this before, to no avail - I was still getting blocked reads while a transaction was in progress. The important bit for me was (in the enableWriteAheadLogging() documentation)
If the database has any attached databases, then execution of queries in parallel is NOT possible.
Unfortunately, my application has two separate databases which are attached together to allow joined queries - enableWriteAheadLogging() returns false. I have prototyped removing the attachment, and this resolves the blocking issue; my select queries all run without delay while another thread runs an IMMEDATE transaction.
Sadly, I'll have to redesign my database schemas to implement a full fix.

Android app hangs at SQL select query

My program involves interaction with SQLite in a fairly regular basis, and in the beginning of the app, I call a query
mDatabase.rawQuery("SELECT key,
indice as _id
FROM Dictionary",null);
Strangely the app stalls on executing this line. This does not happen if I am debugging the application, but when I run the app, the control goes from this line and never returns. I have checked this by putting logcat before and after this line.
I have not been able to comprehend this behavior. Can someone help?
P.S. The table Dictionary has over 2000-3000 records.
EDIT:
I have tried calling this from both UI & separate threads. Either ways, the execution stops at this call (for that thread). So when I call it from another thread, though there is no ANR, the call still fails and holds the thread indefinitely.
EDIT2:
This issue does not happen every time I run the application but 5 out of 10 times. And apparently happens more on weaker phones.
Take care of below points.
Make call to query in separate thread other than UI thread.
Cursor at max can hold upto 1MB of data. So query for minimum amount of data.
You should take this off the UI thread. Looks like a heavy call. Anything which takes longer than 5 seconds and stalls the UI thread will trigger an ANR.
Yes piyushnp and abhinav are right. AsyncTask or Thread are better option for getting the details. Show progressbar when doing background processing. And when you get query results display it in activity or do whatver processing you want on query results.
This example simulates your problem. AsyncTask basic Example : AsyncTask

Categories

Resources