G'day, welcome back.
Okay, the title of this video's probably given away what the problem is, with the way we're doing things.
With the old CursorLoader class,
the Loader made sure that the database accesses were performed on a background thread.
We're moving away from loaders, and now we have to take care of that ourselves.
In Kotlin, it's incredibly easy, using an extension function.
Let's start by seeing how we can tell whether something's running on the main thread, or not.
Run the app again, then open the logcat.
You may find that you're still getting all sorts of events being logged,
even when you've got the 'show only selected application' filter set.
If that happens, use the second drop-down from the left, to choose your application again.
That seems to put it right, and prevent all that extra stuff from appearing.
Each entry in the logcat starts with the date and the time.
After that, are the process ID and the thread ID, separated by a dash.
The main UI thread has the same ID as the app's process. Any new threads that are created will have a new ID,
which will be a number greater than the process ID.
A quick way to see everything is executing on a background thread,
is to search for the process and thread ID of the app.
For example, my process and thread IDs for the main thread are both 7972.
So I'll select 7972-7972 in the logcat,
and use ctrl F (command F on a Mac) to highlight all the matches.
That highlights all the entries that are created by code running on the main thread.
Everything else is running on a background thread,
and the highlighting makes it easy to pick those entries out of the list.
The app hasn't done much, but there may still be some background threads started.
When the OpenGL renderer logs its initialization, for example,
that should be on another thread.
You may also see zygote logging code cache collection, when garbage collection is performed.
We're interested in database access - the entries logged by AppProvider.
SingletonHolder and AppDatabase are all on the main thread.
That's not good, so let's fix it.
I'm going to show you two easy ways to do that.
The first will involve creating a new thread. The other way is to use a coroutine.
These are much cheaper to use, there's far fewer overheads when using a Coroutine,
rather than creating a new thread.
Okay, back to our TaskTimerViewModel code.
To access the database on a background thread, all we have to do is use the thread extension function,
and pass our code to it as a lambda block.
I'll start with the deleteTask function, because that's very short and simple.
We simply log what's happening, and surround the existing code with a thread.
That's it.
The extension function uses default values for all the other settings
the thread class needs, and starts the thread for us.
Let's see that working before we change the rest of the code. Run the app
and search for the process and thread ID of the main thread again.
It'll be a different number this time.
On the emulator, delete task 3.
Now the entries for AppProvider and SingletonHolder have a different thread ID, when we delete the task.
Scroll through the logcat, and the AppProvider entries that appear when the database is re-queried,
are still running on the main thread. That's easy to fix.
We just need to use a thread for the database access code in the load tasks function.
All we need to do is to wrap the existing code into a thread.
Before running the app again, open the terminal pane,
and enter the command adb shell setprop log.tag.SQLiteQueryBuilder DEBUG
That turns on debug logging for the query builder, as we did a few videos ago.
Now we'll also see the query being created, and can confirm that that's happening on a separate thread too.
If you're doing this on a physical device, rather than an emulator,
remember to reboot your device when you've finished.
Otherwise the debug logging will stay in effect, and the performance of your phone may suffer.
When you don't need to work with the results of a code block straight away,
this is a very easy way to run it on a separate thread.
In these two cases, we're not interested in waiting for the database accesses to complete.
When deleting a task, the ViewModel will get a notification by its ContentObserver,
and it will re-query the database.
There's no need for the ViewModel to wait for the delete operation to complete.
All the code that responds to a delete will be executed, when the delete is eventually performed.
The same is true for querying the database.
When the query finishes, and we get a new cursor back in loadTasks,
the database cursor live data object gets that new cursor.
That causes MainActivityFragment to swap the adapter's cursor,
because MainActivityFragment is observing the live data object.
Run the app again, and search for the new process ID main thread ID.
Remember that they change every time you restart the app.
Scrolling through the logcat, you can see that the AppProvider query function is being called on a separate thread.
You can see the entry "forming query, select ID, name, description, sort order from tasks,
order by sort order and name" running on that different thread.
All the database access is also happening on that thread.
That's seriously cool. It really is that easy.
I'll stop this video here.
In the next video, we'll see how to do the same thing using Coroutines.
See you in the next one.