1 00:00:04,780 --> 00:00:07,060 G'day everyone, welcome back. 2 00:00:07,060 --> 00:00:12,360 At the end of the last video, we were able to add new tasks the database, 3 00:00:12,360 --> 00:00:16,059 but the RecyclerView wasn't updating to display them. 4 00:00:16,059 --> 00:00:20,140 We had to close the app and run it again to see the new tasks. 5 00:00:20,140 --> 00:00:23,700 That's not exactly the ideal user experience. 6 00:00:23,700 --> 00:00:30,660 Fortunately, all the support to make the RecyclerView update is built into the Android framework. 7 00:00:30,660 --> 00:00:35,900 So the changes we need to make to our ContentProvider are really simple. 8 00:00:35,900 --> 00:00:43,800 Where it gets a bit more complex is getting the ViewModel to react to the notifications that the ContentProvider will issue. 9 00:00:43,800 --> 00:00:47,640 With the CursorLoader all this was handled for us. 10 00:00:47,640 --> 00:00:50,240 Now we have to do it ourselves. 11 00:00:50,240 --> 00:00:57,100 One thing you may be asking is, why does he keep talking about loaders when they've been deprecated. 12 00:00:57,100 --> 00:00:58,760 The answer is simple. 13 00:00:58,760 --> 00:01:05,860 If loaders are deprecated, they're going to be removed from Android at some point over the next couple of years. 14 00:01:05,860 --> 00:01:07,840 But before that happens, 15 00:01:07,840 --> 00:01:10,540 companies that have apps that used loaders 16 00:01:10,540 --> 00:01:16,260 are going to be looking for programmers that can make them work without the loaders. 17 00:01:16,260 --> 00:01:21,180 After completing this section, you'll be one of those programmers. 18 00:01:21,180 --> 00:01:28,460 Okay there are two parts to this. The first step is something that I've hinted at a few times. 19 00:01:28,460 --> 00:01:35,580 We need to modify our ContentProvider, so that it notifies any observers about changes to the data. 20 00:01:35,580 --> 00:01:41,380 Our ContentProvider has three functions that can result in changes to the data; 21 00:01:41,380 --> 00:01:45,100 the insert, update and delete functions. 22 00:01:45,100 --> 00:01:52,520 The code we need to add is very similar for all three, so let's start with the insert function in the AppProvider. 23 00:01:52,520 --> 00:02:01,780 Open our AppProvider, scroll down a bit, and the change is simple. 24 00:02:01,780 --> 00:02:09,160 If something was inserted then the record ID will be non-zero. 25 00:02:09,160 --> 00:02:12,860 Let's log that we're starting, 26 00:02:12,860 --> 00:02:17,240 and then we'll call the notifyChange function 27 00:02:17,240 --> 00:02:20,560 with the URI of the value that has changed. 28 00:02:20,560 --> 00:02:25,340 Notice that we're passing the original URI that was provided to the insert function. 29 00:02:25,340 --> 00:02:30,220 The URI that the function returns will have the new ID appended to it 30 00:02:30,220 --> 00:02:33,640 and that refers to a specific row in the table. 31 00:02:33,640 --> 00:02:38,680 It is the URI for the entire table that we want to notify changes on. 32 00:02:38,680 --> 00:02:44,440 I used the safe call operator when getting the ContentResolver from the context. 33 00:02:44,440 --> 00:02:50,500 We know the context won't be null, but using the safe call operator removes warnings. 34 00:02:50,500 --> 00:02:53,880 We do something very similar for the update function. 35 00:02:53,880 --> 00:03:05,100 Scroll down a little way further, and once again check that the count is greater than zero. 36 00:03:05,100 --> 00:03:07,800 That makes sure something happened. 37 00:03:07,800 --> 00:03:15,900 Log that we've started, and then call the ContextResolver to notify the change, 38 00:03:15,900 --> 00:03:19,120 again, passing the URI. 39 00:03:19,120 --> 00:03:22,100 The final change is to the delete function. 40 00:03:22,100 --> 00:03:40,640 Once again, it's pretty much the same. 41 00:03:40,640 --> 00:03:43,020 That's the first part done. 42 00:03:43,020 --> 00:03:48,320 When we call a function of the ContentProvider that results in a change to the database, 43 00:03:48,320 --> 00:03:53,400 the ContentResolver will notify any observers about the change. 44 00:03:53,400 --> 00:03:56,620 The second part is to observe these notifications. 45 00:03:56,620 --> 00:04:00,740 We do that in the TaskTimerViewModel class. 46 00:04:00,740 --> 00:04:08,400 We need to create a ContentObserver and register it. Let's start by creating a ContentObserver. 47 00:04:08,400 --> 00:04:30,540 If you're prompted for the correct import in the handler, we want android.os.handler. 48 00:04:30,540 --> 00:04:35,160 When a change is notified, the onChange function will be called. 49 00:04:35,160 --> 00:04:42,620 We log the fact, then we call loadTasks to reload the data, and update the current cursor LiveData. 50 00:04:42,620 --> 00:05:01,240 The next step is to register the observer, and the init code is a good place to do that. 51 00:05:01,240 --> 00:05:05,180 We tell it the URI that we want to observe changes on 52 00:05:05,180 --> 00:05:08,660 and provide our ContentObserver. 53 00:05:08,660 --> 00:05:14,780 In an app with many tables that get updated, we can create an observer for each URI, 54 00:05:14,780 --> 00:05:20,280 or we could register the same observer for several URIs. 55 00:05:20,280 --> 00:05:26,400 Notice that the URI is passed into the onChange function on line 22, 56 00:05:26,400 --> 00:05:30,040 so we could respond differently to different URIs. 57 00:05:30,040 --> 00:05:35,420 When you register an observer like this, it's important to unregister it again. 58 00:05:35,420 --> 00:05:39,120 If you don't, you can end up with memory leaks. 59 00:05:39,120 --> 00:05:44,540 Remember from our discussion on ViewModels, that they have an onCleared function. 60 00:05:44,540 --> 00:05:50,400 That function gets called when the ViewModel's no longer used and can be destroyed. 61 00:05:50,400 --> 00:05:56,520 It's a good place to clear up any resources, and that's where we'll unregister our observer. 62 00:05:56,520 --> 00:06:06,060 Android studio will generate the stub for us. 63 00:06:06,060 --> 00:06:11,760 ctrl o, select onCleared 64 00:06:11,760 --> 00:06:19,900 remove what's already there, and add our code. 65 00:06:19,900 --> 00:06:23,640 Unregistering the observer is similar to registering it. 66 00:06:23,640 --> 00:06:28,840 We call the ContentResolver's unregisterContentObserver function. 67 00:06:28,840 --> 00:06:37,980 Let's see that running 68 00:06:37,980 --> 00:06:50,820 We should be up to task 7 69 00:06:50,820 --> 00:06:53,620 but we'll set the sort order to 2. 70 00:06:53,620 --> 00:06:57,740 That way it should appear on the list before task 6. 71 00:06:57,740 --> 00:07:06,140 I'll click Save, and when I return to the main screen there's our new task 7 showing in the list. 72 00:07:06,140 --> 00:07:08,100 Let's have a look in the logcat, 73 00:07:08,100 --> 00:07:12,820 and also see how to fix a quirk that it's got at the moment. 74 00:07:12,820 --> 00:07:17,960 If you find your log cat running wild and logging all sorts of things, 75 00:07:17,960 --> 00:07:23,060 even when you've got the show only selected application filter on the right, 76 00:07:23,060 --> 00:07:24,540 don't panic. 77 00:07:24,540 --> 00:07:32,340 Use the second drop down from the left and select your application again. 78 00:07:32,340 --> 00:07:38,860 That causes it to respect the filter. It seems to be a quirk in Android Studio at the moment. 79 00:07:38,860 --> 00:07:51,240 Ok, type the filter, query|insert, with the regex box checked. 80 00:07:51,240 --> 00:07:54,300 That will make it easier to find what we want. 81 00:07:54,300 --> 00:07:59,440 There's our insert happening, and just below that we can see the query being called. 82 00:07:59,440 --> 00:08:04,560 So our ViewModel has re-queried the database to refresh its cursor. 83 00:08:04,560 --> 00:08:12,880 It then updates the database cursor MutableLiveData object, which is being observed by the MainActivityFragment. 84 00:08:12,880 --> 00:08:16,980 When the MainActivityFragment observes that the cursor has changed, 85 00:08:16,980 --> 00:08:22,820 it swaps the adapter's cursor and the RecyclerView can immediately access the new data. 86 00:08:22,820 --> 00:08:26,800 There are two important changes we had to make, to make all that happen. 87 00:08:26,800 --> 00:08:34,240 Firstly, we made sure that all our functions that can change the database; insert, delete and update, 88 00:08:34,240 --> 00:08:35,780 in the AppProvider, 89 00:08:35,780 --> 00:08:40,059 call the ContentResolver's notifyChange function. 90 00:08:40,059 --> 00:08:44,620 And so they notify the listeners that there's been a change. 91 00:08:44,620 --> 00:08:48,980 Then, we set up a ContentObserver in the ViewModel class, 92 00:08:48,980 --> 00:08:52,080 and registered it to be notified of the changes. 93 00:08:52,080 --> 00:08:54,940 Okay, we'll stop the video there. 94 00:08:54,940 --> 00:08:59,460 In the next video, we'll work on the onClick listeners for the buttons, 95 00:08:59,460 --> 00:09:03,000 so that we can then edit and delete tasks. 96 00:09:03,000 --> 00:09:08,180 By the time we've done that, you should be able to tell me what we're doing wrong. 97 00:09:08,180 --> 00:09:12,760 Our code works, but we're doing something that we really shouldn't be doing. 98 00:09:12,760 --> 00:09:15,980 It's something we've seen mentioned in the documentation, 99 00:09:15,980 --> 00:09:18,200 and we've hinted at it a couple of times. 100 00:09:18,200 --> 00:09:24,520 We'll be putting it right in a future video, but until then, see if you can work out what it is. 101 00:09:24,520 --> 00:09:27,500 See you in the next video