Android Jetpack: What's new in Architecture Components (Google I/O '18)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

This was a problem I suffered with all Fragments and now it's fixed because the Fragment View has it's own lifecycle.

Here is detailed explanation of the problem: https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808

New solution in support lib 28:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  viewModel.liveData.observe(viewLifecycleOwner, Observer { ... } )
}
๐Ÿ‘๏ธŽ︎ 24 ๐Ÿ‘ค๏ธŽ︎ u/sebaslogen ๐Ÿ“…๏ธŽ︎ May 13 2018 ๐Ÿ—ซ︎ replies

The fact that this was ever a problem is even more evidence that Fragments are too complicated. We need something simpler.

๐Ÿ‘๏ธŽ︎ 16 ๐Ÿ‘ค๏ธŽ︎ u/RaisedByTheInternet ๐Ÿ“…๏ธŽ︎ May 13 2018 ๐Ÿ—ซ︎ replies

So, there is one more lifecycle now and if you accidentally use the wrong one in wrong place then you'll have a subtle bug.

Very elegant solution.

When I gave this talk I knew that we're going down the rabbit hole of ever increased complexity and subtle bugs, but the reality exceeded my worst expectations.

What I especially dislike about IO 18 is how easily googlers talk about their huge mistakes. Like it could happen to anyone and it's not a big deal: "yeah, we introduced this bad library/API/practice/bug/etc, but you know, Android is difficult, haha".

Arch components, Loaders, Fragments, SyncAdapter, JobScheduler, AsyncTask, numerous Layouts, etc. They just joke about how all of them sucked, or were too complex, or contained serious bugs, or whatever.

Well, IMHO it's not funny at all.

