1 00:00:04,380 --> 00:00:06,220 G'day, welcome back. 2 00:00:06,220 --> 00:00:12,920 Okay, the title of this video's probably given away what the problem is, with the way we're doing things. 3 00:00:12,920 --> 00:00:15,420 With the old CursorLoader class, 4 00:00:15,420 --> 00:00:20,320 the Loader made sure that the database accesses were performed on a background thread. 5 00:00:20,320 --> 00:00:25,120 We're moving away from loaders, and now we have to take care of that ourselves. 6 00:00:25,120 --> 00:00:29,940 In Kotlin, it's incredibly easy, using an extension function. 7 00:00:29,940 --> 00:00:35,180 Let's start by seeing how we can tell whether something's running on the main thread, or not. 8 00:00:35,180 --> 00:00:53,720 Run the app again, then open the logcat. 9 00:00:53,720 --> 00:00:57,660 You may find that you're still getting all sorts of events being logged, 10 00:00:57,660 --> 00:01:02,720 even when you've got the 'show only selected application' filter set. 11 00:01:02,720 --> 00:01:10,399 If that happens, use the second drop-down from the left, to choose your application again. 12 00:01:10,399 --> 00:01:14,210 That seems to put it right, and prevent all that extra stuff from appearing. 13 00:01:14,210 --> 00:01:18,380 Each entry in the logcat starts with the date and the time. 14 00:01:18,380 --> 00:01:24,000 After that, are the process ID and the thread ID, separated by a dash. 15 00:01:24,000 --> 00:01:31,880 The main UI thread has the same ID as the app's process. Any new threads that are created will have a new ID, 16 00:01:31,880 --> 00:01:35,440 which will be a number greater than the process ID. 17 00:01:35,440 --> 00:01:39,260 A quick way to see everything is executing on a background thread, 18 00:01:39,260 --> 00:01:42,900 is to search for the process and thread ID of the app. 19 00:01:42,900 --> 00:01:50,840 For example, my process and thread IDs for the main thread are both 7972. 20 00:01:50,840 --> 00:01:57,900 So I'll select 7972-7972 in the logcat, 21 00:01:57,900 --> 00:02:11,580 and use ctrl F (command F on a Mac) to highlight all the matches. 22 00:02:11,580 --> 00:02:16,540 That highlights all the entries that are created by code running on the main thread. 23 00:02:16,540 --> 00:02:19,440 Everything else is running on a background thread, 24 00:02:19,440 --> 00:02:23,900 and the highlighting makes it easy to pick those entries out of the list. 25 00:02:23,900 --> 00:02:28,900 The app hasn't done much, but there may still be some background threads started. 26 00:02:28,900 --> 00:02:33,340 When the OpenGL renderer logs its initialization, for example, 27 00:02:33,340 --> 00:02:35,560 that should be on another thread. 28 00:02:35,560 --> 00:02:41,820 You may also see zygote logging code cache collection, when garbage collection is performed. 29 00:02:41,820 --> 00:02:47,400 We're interested in database access - the entries logged by AppProvider. 30 00:02:47,400 --> 00:02:52,200 SingletonHolder and AppDatabase are all on the main thread. 31 00:02:52,200 --> 00:02:55,000 That's not good, so let's fix it. 32 00:02:55,000 --> 00:02:58,040 I'm going to show you two easy ways to do that. 33 00:02:58,040 --> 00:03:03,960 The first will involve creating a new thread. The other way is to use a coroutine. 34 00:03:03,960 --> 00:03:09,320 These are much cheaper to use, there's far fewer overheads when using a Coroutine, 35 00:03:09,320 --> 00:03:11,320 rather than creating a new thread. 36 00:03:11,320 --> 00:03:20,420 Okay, back to our TaskTimerViewModel code. 37 00:03:20,420 --> 00:03:27,020 To access the database on a background thread, all we have to do is use the thread extension function, 38 00:03:27,020 --> 00:03:29,660 and pass our code to it as a lambda block. 39 00:03:29,660 --> 00:03:36,420 I'll start with the deleteTask function, because that's very short and simple. 40 00:03:36,420 --> 00:03:44,620 We simply log what's happening, and surround the existing code with a thread. 41 00:03:44,620 --> 00:03:45,920 That's it. 42 00:03:45,920 --> 00:03:50,280 The extension function uses default values for all the other settings 43 00:03:50,280 --> 00:03:54,180 the thread class needs, and starts the thread for us. 44 00:03:54,180 --> 00:04:04,100 Let's see that working before we change the rest of the code. Run the app 45 00:04:04,100 --> 00:04:08,740 and search for the process and thread ID of the main thread again. 46 00:04:08,740 --> 00:04:16,420 It'll be a different number this time. 47 00:04:16,420 --> 00:04:26,480 On the emulator, delete task 3. 48 00:04:26,480 --> 00:04:33,520 Now the entries for AppProvider and SingletonHolder have a different thread ID, when we delete the task. 49 00:04:33,520 --> 00:04:39,100 Scroll through the logcat, and the AppProvider entries that appear when the database is re-queried, 50 00:04:39,100 --> 00:04:42,920 are still running on the main thread. That's easy to fix. 51 00:04:42,920 --> 00:04:52,260 We just need to use a thread for the database access code in the load tasks function. 52 00:04:52,260 --> 00:04:59,800 All we need to do is to wrap the existing code into a thread. 53 00:04:59,800 --> 00:05:04,920 Before running the app again, open the terminal pane, 54 00:05:04,920 --> 00:05:16,640 and enter the command adb shell setprop log.tag.SQLiteQueryBuilder DEBUG 55 00:05:16,640 --> 00:05:21,960 That turns on debug logging for the query builder, as we did a few videos ago. 56 00:05:21,960 --> 00:05:28,740 Now we'll also see the query being created, and can confirm that that's happening on a separate thread too. 57 00:05:28,740 --> 00:05:32,900 If you're doing this on a physical device, rather than an emulator, 58 00:05:32,900 --> 00:05:36,220 remember to reboot your device when you've finished. 59 00:05:36,220 --> 00:05:42,560 Otherwise the debug logging will stay in effect, and the performance of your phone may suffer. 60 00:05:42,560 --> 00:05:46,560 When you don't need to work with the results of a code block straight away, 61 00:05:46,560 --> 00:05:50,360 this is a very easy way to run it on a separate thread. 62 00:05:50,360 --> 00:05:55,620 In these two cases, we're not interested in waiting for the database accesses to complete. 63 00:05:55,620 --> 00:06:02,040 When deleting a task, the ViewModel will get a notification by its ContentObserver, 64 00:06:02,040 --> 00:06:04,400 and it will re-query the database. 65 00:06:04,400 --> 00:06:09,380 There's no need for the ViewModel to wait for the delete operation to complete. 66 00:06:09,380 --> 00:06:16,040 All the code that responds to a delete will be executed, when the delete is eventually performed. 67 00:06:16,040 --> 00:06:18,900 The same is true for querying the database. 68 00:06:18,900 --> 00:06:23,700 When the query finishes, and we get a new cursor back in loadTasks, 69 00:06:23,700 --> 00:06:28,240 the database cursor live data object gets that new cursor. 70 00:06:28,240 --> 00:06:33,140 That causes MainActivityFragment to swap the adapter's cursor, 71 00:06:33,140 --> 00:06:38,260 because MainActivityFragment is observing the live data object. 72 00:06:38,260 --> 00:06:43,880 Run the app again, and search for the new process ID main thread ID. 73 00:06:43,880 --> 00:07:05,260 Remember that they change every time you restart the app. 74 00:07:05,260 --> 00:07:13,140 Scrolling through the logcat, you can see that the AppProvider query function is being called on a separate thread. 75 00:07:13,140 --> 00:07:20,660 You can see the entry "forming query, select ID, name, description, sort order from tasks, 76 00:07:20,660 --> 00:07:25,340 order by sort order and name" running on that different thread. 77 00:07:25,340 --> 00:07:28,860 All the database access is also happening on that thread. 78 00:07:28,860 --> 00:07:32,960 That's seriously cool. It really is that easy. 79 00:07:32,960 --> 00:07:35,000 I'll stop this video here. 80 00:07:35,000 --> 00:07:40,420 In the next video, we'll see how to do the same thing using Coroutines. 81 00:07:40,420 --> 00:07:42,120 See you in the next one.