[MUSIC PLAYING] LYLA FUJIWARA: OK, so hi My name is Lyla, and you are here at the Architecture Components Training, so hopefully you’re in the right place As I said, my name is Lyla, and I’m an Android Developer Advocate And today, you are going to make possibly your first full app with the Android Architecture Components, and you’re going to do that from scratch So let’s get started But in case you don’t know what Architecture Components are, I’m going to talk a little bit about that and just introduce that to you So architecture components are a growing set of libraries There will be more of them coming out, and they are meant for creating Android apps And the whole point of these libraries is to simplify things that might have been a little bit challenging with Android development The Android team decided to create these libraries by getting feedback from you all– the developers– and asking sort of what slowed down your development So more specifically, and from that information, they started with two libraries– a library for persistence on Android and a library for Lifecycles on Android and making Lifecycle management easier So the first one is Room, which you’ve hopefully heard of, and the second one is the Lifecycle library And both of these libraries reached 1.0 stable in November, so they are production ready and you can use them in your apps safely Now, these libraries were built in a way where they could be used alone, or they could work together just fine So if you’re only looking for a solution to make SQLite databases, you could easily use Room alone, without having to use the other libraries, if you want But they’re also designed in a way to really work well together, which is what I’m going to mostly be talking about today I should also mention that there is a third library, which is in alpha, called the Paging library You should definitely check this out and give myself or any of the other Developer Android Advocates feedback about it And its purpose is to simplify lazily loading large data sets for you So another big part of Architecture Components is the new documentation On developer.android.com, there’s actually a guide now that describes one way which you can properly architect an Android App If you are happily using an architecture pattern, please continue to do that But if this might be new to you and you’re interested in learning more about one scalable way to architect Android apps, there’s a whole guide on it, and that’s actually the architecture that we’re going to be following today So what are you going to make today? We are going to make this very simple app that displays a list of words But even though this is a very simple app, you’re going to see a lot of the different Architecture Components in practice, and it’s going to do some powerful things for you So in the next hour or so, you’re going to be building a SQLite database, you’re going to be displaying all of the content in that SQLite database in a recyclerview You’re going have the ability to insert or add new words to that database And finally, you’re going to create this in a way that uses a reactive UI, which means, in other words, the UI will be automatically kept in sync with the database, which is, one of the powerful things you can do about that connection between Room and the Lifecycle library So let’s go ahead and take a look at what the Guide to App Architecture says about app architecture and how we might architect an Android app So one of the big principles of the guide is really encouraging you to have a separation of responsibilities for each of your classes So I’m going to go through each of the classes and talk about what their responsibility So the first class up there on the screen is the UI controller, and that’s just a fancy name for an activity or a fragment Now, the responsibility of the UI controller is to display data So basically, it’s telling views what to draw to the screen Another responsibility that it has is capturing things like user interactions So obviously, your activity is the one that knows if a user clicks a button So that is, of course, a responsibility that it has But as soon as it gets that button click, instead of processing itself, it will pass on that information to a new class, called the ViewModel The ViewModel will also contain another new class I’ll be talking about, called LiveData And I’m going to get into those much more later They’re part of the Lifecycle library But for now, what you should know is that the responsibility of the ViewModel class is to hold all of the UI data that is needed for your UI controller So the ViewModel will then be communicating with a class known as the Repository Now, creating a Repository class is a convention and it’s a suggested best practice, but it’s not actually part of the libraries It’s not a new Architecture Component It’s just a best practice And simply put, the Repository class contains the API through which you will get access to all of the app’s data
And in this case, our sort of data layer is pretty simple We’re just going to have a SQLite database, which you’re going to build using Room And Room manages all of the local persistence in the SQLite database Now, Room is a little bit more complicated than just one class It contains a bunch of different classes that work together, including entities, a DAO, and a database class And it’s built on top of SQLite, as I’ve mentioned a few times, and we’re going to get into those details very, very shortly So another core principle that I want to point out– so we have separation of responsibilities, but we also have the idea that the classes only reference the class directly below them So what I mean is for your UI controller or your activity, that class is only going to have a reference to the ViewModel The ViewModel, though, will not have a reference back up to the activity And in addition, the activity won’t reference the repository or the database This sort of strict rule of avoiding strong references from the activity to the database or in different parts of your architecture makes sure that you keep things modular and that your app doesn’t become a tangle of dependencies And what this means is that if, at a later point, you want to rewrite a portion of your app– say, you want to replace the Room database with something else– you can easily do that, and you would only need to change references in the Repository and not your entire app to be able to do that update It also makes things more testable So in some cases, you’re going to want to be able to communicate information back from a lower level on this diagram back up For example, if some data changes in your database, you’re going to want to be able to communicate that back up to the UI But the database doesn’t know about the activity, so how do we do that? Well, you will be using the observer pattern, and more specifically, you will be using LiveData, which I’m also going to talk about more, later In the past, you might have used callbacks, but LiveData and observation will replace that So now that you have a general idea of what the Guide to App Architecture’s architecture is, let me show you what that’s actually going to look like for your Word app So now we’ve transformed those names into the names of the classes that you will actually be creating In addition, you’re going to create a class called, New Word Activity, and the whole point of that class is just to edit text where you enter in new words Also, you’re displaying these words in a recyclerview, so you’re going to need to make an adapter for that recyclerview But hopefully, this stuff is pretty familiar to you, already So that’s our general structure And we’re going to go ahead and work from the bottom back up with all the concepts along the way So let’s go ahead and start off with Room So Room is a SQLite object mapping library, and it takes care of local data persistence for an app And Room has a lot of advantages over using the framework classes, and you’ll actually notice in our documentation, we’re switching over to Room instead of talking about the framework SQLite classes So, for example, instead of using SQLite over Helper, use Room, and it will make sure that you write a lot less boilerplate And one of the ways that it does this is that it maps database rows to objects, and vice versa So you don’t have to use intermediate values like cursors or content values You can get rid of all of those for your app So that’s cool Room also does a handy thing where it validates your SQLite queries at compile time So it actually won’t let you compile invalid SQL And it will also give you a helpful error message so that if you wrote your SQL incorrectly, you’ll know how to fix it Finally, Room has support to work well together with LiveData and RxJava for that observability, and I’ll be talking about that later So the fact that you don’t have to write a lot of boilerplate is because Room generates a lot of code for you, and it does this using annotations So by using annotations and generating this code for you, it makes it easy for Room to create a simple API for your database So let’s go ahead and look at those annotations So the first annotation is the Entity annotations The Entity annotation defines the structure of a table in your database The next annotation is the DAO annotation, which stands for Database Access Object, and it’s an interface that defines all of the read and write operations that your app will need And finally, we have an annotation called the Database Annotation that is placed on the database holder object So let’s go ahead and start by taking a deep dive into the Entity class So our data for this app is very simple The only piece of data that we’re storing is a word, and it is a single string So I tried to create a visual for you here of what that table looks like So it just has one column in it, and it’s for a string word That column is the primary key The word is the primary key And that’s about it So what does that look like?
So here is a plain, old object for the Word class that I just described to you And now it’s an Entity class That was pretty simple We just added a couple annotations So the first annotation that I want to point out is the Entity annotation All entities in your app need this Entity annotation to make it an Entity And again, that says this is going to define the schema for a table in my database Now, I’ve also added an optional property here, and that is to name the database table, Word Table Now, this is optional because if you didn’t use it, it would just name the database table the name of the class So it would have named it Word, but now it’s Word Table instead Now, when you mark a class as an Entity, it goes to all of your fields and it says, hey, these fields are going to be the columns of my database table So in this case, we only have one field, which is that Word field, so it’s only going to have one column And it takes the fact that the type is string, and it makes the database of the appropriate type there, too Now, if you have an object or a value in your Entity class that you do not want to be a column in the table– let’s say, you have a bitmap image or something– you can mark it with the @ignore annotation, and that will not become a column in your database table So then we wanted to showcase a couple of other annotations off The primary key annotation is important because every single Entity needs exactly one primary key Because we only have one field, it’s going to be the primary key Here are a couple other sort of optional annotations The not null annotation says, hey, this field is never going to contain a null value And the column info is also basically similar to the table name value that you have up there It says that I want to name this column “Word,” and if we didn’t include this optional annotation, it would just name it, MWord, which is the name of the variable there So another important thing to make sure that your Entities work properly is that Room needs to be able to have read and write access to all of them And you can accomplish this in basically three different ways The first way, which I’m showing here, is that my field is private But I provide a constructor to it, which has the exact field name as a parameter, and then I also provide a getter for it So that’s one way that you could do it You could also make all of your fields public, if you wanted, or some of your fields public Or you could provide getters and setters, and then you don’t have to worry about the constructor But you have to allow Room to have access to these Otherwise, it can’t do all the table creation that it does for you All right Now once you have your entities defined, you could go on to building the DAO object So again, the DAO object defines all of the database operations that you need for your app So let’s talk about what those are Well, here we’re showing a list of words So we probably need some way to be able to get that list of words from the database We’re also inserting a single word here, so we probably need a way to insert a single word And then finally, it’s probably good to have a method to sort of flush out our database and delete all of the words And put very simply, that’s all the functionality right there in our DAO Importantly, at the top of your DAO, you need to mark it with the @DAO annotation And the class or interface that you have either needs to be an abstract class or interface so that Room can go ahead and generate and provide the full method implementations for you Now there are three convenience annotations that you can use– Insert, Delete, and Update And these create the commands to insert, delete, and update objects, like the one that I’m showing you here for inserting a single word And this actually shows off Room’s powerful object mapping capabilities So in this case, I’m simply passing it an object as a parameter, and then Room’s taking care of putting that in my database for me I don’t need to do anything besides that Similarly, up above, you can see that I’m saying, hey, this method is going to return to me words, and room is able to do that conversion for me, as well And I can also return a list of words by just saying, hey, I want a list, instead of a single word And Room can take care of all the appropriate conversions for that object So finally, if you need to make any methods that are doing reading from your database or you have a more complicated query operation, you can use the @query annotation, and this basically lets you just write some straight SQL commands in your DAO So one more thing I wanted to point out about the DAO So actually, there’s a little bit of extra functionality So we want the following to be true When I add a new word to the list of words, I want the database to automatically tell the UI that it has updated And so, because I do that, it will mean that I can always keep the UI state exactly
in sync with the database’s state, which is great So to do that, we’re going to go ahead and use the Lifecycle library class called LiveData, and I’m going to talk much more in-depth about this But the basic idea is– so here’s what I originally showed you When you call this method, it gets you the current state of the list Now, if I make this tiny little change and just say, hey, I want you to return to LiveData back, instead, it’s getting you this object that returns a list of words that automatically updates itself when the database changes And again, I promise I’ll talk to you more about this later, when we hook up the UI to the data layer So here’s the final part of our Room database, and it is the database holder class that I talk to you about before It is annotated with @database, and very importantly, it also must be abstract and extend the class Room database So this is the class that’s generating your database tables for you, so you need to tell it about all of the Entities that you have created as an attribute, as seen here And you also have to provide an abstract getter method for the DAO So this is the code that actually creates that database for you It’s a builder class for creating your Room database And this code will either return to you a link to a new database if the device doesn’t already have one, or it will return to you a connection to a preexisting database, if one already exists on the device And importantly, I want to point out this one line that we chose to add in there So in this case, what I’m saying is, if the database schema is updated, I just want you to wipe out all of the data on the device It’s the most simple way to do a migration, is to just be like, hey, forget everything else and update my schema And because this is a simple app, I’m not going to get into migrations, but do be aware that there is a migration class and there are tons of options for making more complicated migrations of this very simple just flush and delete everything So the next question you might have is, OK, you showed me the code to construct a database or get a connection to a database Where does this actually live? Well, creating a database is actually a relatively expensive process, and you normally don’t need multiple instances of a database So the recommended practice is to actually create a singleton for your database– a static variable Singleton, and ensure that you only instantiate it once So that’s the code to do that You’ll be able to copy and paste in the Codelab, so you don’t need to take notes on all of this right now But one thing that I did want to point out is that down here, that’s that code that I showed you earlier, which actually creates the connection So that’s pretty much what you need to know about Room to complete the steps in the Codelab So you will complete the steps now to make that database They are steps two to seven There is a short link for you, so that you can get to the Codelab, and there are– actually, can all of the TAs just raise their hands and wave around a bit? And if you want to look around, you can see in the back of the room We have a of TAs You can do this Codelab at home, so one of the useful things about being here is that you actually have knowledgeable people around to help you So please, if you have any questions, go ahead and raise your hand So we’re going to be out 20 minutes right now Go ahead and get through as much as you can, and then we’ll start the Lifecycle portion of the lecture in Codelab [MUSIC PLAYING] So I want to talk about a few more additional Room features that weren’t covered, necessarily, in the Codelab, itself So one is the ability to auto-generate IDs So if you want Room to auto-generate IDs for you, that’s pretty simple Right next to your primary key, you can just use the annotation attribute auto-generate Set it to True It’ll start incrementing from 1, and just go up for you as you create new objects Now, Room also supports DAO methods with parameters for more complex queries, such as the one that you see here with this where clause This query returns words that start with a given substring As you can see, within the actual query itself, you just use the column name as a word, and then when you’re actually using the parameters, you can refer to it in the query string by putting a colon in front of the exact parameter name And that’s how you would use a query in your string So as you can see, we have a single parameter called substring here, and I’m referring to it in my query string just by putting a colon in front of it and writing it as plain text So finally, I mentioned that Room has this cool feature of Compile Time Checking So here is our working query, but let’s say that I was jet lagged a bit, and I typed something incorrectly, like doored, which is not a column that exists
If I do that and I try to compile, I’ll actually get an error that looks like this And maybe some of you saw this if you typed something incorrectly But that tells you exactly what the problem is, and then you could go and resolve that before running into a runtime error So that finishes up the basics of Room There’s plenty more to learn, and I encourage you to check out our documentation But I’m going to talk a little bit about the Repository class next So we’re moving up in our diagram here The Repository functions as a clean API for handling all data operations In our app, because our data back end is pretty simple– it’s just a database– it’s just going to be communicating with the DAO and making the database for you But in a more complicated app, the Repository functions as a mediator between different data sources like you might have So you might be able to imagine an example where you’re getting both data from your network server and you’re also getting data from a local data cache The logic about whether to grab new data from the server or use the local cache data and when to do additional fetches– all of that complexity would be inside of the Repository And what this means is that when your UI code needs some data, it just says, hey, get me the list of words And it doesn’t have to worry about all the complexity of, should I get this from the network or should I get this from local data cache or whatever else you might have there So it hides that complexity of where the data is actually coming from from the rest of your app And that’s actually it for the Repository The Repository isn’t part of the libraries Again, it’s just a best practice But we’re going to have you do that in this Codelab because we’re following the architecture So let’s go ahead and jump into the Lifecycle library So there’s a couple of core classes and concepts that you’re not going to use directly, but you should be generally aware of The first of those is that in the Lifecycle library, we now have an actual object that represents a Lifecycle in Android, and it’s just called the Lifecycle, or Lifecycle Similarly, we have the idea of a Lifecycle owner A Lifecycle owner is an object that has a Lifecycle For example, an activity or a fragment And as a support library, 26.1, app compat activity is a Lifecycle owner So you could actually get its Lifecycle object from it, if you’d like Finally is the concept of Lifecycle Observation So you can actually make a Lifecycle Observer It’s an interface for observing Lifecycles So if you’ve ever had listeners or services that require you to write some cleanup code in onStop, those listeners and services could be using Lifecycle Observation to be doing that clean-up for you So you, as a dev that is using those libraries, don’t have to do it yourself So that’s something, if you were writing classes like that And you’ll also see an example of how we do this Lifecycle observation later when we talk about LiveData So let’s go ahead and move up and talk about ViewModel So if you’ve watched the material that I’ve written and put out into the world, you probably know that ViewModels provide UI data for components while also surviving configuration changes A common example of a configuration change is rotating your device or changing languages as well So because ViewModels survive configuration changes, they can replace Async Task Loaders More importantly, though, they encourage you to have this separation of responsibilities that I was talking about before So one of the comments that early Android developers or potentially intermediate Android developers might find is that they end up putting a bunch of code in their Activity class, and they end up with a very bloated Activity class So what this architecture suggests for you is how you might be able to divide out that code a little bit more intelligently So in the ViewModel class, we suggest that you have all of your UI data, and then leave the activity class, just to be responsible for actually drawing that UI data So why is that useful for us? Well, let’s go back to a time before ViewModels, when you had your activity instance, and it contained all of its variables and UI data, itself The thing about activities is that they don’t live for a very long time if configuration changes are happening So let’s say that the user rotates the phone This means that the activity instance is destroyed, and then it’s recreated Now, the Android framework is smart, so it will attempt to save some UI data for you So for example, the current text in a Text View is automatically bundled up for you and on saved instance state, and it’s delivered to the recreated activity But if you have some UI data which is specific to your app
and is not necessarily immediately shown in one of the UI components, that won’t be automatically bundled up for you So most of you have probably used unsaved instance state to save this data, but it requires you to do that manual bundling up, serializing and deserializing of that data, which can be annoying but if you forget to do it, you will end up in this state, where you have not actually passed your full UI state on to the recreated activity, and that will create bugs for your end user, potentially even crashing the app So a simpler way to do this would be to use the ViewModel object, where this is not a problem So as you can see here, my activity data is no longer in my activity I’ve moved it over to the ViewModel, and my activity gets the data that it needs to be able to display itself by communicating with the ViewModel Now, as I said before, ViewModels survive rotation or all configuration changes So if a configuration change happens, activity still dies and is recreated, but importantly, that activity UI data did not go with it And all that my newly recreated activity has to do is reestablish a connection with the same ViewModel, which continued living throughout So here’s an example of what the ViewModel code looks like Importantly, to make something a ViewModel, you either have to extend ViewModel or Android ViewModel And the only difference is that Android ViewModel gets a reference in its constructor to the application class, if you need it So as we saw, the diagram– the ViewModel knows about the Repository, which is why we have a variable for that there And ViewModels also contain all of the UI data Well, what UI data do we need for this app? The list of words Therefore, that list of words is in the ViewModel And because we’re essentially creating a one screen app, we’re doing the shortcut where we’re creating the Repository in the ViewModel But in cases where you have more than one screen per app, which is probably the case for all of the apps that your working on, you probably also want to make a singleton for the Repository, and use dependency injection to get it inside of the ViewModel Using dependency injection means that you can easily mock the Repository, which makes the ViewModel incredibly easy to test And I was hiding this code from you before, but you will also additionally have the getter there and probably this insert method So your activity needs to be able to actually access the data So because my LiveData list of words up there is private, I’m creating a getter for it Notice how it is returning LiveData, and it’s not returning just the list of words This is important later, because you’re going to be accessing the LiveData in the activity Similarly, you will have methods for dealing with any user interaction that the user might have with your activity So the activity will not be responsible for doing any of the processing, itself When you insert a word, it will immediately tell the ViewModel, hey, a word has been inserted And then it will be the ViewModel’s responsibility to decide what to do with that And you’ll notice that our main activity knows about our ViewModel So let’s see how it does that So the code to set up that association looks like this And you’ll notice that you’re not constructing a new ViewModel there So this code happens at onCreate, and yeah You’re not constructing a new ViewModel You’re using a provider class, and that’s getting you the correct ViewModel reference, because remember, when we recreate the activity, we need to reestablish a connection to the correct ViewModel So ViewModel providers provide a ViewModel for a given Lifecycle scope So basically, a given associated activity And to identify what activity you want to get the ViewModel for, you have to put it in there as a parameter And in this case, we’re saying, hey, I want the ViewModel for this main activity And finally, you just need to tell it which ViewModel class you want So I have a couple of additional notes about the ViewModel Lifecycle So ViewModels survive configuration changes, but they don’t survive the activity being finished, So they’re not a permanent thing that stays around forever They’re linked to an activity Lifecycle, but they’re not linked to the app Lifecycle or anything like that So when the activity finishes, such as if the user presses a Back button or if they go to their Overview screen and actually swipe your activity off the screen, the ViewModel will also be destroyed as well So a ViewModel does not replace having a database and persisting your data It’s pretty transitory An important thing to realize, too, because I want you guys to avoid having bugs in your app, is that ViewModels are not a replacement for onSaved instance state, even though they seem similar And that is because onSaved– so if your device is very stressed out because there’s a lot of memory constraints going on, and your app is in the background, it’s possible that the OS will just kill your app And onSaved instance state is actually
useful for surviving this total app destruction ViewModels do not survive this They also get destroyed Therefore, in those cases, you still need to use onSaved instance state to get your UI state back to what it was before So this is fairly complicated, so I’ve written a blog post that talks about how you use, together, persistence onSaved instance state and a ViewModel to be able to restore that state And you can go ahead and read that if you’re interested So the final step is to see how we get this connection where the ViewModel is actually communicating its UI data back up to the activity via Observation And it does this with LiveData, which I’ve mentioned about 100 times at this point So in your DAO, we had a little bit of foreshadowing We already saw that we’re using LiveData here So LiveData is an object that is a data holder class, and it’s also Lifecycle aware It also allows for data to be observed And what do we mean by that? Well, many of you might already be familiar with the observer pattern, but I’m going to go over it quickly, in case you aren’t So with the observer pattern, you have an object called a subject And that subject will have a list of associated objects called observers that basically register with the subject and say, hey, I’m interested in you Please tell me when you change So then, when the subject’s state changes– some event causes it to change, it knows about that list of observers, so it will notify all of them And it will usually do that by calling a method that is inside of that observer object So LiveData follows this pattern almost exactly So in our case, the LiveData is the subject, and you will be creating objects called Observers, which are the Observers As far as where these things are located– Observer objects will usually be located in the main activity, if we’re talking about UI updates And Observer objects will be created in the main activity, and LiveData is obviously located in the ViewModel, in this case So in the main activity, you will call Observe on LiveData, which will set up that connection between the observer and the LiveData, and you will create a new observer object Then, because we’ve set up our LiveData to work with Room, when the database state changes, the LiveData updates And then the LiveData will tell all of the Observers that it’s updated, and the Observers will call this onChanged method Now, because we have our Observers being instantiated in our main activity, at least in this case, those Observers can easily update the UI of the main activity So what the end state of this is, is whatever the database state changes, that our main activity is updating its UI automatically, without us having to call anything else at that point So the observation code looks like this Remember how I said that getter in our ViewModel is returning a LiveData? Well, it’s doing that because we need the LiveData in the activity So here, we are getting the LiveData We are calling that Observer method, which sets up the Observation relationship We’re making a new Observer object, and when we make a new Observer object, we have to define the onChanged method And in that onChanged method, we’re doing a UI update And this means that whatever database data changes, that method is going to be called And as you can see, it’s taking our adapter and telling it to update the list of words that it’s showing And you’ll also notice how the type of LiveData determines the parameter of that onChanged method of the type of Observer that you have there, and it matches Now, one more thing that you might notice is that the Observer takes in your activity So why is it taking in your activity, also known as a Lifecycle owner? Well, that’s because of this other property of LiveData, which I mentioned very sort of quickly, which is that it is Lifecycle aware, and this has to do with Lifecycle Observation, which I mentioned before So LiveData actually uses Lifecycle observation It observes Lifecycle to make sure that it only notifies observers when they are in a started or resumed state So what this means in practice is that because you associated your activity with that specific Observer, it will only update your activity if it is in an active state, if it is actually on screen So it will never notify your activity of UI changes if it’s off screen, which is probably what you want because you don’t want your offscreen activity to be updating its UI In addition, the LiveData knows how to clean up Observers when they’re no longer needed So if the activity that’s associated with the Observer is destroyed, the Observer will clean itself up, which means that you are never in a case where your LiveData is updating a destroyed activity And this makes it so that you’re not going to have memory leaks So this Observe method is actually pretty powerful
It’s, again, setting up that relationship so that your Observer and your activity are linked together, and your LiveData can be Lifecycle aware of it So to reiterate, LiveData actually has a lot of benefits It is important for creating reactive UIs, which are UIs that automatically change when underlying data changes It’s also Lifecycle aware, so it only updates the UI when the UI is actually on the screen, and it cleans itself up automatically You’ll notice how there was no onStop method that said, Lifecycle, stop observing, or anything like that That’s that Lifecycle Observation And actually, if you were creating your own libraries and people need to call some method in onStop, you could use the same Lifecycle observation that LiveData does to get that same sort of capability And finally, it allows for the database to be able to communicate back up to any sort of UI controllers without the database needing to know about those UI controllers, which is very powerful when your UI controllers are being potentially destroyed Cool So this, again, is our whole architecture there And with that, you’re going to go ahead and continue on in the Codelab, and actually implement LiveData and ViewModel now [APPLAUSE] [MUSIC PLAYING]