๐Ÿ‘๏ธŽ︎ 7 ๐Ÿ‘ค๏ธŽ︎ u/VasiliyZukanov ๐Ÿ“…๏ธŽ︎ May 13 2018 ๐Ÿ—ซ︎ replies
Captions
[MUSIC PLAYING] LUCAS BERGSTROM: Good morning. [APPLAUSE] It's great to see everybody here. Hi, I'm Lukas Bergstrom, Product Manager for Android Architecture Components. And before I get started, I'm curious how many of you were here last year when we announced Architecture Components for the first time? Awesome. OK, this is a much bigger venue, so that's actually a decent percentage. When we did launch Architecture Components last year, we were doing something sort of different for us. For the first time, we were offering explicit guidance in how to architect your app and giving you components letting you do that. And frankly, for us it was a little bit of a journey into the unknown. So 12 months in, let's check in on how we're doing. So we've shipped 26 releases since May of last year. So we've been constantly iterating and improving the core set of architecture components. In the sign of any healthy open-source project, we have a very active issue tracker. I'd like to thank everybody that's taken time to file a feature request or even a bug. And we've closed a lot of those. We launched a major new library, Paging, which is now stable. And we'll talk a little bit more about it today. And I'm pleased to say that based on our survey data, over half of you are using or planning to use Architecture Components. And this survey was done only a few months after we went stable. So this data is pretty out of date by now. But I'm pretty proud of the fact that only a few months after launching to stable, over half of the Android developers we talked to were planning to use this stuff. But more importantly than any of this, you've told us that Architecture Components actually make it easier for you to build robust apps. That having a clear path on how to architect your app and components that help you realize that has actually made a difference in the real world in how you build your apps. And we've heard that not just once, but we've heard that over and over from a lot of developers that have taken time to speak to us. So Architecture Components has grown. And we're going to continue to invest here. This is foundational, we think, for Android apps going forward. But not just are we investing in Architecture Components, but this year with Jetpack, we're going to take that same approach that we took with Architecture Components, a blank sheet of paper approach to how Android developer experience should be. And, you know, how we can improve things for you. And Jetpack is going to take that and apply that to the rest of the developer API service that we offer. So now I'm going to turn it over to you, Yigit, to talk about what's new in Architecture Components. YIGIT BOYAR: Thanks, Lukas. [APPLAUSE] Thank you. So we all talk about what we have been doing in this last one year. We all look at the existing libraries, talk about the improvements, and also look at the new shiny stuff, paging, navigation, and work manager. Let's start with life cycles. So we shipped lifecycles in the last I/O. But to better understand why we have created this component, let's go to two years before. In 2016, we did a bunch of developer surveys and asked developers, what is the hardest part of Android development? And by far in the list, big surprise, was lifecycle management. We're wondering, like, what can be hard about a phone rotating or user switching applications? This happens all the time on Android. Android is built for this. But if you look at the problem in detail, if you want to handle them properly in your application, you need to understand these state graphs very well. And when these two are interleaved, it becomes very confusing. So we have created lifecycles component to get rid of these problems. And it seems to be working because many developers give us testimonials where a group of problems just disappeared from their applications when they started using these libraries. Another important change for lifecycles was one year ago we had introduced them as an optional library. But now, they are a fundamental part of Android development. You don't need to do any additional libraries to start using them. Now both AppCompatActivity and the Fragment class implement lifecycle when they're out of the box. Another interesting thing that happened was the community adoption. So we ourselves create new APIs that works with lifecycles. But we know other people are doing the same in their libraries. And this is so much easier because already AppCompat has these dependencies so you can easily depend on them in your libraries. One great example of this is the AutoDispose library from Uber. So if you're using RxJava, but you want the automatic lifecycle management, you can just add this auto disposable onto your stream, give it a lifecycle, and it will manage the subscription for you for free. Now, working on these things we also discovered more problems we have. One of them is the Fragment view's lifecycle. Now, Fragment has a very complicated lifecycle. Let's look at an example. So if you have a Fragment that when it is created it stops observing LiveData, it goes through the regular creation. We create a View for it. And it goes to the resume state. At this point, if you are meanwhile using your LiveData, your UI will start displaying them. Everything works fine. Later on, user hits a button, so you want to detach this Fragment because they are going to another Fragment. And you are going to stop that Fragment. But once the Fragment is stopped, we don't need the View for it. You would like to reclaim those resources. So we destroy the View. Later on, user hits the back button. You'll go back to the previous Fragment. We reattach it. Now, because we have destroyed the View, we need to create a new one. We go ahead and do it, but this is a brand new View. It goes through the regular creation cycle. And now we have a bug. This new View will never resume the state of that LiveData because you are using the Fragment lifecycle to observe it. So we don't have any reason to redispatch the same value. Of course, if LiveData receives any value, the UI will be updated. But it's kind of too late, because on recreate, your UI has a bad state. Now, this left you with two options. You would either subscribe in onCreate. It looks very clean, one time setup. But it will fail if you recreate the View, so you needed to manually update the View. Or you were subscribing onCreateView, which handles recreation. But now you have double subscriptions that you need to remove. You lose the automatic lifecycle management. So the problem here is that Fragments have not one but two lifecycles. And we have decided to embrace it and give the Fragment View its own lifecycle. So now, starting with support library 28, or Android Text Fragments 1.0, while observing your LiveData, you can specify the real lifecycle. So while you are observing, if it is about the View, you use the View lifecycle. Otherwise, use the Fragment lifecycle. And we manage the subscription for you for free. OK, data binding-- so when we started to look at our offerings as part of Jetpack, we had decided to move data binding as part of Architecture Competence. But if you're not already using data binding, data binding is our solution for boilerplate free UIs. So if you have an object like this in your application, in your binding layouts, you can reference the fees in your object. And we take care of updating the UI for you. In Data Binding 3.1, we have added native support for LiveData. So if you're a view model like this, which has a LiveData for users, and if you pass it as a parameter to your binding, you can use that LiveData as if it's a regular field in your binding expressions. Data Binding will understand that is LiveData and generate the correct code. Unfortunately, this is not enough for it to observe it because Data Binding doesn't have a lifecycle. To fix that, when you get your binding instance, you just tell it which lifecycle it should use. It will start observing the LiveData to keep itself up to date. You don't need to write any code for this. We have also rewritten parts of the Data Binding compiler to be a lot more incremental. So if you have a project with multiple modules, it's going to compile a lot faster. We are also working on making the compilation even more incremental. But they're not finished yet. This new compiler also gave us the ability to support Instant Apps. Now you can use Data Binding in the future modules of your Instant Apps. OK, Room, my favorite Architecture Component. So Room is our solution for object mapping that minds the gap between the ASCII lite and your Java or coupling code. One important change we did in Room 1.1 was the support for better multithreading. So if you are using one Room 1.0, and if you have one thread that is trying to insert a lot of data into the database, and you have another thread that's trying to read data while the write is executing, your read will be blocked. And it can only execute after the write is complete. In Room 1.1 with writer logging, they can run in parallel now. And another nice feature of this thing, your writes run a lot faster than before. Best part of this change, you don't need to do anything to take advantage of this. If the device is running Jelly Bean or newer, and it's not a low memory device, we are going to enable writer logging for free. OK, another important addition to Room was the support for RawQuery. But to better understand why we need RawQuery, let's talk about the Query annotation. Now, when you are using Room, you can specify your SQL query in this annotation. You can use the name bind parameters. You can pass those parameters as regular function arguments. And you can tell the Room what to return. The best part of this setup is that Room is going to validate all of this at compile time. So if there's a mistake in your query, if your parameters doesn't match, or what you try to return doesn't make sense, it's going to fail the compilation and let you know what is wrong. Now, this is all cool except what if you don't know the query at compile time? What if you're writing a real estate application, my user can't search the houses with their price? Maybe they want to specify number of bedrooms, bathrooms, whether it has a yard. If you needed to query in a method for each iteration of this, that will be impossible to maintain. So what you do in this case is you create the query at runtime based on the options the user provided. And you prepare the arguments for that query. Now you obtain an instance of the Room database and use this query method to get the result. So far so good. The problem with this approach is that it returns your cursor. Who wants a cursor? You're trying to get the list of houses. So this looks like a failure of the library. Hence, we have decided to introduce RawQuery annotation. It looks very similar to Query except instead of specifying the query in the annotation, you pass it as a parameter to the function. And then you tell us what you want us to return. Now, here, Room cannot validate that query anymore. So it's kind of, you promised us to send the right query, and we'll take care of it. Once you have that, if you go back to our previous example, we can get an instance of our Dao. Now we need to merge our parameters and the query in this simplest guide query class, which is basic data holder. And then you can pass it through the Dao and get the list of houses-- no more cursors, no more boiler plate code. OK, Paging-- so Paging is our solution for lazy loading in RecyclerView. But to better understand why we developed this component, let's go through an example. So you have a list like this. It's very common. Every single application has this. User can scroll. But you know, it actually represents a much larger list than what's on screen. And if you're an application like Twitter, for instance, it's a really, really long list. So you probably cannot fit it to memory, and it's also very inefficient to log all of it. So what you will do is you will keep some of it in memory, the rest of it in database, and you also have your server compromised where you pull this data from. Now, this is actually very hard to implement properly. That's why we have created the Paging Library to make these common falls very easy and efficient to implement. Paging Library comes with a PagedList class, which is an actual Java List implementation, but it works with a data source. Every time you access the items in the PagedList, it pulls data from the data source lazily. So if user scrolls, you need more data, it just brings in more data. Now, let's look at how we can get an instance of these classes. It's actually super easy if you are using Room. So Room already denotes how to create a data source. It's a great example of how these Architecture Components work very well together. So you can just tell the Room to return your data source or a data source factory. In this case, I'm using a data source factory because data waves is something that changes. And each data source represents a snapshot. So we need a factory so that we can create new data sources, run database changes. Once you have that, you can use this Live Paged Builder class, pass the page size, and code build on it. It's going to give you a live data of pages of users. This is almost the same thing as LiveData of list of users. Now, in your activity or Fragment, you would use this page to set up there, which is the RecycleView adapter that works with PagedList. You would observe the live data every time we have a new page list, give it to the adaptor. And inside your adapter, you can just call this get item function to obtain the user object. This is a super simple code to write. And we take care of all the hard work of paging it lazily for you. Now, even though I have shown all those examples with LiveData, paging supports RxJava out of the box. So if you are using RxJava and you want that observable of pages, you can just get the same factory that Room generates. But instead use the Rx PagedList builder to build your observable or flowable. Now, paging supports paging from the databases I have shown here. But it also supports paging from the network. Or you can combine both database and the network for the best user experience. To learn more about it, please join me and Chris Craik tomorrow at 2:30 in the Paging session. All right, now about FOO. LUKAS BERGSTROM: I think, possibly, some of the suspense about what FOO is is gone now. But when you think about core problems that almost every app has to deal with, in-app navigation has to be close to the top of the list there. Right now the framework doesn't really offer anything for you to do or anything for you to use there other than start activity, which for various reasons is not the best option. So that means that for navigation, there are a bunch of things that you have to figure out on your own. And that ranges from executing a Fragment transaction without throwing an exception, hopefully. Passing arguments from place to place, possibly even with type safety if you can figure that out. Testing the navigation is working and that the right things are happening when you navigate from place to place. Making sure that the up and back buttons work correctly and take the user where they're supposed to go. Mapping deep links to destinations in your app and having that work. And by the time you've solved all of these problems, you've typically gone one of two directions. You've either written 60% of a navigation framework just for your app. Or you've got a lot of error-prone boilerplate. So everywhere navigation needs to happen, you've got a bunch of parallel lines of code that need to be changed any time the navigational structure of the app changes. And this is all pretty brittle and can end up being a headache. And individually these problems are pretty tractable. But when you compose them into a real-world example-- so say I have an item screened in my app, maybe a product screen. And that screen is accessible via Deeplink. But actually, there are other pages that if the user had navigated here by opening the app from the home screen, they would have come via the home screen, the category screen. And I want the up button to take them through those screens rather than exiting the app. So that means that if someone deep links into the app, I need to synthesize these screens and add them to the back stack, but only on a deep link. And talking to a third party developer, he said, you know, it's when you're in the middle of writing code to do this on any deep link into my app and synthesize the back stack correctly, you start to feel like maybe this is a failure of the framework. And so that's why we're really happy to be launching navigation, which is both a runtime component that performs navigation for you and a visual tool that works with XML to define the navigational structure of your app and then allows you to just navigate at runtime with a single navigate call. And so the kinds of things that you're going to get for free, that you simply need to define in XML and then the navigation framework will handle at runtime for you are animations, passing arguments in a typesafe way from place to place, making sure that up and back work correctly, and mapping deep links to various screens in your app. And last but not least, no more Fragment transactions ever. [APPLAUSE] So I'll show you a couple demos of this in action. The first one is just to kind of give you an idea of what this all is. So we're looking at a set of Fragment destinations in my app. And I'm adding a new one. And now I'm creating an action. And this action is the thing that I'm actually going to call at runtime to go from place to place. And you can see that there are a bunch of other options that we'll get into more in the navigation talk. But the one thing I do want to show you in more detail right now is the example we went through before. So this is a simplified version of that where there isn't a category screen. We just have the home screen and then the item screen. But I'm going to, right now, configure this to both have a deep link pointing at the item screen and to make sure that if someone deep links into the app, that they go to the home screen first when they hit up or back rather than just exiting the app right away. So first, I'm just going to configure a deep link on this screen. And the curly brackets that I'm going to put around item ID indicate that I want to extract a variable there and pass it as an argument into the item screen. OK, and now that's ready to go. And if I compile and run my app, that I'll just work and navigate to the right destination. Now, I just set the screen to the start destination. That means that it's the hierarchical parent of all the other screens in the graph. So when someone deep links into the item screen and then hits up, they're going to go directly to that home screen. So now I've just solved in 30 seconds what would have been a really terrible and time consuming task in Java or Kotlin back in the old world. So now I'm going to pass it off to Yigit to talk about Work Manager. YIGIT BOYAR: I've been using navigation a couple of weeks for some demos. It feels like magic, so I hope you all liked it. Our next Architecture Component is Work Manager. Now, Work Manager is our solution for deferable guaranteed execution. What do I mean by this? You're out of actions on Android that you really, really want to do if user does something. For instance, if user decides to send a tweet, you want to send it now. But if there is no network connection, you want to send it as soon as device is connected to the internet. There's things like uploading logs you may want to do if the device is charging. Or you may want to periodically sync your data with your backup. Now, we know this is not a new problem on Android. And we had some solutions for this. We have introduced JobScheduler in Lollipop. And we have Firebase JobDispatcher that back forced this functionality in the devices which has Google Play services. And we also have AlarmManager for exact timing. Now, each of these has different behaviors and different APIs. It becomes very hard to implement. Hence we have built WorkManager that sits on top of them and provides a much cleaner API with new functionalities. WorkManager has two simple concepts. You have the workers that execute these actions. And you have the work requests which trigger these workers. Now, if you want to look at a sample worker, this is basically all you do. You extend the worker class. You implement one function that says do the work. And that function just needs to return to us what happened as a result of that work. So you can do whatever you do, and you return the result. There is no services, no intents, no bundles, nothing like that. Once we have the worker, we need to create a work request. So you can use this one time work builder. Or there's a periodic version of this one. You can specify the worker class. But now, you can also add constraints. You can tell it only run if there's network connection if the device is charging. Or you can specify a back off criteria. So if the worker is failing, how should we retry it? You can also pass input parameters to these workers. Once you build that work request, you can get an instance of WorkManager and inquiries. Now, WorkManager will take care of executing it. One of the important distinctive features of WorkManager is that it has input and output semantics. So your workers can receive input, but they can also output some data. You could observe this data through WorkManager. But it was actually really useful to chain your workers. So imagine you have an application where user picks an image for their device. Now you want to run some image processing on that picture. And then once it's done, you want to upload it to your server. Now, these are two different units of work. Like, you can process the image. Maybe you want to do it when the device is idle. Or you can do it anytime. But to upload it to server, you need internet connection. But you don't want to wait the processing for the internet connection because it doesn't need it. This is super easy to implement in WorkManager. So we'll have two different workers. They all have single functionality. One of them does the image processing. The other one does the upload to server. OK, the helper function that receives an image file and creates the process image work request-- so it prepares the input, just uses the same builder to produce the request. And now we get that. Now we wanted our network upload to wait for the internet connection. So we set the constraint. We say, OK, wait for internet connection before trying to run this work request. And then create the upload image work using that constraint. Once we have them, you can tell WorkManager, OK, begin with the process image work. Once you are done, then run the upload to server work. And now you enqueue both of these as an atomic operation to the WorkManager. Now your device can restart. Anything can happen in between. We will take care of running the two. You can also use this API extensively. Like, you could run image processing in parallel the same way. So if user pick multiple photos, you want to process all of them, but upload it to server while some of them are done. You can easily do that with WorkManager. We'll just use the same function or create three work requests for each of the images user picked. We create the upload work in much the same way we did before. And now we say, OK, begin with all these three work items. Once all of them are done, then run the upload work. And then you enqueue that as an atomic operation. And it takes care of running it. Another important feature of Work Manager is that it is not just a wrapper for JobScheduler or Firebase JobDispatcher. It's actually an executor itself. So let's look at why opportunistic execution is important. So if you're an application where user can send an email, so user hits the send button, you send the job info to the JobScheduler or to the Firebase JobDispatcher. And it will eventually call you to execute it back. The problem here is that you don't know how long it will take. Even if you're device currently has network connection, it may take a couple of minutes for JobScheduler to call you back. And you have no control over it. You just don't know. And it results in a bad user experience. To work around that what you usually do is you also have your own thread pool. Whenever a user hits send, you try to run the same thing there as well. And you take care of the duplicating when the JobScheduler calls you back. If you are using WorkManager, you don't need to think about it. Because when you send the work request to the WorkManager, it puts it into its own database. Then it tells JobScheduler, or whichever scheduler it has on the device, OK, I need to be invoked when these constraints are met. But it also checks those constraints itself. And if they're already ready, it will instantly start executing the job. And later on, if JobScheduler comes in and asks to execute, now WorkManager knows whether it is executed or not and handles the request properly. To learn more about WorkManager, please join us today at 5:30 in the WorkManager session. All right, what's next? LUKAS BERGSTROM: OK, so I think it's been a pretty great year in Android app development. Hopefully you agree. We launched the set of great new components last year. And we kept working on those and iterating on those. We've launched three new major components, WorkManager, Navigation, and Paging since then. So does that mean we're done? Obviously not. We have a lot more to do. And the first thing that we want to do is we want to make Architecture Components the default way that people build Android apps. And that doesn't mean that it's going to be required. But it does mean that we want to make sure that as many people as possible get Architecture Components, regardless of how they get into Android development. So that means that not only are we going to be building more tools like the Navigation Editor into Android studio that are sort of Architectural Components aware, but we'll also be adding more templates that include things like view models so that people starting a new project have the easiest possible OnRamp into Android development. And in terms of libraries, not only are we going to be building more Architecture Components, and not only are we going to be building in more of the core Architecture Components goodness like lifecycle awareness into Jetpack, but we want to look at other Google APIs as well and see how we can make those Architecture Components aware. So that, for example, if you're calling another Google API that's asynchronous, it already has that built in. So that you're kind of getting Architecture Components benefits, whether you know you're using it or not. And then finally, we've heard from everybody that you want us to speak with a single voice. You want us to give clear and consistent guidance. So that means that in terms of education-- and that means not just documentation. But it also means sample apps, code labs, that all this stuff is going to be refactored to kind of be built on top of Architecture Components. So that whether you start with the guide to app architecture or you just download a sample MediaPlayer app and you start customizing it, that you kind of, regardless of what your on ramp in and development is, that we get you to the best possible place. We know that we still have some areas left to address in the core. And you can see some of those here. So this is just to say that we are definitely not going to stop investing in the original set of Architecture Components. And there is some, not just problem solving here, but some exciting stuff that we can do around, how can we make Architecture Components as idiomatic and fun to use as possible for people using Kotlin? So there's a lot to be done still in, I think, the core set of app architecture and lifecycle problem areas. So we'll keep working there. But beyond that, I think you'll see something interesting about our trajectory if we look at all the components we've launched to date. So last year, this set of Architecture Components was-- these are relatively small pieces, relatively small APIs that are designed to be used in a lot of different places in your app. And then if you look at Room navigation and WorkManager, these are much larger and richer APIs. But they're still relatively self-contained. They solve a single problem. They do it really well. Paging also solves a single problem and solves it well. But in this case, we took a very specific use case, so lazy loading for recycler view. And we're actually, in this case, orchestrating multiple Architecture Components and pieces of Jetpack to solve that problem. So Paging is a little bit higher level. It's not just here's your object mapping layer. It actually takes a very specific, I know I have a recycler view with more data that I can fit in memory. And it uses multiple pieces of Architecture Components to make that as easy as possible. And we want to continue to build more stuff like that. So we're not just going to keep investing in the core areas of app architecture and lifecycle. But we want to start solving higher level problems and make more and more as easy as possible. But I can't leave today without thanking everybody that helped us get here. The reason that we were able to have a really high quality bar for Architecture Components was because a lot of people, many of whom are here today, were really generous with their time. And that includes not just filing issues on the Issue Tracker, but also testing pre-release components, having one-on-one conversations with us to tell us what your biggest problem areas with Android app development were. This has been critical to us in making sure that we're focusing on the right problems and delivering solutions that are going to work for you. So I really have to thank everybody in the community that's been so helpful. YIGIT BOYAR: Thank you. [APPLAUSE] [MUSIC PLAYING]
Info
Channel: Android Developers
Views: 67,176
Rating: 4.870101 out of 5
Keywords: type: Conference Talk (Full production);, pr_pr: Google I/O, purpose: Educate
Id: pErTyQpA390
Channel Id: undefined
Length: 34min 6sec (2046 seconds)
Published: Wed May 09 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.