What's new in Jetpack Compose (Android Dev Summit '19)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[MUSIC PLAYING] ROMAIN GUY: Good morning, and welcome to What's New in Jetpack in Compose. I don't know why it's called that, because this is not really what the talk is about. It's more, what is Jetpack Compose? Anyway, I'm Romain Guy. I manage the Android Toolkit team at Google. CLARA BAYARRI: I'm Clara Bayarri. I'm a tech lead on the toolkit. ADAM POWELL: And I'm Adam Powell. And I am also a tech lead on the toolkit. ROMAIN GUY: And the toolkit now is also Jetpack Compose. My clicker. I need that. All right. So what is Jetpack Compose? The real title. So according to developer.android.com, Jetpack Compose is a modern toolkit for building native Android UIs. And it simplifies and accelerates UI development. Blah, blah, blah. It's all in Kotlin. So instead of trying to tell you what it is, I think it's more useful if we try to tell you what kind of problems we're trying to solve, what kind of goals we're trying to achieve with Jetpack Compose. We did some of that. And look, it's me again. Same short. Same pants. Different watch. ADAM POWELL: Consistency. It's good. ROMAIN GUY: Yes. So Adam and Jim were with me on stage and did most of the talking. So we explained some of the reasoning. And I wanted to go over other points. The first one for us is to simplify the development. And we all got used to the complexity of Android development. And the complexity can mean many different things. Some of our APIs already are awkward to use. You have two many different files, different languages. So I want to show you an actual example. So that's something that Jim, who works on the Compose compiler in runtime, has come up with to try to convince a bunch of people internally that Compose is a good thing. So the idea is to build a simple app where you have a list of names. And you just want to type something at the top. And it's going to filter the list. So how would you do that with the existing UI toolkit? You start with the simple XML file. You're all used to that, a recycler view with the text and the [INAUDIBLE],, all that good stuff. Then you need another XML file for each list item in the list. So we already have a couple files in XML. Then you need your filtered list view that's just going to do that find [INAUDIBLE],, find delete text, set up the listeners, find the recycler view, set up the adapter. I'm not even showing the activity here. Finally, you need the adapter itself. So actually, it's funny. Because when I put that on the slide, I'm like, wait, is that the best way we have to filter a list? So I Googled it. And it turns out-- [LAUGHTER] Because I did write some of the filtering APIs. So I thought there was a better way. It turns out there's not really. And there's a lot of blog posts, articles, and even Stack Overflow questions on how to do this. I'm proud to announce that this is one of the simpler ways. And then you can use RxJava or fancy frameworks to make this happen. Anyway, that's a lot of code. It's annoying. It is not simple. So this is the entire Compose version. All the code you need is there on screen. Much easier. It's going to be a lot easier to find bugs. And more importantly, when you read the code, it's pretty obvious what the intent of the code is, whoever read that code, what they wanted to do. So you say, I want a scroller. I want a column of text fields. Sorry. I want a column with a text field at the top. And for you each row I want an imagined piece of text. And as soon as you change the content of the list to contact list, everything updates as you would expect. Another thing we want to do is fix what's broken. So that's our API regrets. We talked about this at Google I/O. I invite you to watch the talk if you want to know more. We had a few funny anecdotes. One of the good ones, of course, is View.Java that now clocks at around 30,000 lines of code. Sure. There's a lot of comments in there, but still-- ADAM POWELL: I think that's down from some point. ROMAIN GUY: Yeah. But still, it's a lot. So the plan for us is also we have a lot of baggage and history. So there are things that we just cannot fix. We know there are problems, but we just can't fix them. We could introduce new bugs, or apps may be relying on the current behavior. So we're kind of stuck. We can't really do much more with the existing UI toolkit. We want to stop cheating. This is something we did originally. And I think Adam recently, you said on the Kotlin [INAUDIBLE] Slack channel that you really, really want add hide to disappear. So if you ever looked at the source code of the Android platform, we have this magical annotation called add hide, which turns public APIs into very private APIs. And super useful to us. It was a way to work around some of the limitations of the Java programming language. But it led, over the years, to an issue for us. For instance, let's say you want to do something with text view. That text view doesn't do well out of the box. So you have your sensors. Write your own text view. Well, you start pulling that thread. And then you're grabbing the entire text package. And you're running two priority APIs all over the place, and you can't do anything about it. So that's an issue for us. And it's an issue for us. So what we want to do with Compose is we don't want to rely on private APIs anymore. We kind of have to at the low level to interact with the platform. But all of our widgets are built entirely using public APIs that are available to you. And I'm going to show you actually an example of that. ADAM POWELL: And the good news is that it's kind of forming a forcing function for us to open more things in the platform itself along the way. ROMAIN GUY: And we have to think a little harder about what we open. Material and animations. Oh, I had a pretty heart emoji on the slide. It's gone. ADAM POWELL: Aw. ROMAIN GUY: I like hearts. Anyway, so now we're in the Material Design world. The existing UI toolkit was built before Material Design was a thing. So the good news now, we can take Material Design into account. This is what Clara and her team do in London. And we also are building animations in strong collaboration with the rest of the Compose UI toolkit. So we expect it's going to be easier and more powerful than it was in the existing one. Tools plus Compose. Same thing. When we built the initial UI toolkit, the existing one, there was not that much interaction with the tools team because the tools team was very small. So a lot of the tools came after the fact. So the visual editor, the [INAUDIBLE] that you saw in the keynote. And we want to change that. And you just saw that we're building things like live previews that are composable right now, as we're building Compose itself. We adopted Kotlin. We announced that we want to go Kotlin first. So that would be one thing for us to tell you to go Kotlin first. And we thought that maybe it was a good idea to do what we say that you should do. So Compose is returning to Kotlin. And Kotlin actually unlocks a lot of the things that we do in Compose. The way that we design the API is possible thanks to Kotlin. We rely heavily on lambdas, for instance. And we plan on using fun features like co-routines. Adam is chomping at the bits to add co-routines all over Compose. And finally, compatibility is something that we deeply care about. We are not asking you to rewrite your entire app. If you want to adopt Compose, once it's ready for production, we don't expect you to just delete all the code, like we did for Instant Run and to start from scratch. We want you to write your next activity, or your next fragment, or to be able to reuse some of your custom views. And chances are that some of the existing views, we won't even rewrite ourselves. So things like surface view or web view. If any of you wants to write a new web view, please come help us. But we're not going to do that. So we're going to use the existing [INAUDIBLE].. All right. And finally, this is what the technical stack of Compose looks like. So on the one side, we have the build time stack. So that's on the host that runs on your machine. So we use a slightly modified version of the Kotlin compiler. So we work closely with JetBrains. We just added a couple compiler extensions in collaboration with them. And we use those extensions to enable our Compose compiler plugin. So it's funny, because last week people saw that we had the artifacts on Maven for Compose. And people started using them. And they're like, oh, great. It works. We're like, no, it doesn't work. You need the compiler that we haven't released yet. Like no, it works. The reason it seems to work is because the syntax is just pure Kotlin. So it's just invoking functions. It was just not very reactive at all, which is kind of a bummer. Anyway, and so we also have Android Studio. And then on the runtime side, so Compose at the bottom is a runtime that just deals with trees. It knows how to produce trees. It doesn't know anything but UI toolkit. So we could reuse it to do other things than UI. Then we have the UI core, the UI foundations, and, finally, on top, all the widgets. So that's pretty much it. And now Clara will tell you more about how Compose actually works when you write code. CLARA BAYARRI: Thank you. [APPLAUSE] Sorry. Get your applause. I want to walk you through some of the things that you need to understand to really be able to play with Compose, some of the basic concepts. The first one is composable functions. I know Romain has kind of thrown this term out a few times. But a composable function is just a function. It's a function that takes some input, and just decides what UI to show based on that input. So in a way it transforms your application data into your UI hierarchy. Creating a composable function is very, very easy. So, for example, we have this greeting composable. It takes a name as a string. And it just decides to emit a text element that uses that string. Text is one of the components that we will provide for you. It is the equivalent of a text view. So it shows a text on the screen. The interesting thing here is because we have the runtime and the compiler behind us, whenever you change that input-- so say the name you're going to pass through changes-- well, then we take care of figuring out what changed, re-invoke the function, and figure out what we need to change in the UI hierarchy for you so at any point you are just describing what you want to see. And we'll take care of the rest. Say, for example, we want to build a news story. We can actually omit several composables within a composable function. So we can omit, say, a title, subtitle, and a date. And we're just going to build our news story. This is absolutely fine. But you will find you kind of need to have an opinion on how you want your UI to show. Because if you just do this, you will end up with something like this, which is not really what you wanted. So this is where concept number two, layouts, come in. We are building out a bunch of layouts for you out of the box to make sure that you can place things on screen as much as you want. So, for example, we can wrap those three texts in a column. Column is the equivalent of a vertical linear layout. So we'll just place everything one on top of the other. So then suddenly this looks more like what we were expecting. Right? So we have our title, subtitle, and date. Still, Android developers are used to being able to customize a lot of things. You'll be like, ah, you know, this is nice, but my text is really stuck to the side of the screen. So we can go a bit further, add some spacing. And what that will do is just create the spacing around it that we need to make it look a bit better. So until now, we've built this entire UI just as like one function. Right? Going a bit further, column doesn't just take text. I know I used three text as an example. But we can throw anything in there. So, for example, we can just display an image by using an image composable, and then build up our UI in this way. Huh. Oh, yes. Sorry. We're providing a bunch of layouts out of the box. There you go. We have row, which will place things horizontally. We have column, which will place things vertically. Those are easy to understand. We have more advanced things. For example, we've introduced flex, which I believe is new to Android. We're building a bunch more. There's a bunch more in there that you can go and play with. And we are working on ConstraintLayout. But it's just not there yet. So look forward to that coming. And then, well, we've built a layout, but really what you want is for us to build components for you. Right? So Compose comes with a bunch of built in components that should make it really easy for you to get kickstarted. First of all, we've partnered very heavily with the Material team. Because we want to make sure Material is out of the box just there for you. So we started creating the Material components. They're not all there yet. But the team has already been hard at work to be able create those beautiful UIs. And we already have things like buttons, checkboxes, and a bit more complex things. App bars, drawers. Some of these pieces are in their influx. We'll get there. But you already have the basic pieces to build something like a sample app. So say we want to build this Material card, building up on the example we had before with the image and the three texts. Well, we'll take the code we've had before. I haven't changed anything. We wrap it in a card. And we can give a card a shape, cause this is a material thing that you can choose what your corner should look like. So, for example, I want rounded corners on my card. We can style the text to look prettier than it was before with the three same styles. And you'll say, here you're reaching into a theme text style. This looks a bit weird. So let's take a step back to the Material spec. So in the Material spec there's a definition of all the typographies that you could define in your app. So how you define all the styling of your entire app. There's a pre-set amount of textiles that you could use, just like they have a color system and a shape system. We've built this out of the box for you. So if you buy into the Material system we have a Material theme composable that allows you to configure all of the colors, all of the typography, all of the shapes you need for your app. And then when you put this in your hierarchy, any other Material component you're using will know how to read all these values by default so you don't have to actually style them. Similarly, you can reach into these values. And that's what I'm doing in this example. So I'm saying, out of these three texts, I'm reaching into the style and saying I want H6, subtitle one, and body one. It's a very easy way to style if you've set up all your styling for your app correctly. And finally, to finish the app, I can add a row with a couple of buttons. Again, we've provided the material implementations of buttons. So material defines three different kinds of buttons. There is contained, outline, and text buttons. And you will find that we keep the same terminology to make it easy to find things. So we have contained buttons, outline buttons, and text buttons available. So all of the Material components we provide we've built purely on the public API from layers below us. So Compose UI core provides some foundation for us. And we've built everything layered up on top of that. So we've tried to not cheat with any private APIs. This means that Materials should be a showcase of how you can build components. We're hoping they're samples. Right? They're very useful components. But at the same time, it should be code that you can go in, read, and see how we've done it, and take those examples for yourself. We've also tried to break everything down into multiple layers, because you can break everything into more and more functions, to make sure there's as many reusable bits in there as possible for everyone. And as part of that, we've created this foundation level in the middle. The main idea there is, there's a lot of material that is not necessarily design specific. There'll be a bunch of gestures, accessibility that we've implemented to do a button. So we can put the actual button in Material. But then we can put the internals of the button in foundation. So all the gestures, accessibility, other things that we've built that are not UI so that you don't have to start from scratch. You can already start from that layer and just build your UI on top. And then no matter how many components we give you, there's always going to be custom views that you'll want to do. There's always going to be that designer that asks for a very strange thing that we had never anticipated. So in the existing toolkit, you have to extend a view. You have to take care of a bunch of things. It's a lot of work to create a custom view. In Compose, it's extremely simple. You just have to create a function. And there's some building blocks that you can do to help you build up that custom view. The two basic ones are draw and layout. So draw will give you a handle into doing any kind of custom coding that you want. Say, for example, we want to do a checkbox. And a checkbox is just a square with rounded corners and then a checkmark. Right? So let me walk you through the code to do this, cause it's quite simple. To draw the box, we open a draw tag. We get access to a canvas and the sizes we've been measured to. So we can easily calculate, how much do we want our square to be? Apply the rounded corners, and then just draw the square. Same for the checkmark. We'll open a draw. We have access to the canvas and the sides. We can calculate the three points that make a checkmark, and then just draw those on the screen. So surprisingly simple. The next part is layout. Of course, we will try to provide as many layouts as we can out of the box. There will be always something that you want to do that is extremely custom. Well, you can reach down into the layout component. I've tried to show here an example. The most simple example I could find is a stack or a frame layout. Right? Like stacking things one on top of the other. So you will create a function that takes a bunch of children that it needs to place. We open a layout tag. We get access to these children that are measurable pieces, all the constraints we have. And then we just have to go through two easy steps. We measure each of the children to make sure we know what size they want to be. And then we decide what size we want to be. So, for example, in a stack I'll probably want the maximum of the children to make sure they all fit. And then I'll place them each at the 0, 0 coordinate, cause that's what a stack would do. So then we'll end up with an effect that is something like this. Hand it over to Romain. ROMAIN GUY: All right. So what do we have today? Like I mentioned in the keynote, we just released our developer preview. It's called 0.1.0-dev02. So if it's not clear enough, it is not quite ready for production. But I want to show you some code and a demo. So if you can switch to the demo machine, please. All right. So one of the simple apps we have is called JetNews. You can see it on the right in the emulator. So it gives you a good idea of the kind of things you can already build today. We have horizontal scrolling lists, vertical scrolling lists. The tech stack is already pretty advanced. So you can do a lot of complex text layouting and styling. So this is a very interesting app to look at. It's not a real app. It's not connected to a database or web service to keep the sample simple, but it's a very good example of how to create your own widgets and to use the existing widgets. So what I wanted to show you is what you see here. So yesterday I built this little to-do list app. So I've got the old [INAUDIBLE] icon in the bottom right. And when you click on it, I just want to add list items. And each list item will have a little bookmark icon that I want to be able to toggle. So the way it works here, [INAUDIBLE] is a simple data class in Kotlin. Is the text big enough, by the way, on the screen for everyone? All right. I'll assume it's a yes. So we have a data class. It's immutable. Everything's a vowel. And you can see that night time can be a favorite, and just a description and an ID. Then the actual model for the app is just this empty list of to-do items. And it's going to be an immutable list. And I wrapped it into this object. That's probably not how you would do it in a real app, like using an object like this as a global object. But I used this at @Model annotation. And an at @Model basically turns this object into an observable object. So whenever I change the item's property, the UI will re-compose automatically. And Adam will tell you more about this. And if you want to know even more details, Dylan has a talk tomorrow where he explains how it works internally in the compiler. This is the main part of the application. So very similar to the way we saw this earlier in the keynote. I have a vertical scroller. I have a column. I just iterate over all the items. And then for each item, I create a row. You can ignore the layout parameters. But I have this bookmark icon. So this is a composable I created. And one of the things I really like about Compose-- and that was one of the things we really wanted to do. We wanted to make it easy to create your own widgets. Very often in Android applications we end up with very large XML files, because it's very cumbersome to create all those XML files, doing the [INAUDIBLE],, doing the includes, and the merge, and all that stuff. And here all you have to do is when I first wrote this application, I wrote everything into these items composable. And then when I saw that, I had this bookmark icon composable. I just selected it. I extracted a method using a refactor in IntelliJ. And then I just added my add composable annotation. And that's all you need to do to create a new component. You don't have to extend from view of your group in override methods. And then I just have a small spacer, and finally the piece of text that describes the item. So the bookmark icon is here. And here you can see another interesting-- oops. I'm revealing what I'm going to type. That's my cheat in case I forget what to do. So my bookmark icon is pretty simple. It takes one of those to-do items. It's read by a clickable. So that's how you create a click listener really. And inside all I do is, I have this branch, and I say, if this item is a favorite, then I want to show the image that corresponds to the favorite state. And if it's not a favorite, I want to use the other image. So it shows you that it's not just about using four loops, one instance for each. You can use any kind of control flow you want. And it makes the code extremely easy to read. And you could do this piece of code in different ways. Anyway, so I have two callbacks in the app. I have this toggle favorite, when we're going to click the bookmark icon. And I also have one when we click the [INAUDIBLE].. So I'm going to try to write them. So when we add an item, I'm going to take the list of items, and I'm going to add a new item that I conveniently have in my clipboard. So it will just give you a default string and an ID. So if I rerun that-- And you can note, that's all I did. Right? There's no listener. I don't call re-layout, request layout, or invalidate it. And when I click the button, a new item appears in the list. And that's all you have to do. And that's thanks to this @Model right here, because we observe that the items are changing. But now I want to be able to toggle those little bookmarks. Like when I click right now, nothing happens. So the way I'm doing it is because it's an immutable list-- Let's see if I remember. So I'm going to do a map. I feel so fancy when I do functional programming. Let's see. Oops. It.ID equals the item we clicked. ID. That will return a copy. And I will do favorites equals not todo.favorites. So I'm just toggling the Boolean. Otherwise it'll just leave the item unmodified. All right. That should work. Relaunch the app. So add a few items. And now when I click, it toggles the icon. Yay! Ooh. Hm. [GROANS] And that's why always be prepared. All right. I will make it work. Oh, yeah. I used the wrong item. All right. Add items. Bookmark. Yes. All right. Now it works. So again, the point here is to show you how easy it is to manipulate your data and to build that logic, and make it interact with the UI. And more importantly, you can keep both kind of separated, or more separated than you could before. But Adam will tell you a lot more about that. Can you go back to the slides, please? ADAM POWELL: All right. So this is the part of the talk where we talk a little bit more about kind of our design thinking behind Compose, and kind of what's on our mind. So in addition to the team's ongoing work with Compose, we've also been spending a lot of time talking with the community. Some of you, have probably been part of that conversation. As well as running some more formal user studies. And so here are some of the things that are really sort of top of mind for us that we're excited about or that we think is really kind of important as you take it for a test drive. So be careful that some of this isn't implemented yet. This is just kind of how we're thinking about some of these open questions. So please join us in participating in some of the design process. So the first thing that really kind of hits you as soon as you start working with Compose is the data flow. And while some of the tools available for managing that data flow came into Compose very early, or they existed before Compose did in the case of just some of the standard functional and reactive systems that you might be used to, the best practices and teaching materials around all of this is still kind of a work in progress. Like we really are building something new here. So the Android architecture components libraries have been really successful so far. And it set the stage nicely, and established patterns that we've continued with in Compose itself. So let's go ahead and distill down some of those ideas and kind of see where they lead us. Next. There we go. So we showed this diagram at Google I/O during our talk on compose there. Dataflow is really the first thing that you encounter, like I said. The shape of the API more or less enforces this top down, one way data flow approach. It's just functions calling functions. And most people like this so far. So it's always good to have a single source of truth in your UI. And it's kind of frustrating when your views have a different idea about the state of your app than your application model does, which is definitely the case when you're working with views in the average Android app. So what do we mean when we talk about single source of truth in a UI? Well, it's about avoiding conflicts or making sure that you're not showing inconsistent data. But it's also kind of about who has write permissions to that state. So with Compose you prevent inconsistencies by having the owner of a piece of state pass that state to the other composable functions that need it. So the parameters that we call our composable functions with, they have to come from somewhere. And the source of that data is kind of our source of truth, by definition. And the interface that we use to pass those parameters really kind of determines what the composable functions can do with it, just like with any other function. So you see access control layering like this in Kotlin frequently in the standard library. You've got the list interface here, which only defines the read methods. But the methods to actually change that state are defined in mutable list. So even if I have a mutable list, if I pass it to a function like the aptly named do stuff over here as the read only list interface, I can be reasonably sure that do stuff isn't going to change my data out from under me. So from a certain point of view, source of truth has to do with who has access to a mutable reference to that source of truth, and what kind of mutations a caller can perform with that reference. If you can change the truth in any way that you want, are you yourself a source of it? Well, kind of. So we use event handler lambdas kind of liberally throughout Compose, because it's kind of a more restricted way to express a very specific mutation that you can make to state without actually giving away the keys in the process. So here's kind of what I mean by that. We can show this just by using views with some of the APIs that you're used to. So here I've just added a click listener of the view. And inside that listener, I'm using the fact that I have access to this myList variable from the example before. And that's our mutable list. So that's in my lexical scope. So I can capture it as part of a listener block and just use it directly. I haven't actually leaked a mutable reference outside of this code snippet. The block where this appears is the only place where mutable lists is accessible from anything. I haven't permitted mutation in any sort of uncontrolled ways by giving out a reference to it. So all my view can do is just tell me that a click happened. And I've defined how this higher level will respond to those clicks, given this layer's access level. So this is a really simple example. And we're just kind of using lists. And we definitely did some in the demo as well. It's sort of a hand wave for, hey, this is the rest of the @Model. I mean, clearly you'll be doing something a little bit more sophisticated than direct mutations on lists. So bear with us a little bit. So a more real example might do something kind of like this. So in this sort of sample view binding method, I'm setting a click listener and make some requests to my app based on the parameters that I was passed to when the bind happened. So just like before with the mutable list, I didn't teach buttons how to add and remove bookmarks. I defined what a bound button should do when the button raises a click event. And I used the context that was available to me when I bound it-- that's just the lexical scope-- to form that definition. So this is all pretty standard stuff. So what's actually new here? I mean, this isn't unique to Compose here. But it's kind of the framing for what comes next, which is that Compose code is really, really dense. We think that this is pretty great that you can remove so much boilerplate, but the trade off for that is that sometimes the distinctions of what you're really doing become kind of subtle when it's compressed into such a small space. So let's look at the same pattern that we just saw using Compose. In this case, it's not actually that much smaller. It doesn't look that much different. We're declaring the actual button as an implementation detail, along with its properties. So what its text is and what'll happen when it's clicked. Same thing. But the syntax here is kind of interesting. We said before that state flows down and events flow up. But we're passing both text and the onClick handler to button in the same way. They're both parameters. So in a way, event handlers are just another kind of state that flows down. It's a state that the bookmark button created and that it owns. So as far as button is concerned, it's just this opaque token that can be invoked. So when it is clicked, we make a request to this bookmark or object that's not yet defined here to please add or remove the associated bookmark in response. So the bookmarker might be backed by a local database. API calls to some back end. Really whatever. I can fake it or test it outside the context of my UI. But I've defined how the UI itself interacts with the bookmarker in one place here, along with the structure of the UI that's being manipulated. So bookmarker can be written to expose whatever operations that are appropriate to its clients like this one. So the single source of truth here is preserved. So that's a single snapshot of that truth. But the real power of a declarative UI framework like Compose is that it can keep up with changes over time. So it'd be really nice if my code was always this straightforward, even when time is involved, and when changes over time is involved. So we saw a little bit of that in the demo so far. So if you want to update the UI in response to changes, well, you need your data to be observable. And Compose is ready for that. We saw in the demo that when you annotate a class with @Model that Compose will know when its properties change. But why did we add something new here? The Android ecosystem has a lot of existing observable constructs. So what gains do we get from this that justify adding something new? Well, we think that the answer lies somewhere between the ease of understanding of the kind of code that we can write this way, and also just kind of the ergonomics of actually using it in practice. So let's talk about live data for a moment. Because it really has some neat properties for building UI. Fundamentally, live data is a single value holder. It'll tell you when it's updated. So for our purposes in a UI, only the latest value matters. Live data will conflate its values and only keeps the latest one. There's no such thing as missing an event. You don't actually have an event streamed. There's only the latest state. And @Model behaves much in the same way in this regard. So let's take another look at an example, kind of like the bookmarks code from before. If you've worked with Android architecture components, view model, and live data, you've probably seen something like this before. The view model sort of becomes this hub for a bunch of different observable data, along with some operations to request some changes to that data. So we can reflect the same sort of structure with @Model, but you can see something kind of different here. So the hasBookmark method that we used in the example before, it isn't returning a mapped live data that has to be individually observed. It's just accessing the internal object's state. The list of bookmarks itself isn't even public. @Model has turned any normal reads of the object's properties that happen within a special scope into observable subscriptions even if that read happens layers down the call stack. If I called hasBookmark through some other helper function that I extracted in a refactoring or something like that, even in a completely different class, this would still work. We don't have to turn it into a stream or live data mappings each step of the way. So if we take a look back at our bookmark button from before, we see that our UI can be expressed in terms of these snapshots of state and time. And Compose can help manage the complexity of wiring these subscriptions together for us, even if the actual @Model objects might be several layers away across the call stack or across an object reference chain. So Compose will automatically insert these observable scopes for us in composable functions. We don't even have to think about it. So let's go ahead and recap on that. So we need observable data. Compose will add @Model classes as a new tool that we have available. But the cool thing about it is that it allows for the sort of cross cutting observability. So we can write less plumbing and subscription management code in our apps. But the thing is is that all these other observable and reactive systems are great. You don't have to leave them behind if they're a natural fit for what you're doing, especially if it makes sense in other layers of your application. So if it makes more sense for the bookmarker at this particular layer that I've defined it to return a live data or flow, or something from hasBookmark, then we can still support automated but explicit subscription management by using an effect function, kind of like this. One more. There we go. So that's kind of the tour of the background behind some of that. So really what's left for us around data flow is kind of documenting recommendations for the question that's kind of left that you're probably all asking yourself, which is which of these tools should I use when. So kind of establishing some of these guidelines and best practices are still kind of a work in progress here. So more things that are a work in progress. I hear a lot of questions about how to work with resources in Compose, and kind of what we plan to do there. So let's talk a little bit about that too. Again, this is current thinking, subject to change. But this is kind of where our heads are at at the moment. So when it comes to using resources and the UI toolkit, we kind of think that we've learned a few things along the way. And this is kind of a big one. So really we need to avoid too many overloads. Some of you might recognize where these are from. We have a lot of ways to set an image. ROMAIN GUY: It's a convenience. ADAM POWELL: It's very convenient. But the thing is is that this makes the view in question huge in terms of its own implementation. But even worse, it means anything that tries to wrap or reuse one of these in a composable fashion has to reflect the whole capabilities of the API surface. We don't want this. So it's annoying for us to maintain. And it's even more annoying for anyone who wants to build something on top. Now, there are a lot of reasons why you might want to offer overloads for your own composable functions. And we would really be doing you a disservice by asking you to multiply that by the number of overloads that you might need to cover for this form of completeness because some underlying piece that you're relying on offered all of these different ways of doing a single thing. So that's something that we'd kind of like to avoid. So we've also seen a lot of other libraries appear to fill the gaps in between what Android views offer and what you need in your app. So image loading libraries are a really great example of this. There's a bunch of great ones out there. And they apply different loading and caching policies for images, tie into lifecycle, so on and so forth. But really this is because a lot of resources are dynamic. They might come from the web. They might come from a disk cache or plenty of other places. And tying into the appropriate lifecycle events as signals, it's really not always fun. There's a lot to get right. So by focusing so much on static resources in what we offered, we kind of created this privileged type of data. It created a blind spot for us when we were looking at the system kind of from the inside. And it should be much more straightforward to write these dynamic resource policy layers. And static resources should have to play by the same rules. So using the same public API services to operate. So co-routines are awesome. Romain mentioned sometimes I won't shut up. ROMAIN GUY: Yeah. You never shut up about it. You're not the only one. ADAM POWELL: There are more. So we still have a few issues to work out with the experimental compiler around generating suspending code. So we haven't done a whole lot here yet. But our intention is to standardize on co-routines as our async primitive for working with resource loading, whether that's static or dynamic. So Android's R class is also pretty convenient. Having a bunch of symbols that'll autocomplete for the resources in your app is pretty nice. We'd like to keep that sort of convenience, but really all of them being ints is not so convenient. ROMAIN GUY: And I ran into that yesterday while building the demo. I would get a crash, because I had the PNG files and an XML file with the same name. So the runtime would pick the XML file and try to load it as a bitmap. And blah. ADAM POWELL: Yeah. You had some fun with that. ROMAIN GUY: It took us five minutes to figure it out. And we wrote the god damned thing. [LAUGHTER] ADAM POWELL: All right. I think that we might be able to squeeze one more into this list if someone will get out of the way there. OK. So layering is a pretty great idea too. Because once we start talking about type safety and resources, and avoiding overloads, the natural question comes up, why should any of this be specific to Compose? We're really kind of looking at how working with APK resources might be made better whether you're using Compose or not. And while we're talking about things that are great, whether you're using Compose or not, let's go ahead and talk a little bit about view compatibility. So we like Kotlin itself so much because you can adopt it in an existing project at your own pace incrementally, just a little bit at a time, one class at a time, one test at a time, et cetera. So we're designing Compose to be used in the same way. So if you have an Android app today, you probably have a lot of views with layouts, maybe even fragments that you've extensively tested. So they work. Even once Compose graduates to a stable release, rewriting your existing and working code upfront probably doesn't sound so great. So you don't have to. So let's take a look at a couple of the things that we have in mind here, and how this might look once we spend a little bit more time here. So the first step in interop for us is being able to use just a little bit of Compose inside an existing app. The API for this is about as simple as it gets. It's just one annotation. And this may look familiar if you saw our talk from I/O earlier that you'll be able to annotate a composable function. It'll ask the Compose compiler to generate a view subclass for you. This'll host an instance of that composable function, with appropriate setters, so on and so forth that you can use to feed it data in the same way that you would via its parameters. So this will let you use Compose alongside your existing view-based UI. So this still isn't implemented yet, but we're pretty confident that this is the way to go. And that's kind of the way that things always go. Right? It's the things that you're most sure about are the things that you do last. ROMAIN GUY: Well, someone has started working on this. It's just not finished. ADAM POWELL: Right. So the next step is a little bit less certain. And this is something that we're going to be doing some prototyping on. And you can follow along with us in AOSP as we kind of play with some different ideas. So even if you dive into Compose head first, you'll probably still need to use views within that UI at some point. Maybe you're integrating a third party SDK that doesn't offer Compose integration yet. You still want to be able to use it. So one idea that we've had is to leverage the new view binding feature that you might have seen in recent Android Studio builds. So if you're not familiar with it, if you have a layout XML file that looks something like this, view binding will generate some code that lets you consume it like this. So you get a type safe object generated for you that you can use to bind values and listeners to it in code. So you could imagine that turning into something maybe like this when you use it from compose. While this might be nice for giving life to some layouts that you've already setup, it implies some additional work if you just want to use one custom view. Another idea is that we could use the composition system itself to directly manage a view hierarchy. The Compose compiler and composition runtime were originally designed to manipulate the view tree directly. So here, rather than reusing layouts, you could use any existing view subclass as if it were native to Compose UI. But a limitation that this has is that it might make things look a little bit too similar. There's really a lot of mismatches at the boundaries. And some things, like, most notably, edit text, comes along with a lot of gotchas when you're trying to use it in this sort of reactive style, where the single source of truth lives outside of that view. So the existing view implementations still fundamentally want to own their state. And layout contracts work differently, so on and so forth. So we're trying to figure out what the right balance is with some of these things. So this, like some of the other design spaces that we just talked about, are going to be occupying a good bit of our time over the coming weeks. So we want to be sure that interop with your existing code is as straightforward as we can make it, while not leading you down a path that ends up creating additional work if your constraints change. So we'll keep you updated as development continues, but hopefully by now you're asking another question. CLARA BAYARRI: So you've heard what we can do. You've heard what we're trying to get to do. But how do you play with it? Well, today we've published a brand new website. So d.android.com/jetpackcompose. We've updated the content in the Jetpack Compose website, including, for example, how to file bugs. So please, go check that out. As part of that content we've published a tutorial. And the tutorial will guide you through the basic principles of Compose, kind of similar to what we've done today, but much more in detail. So please go check that out. You can read through it and it will make sense. Or you could go and download the new Android Studio build that supports Jetpack Compose. It should be up soon I think. [COUGHS] ROMAIN GUY: Sorry. [LAUGHTER] CLARA BAYARRI: You will also get a new template-- ADAM POWELL: He's uploading it now. CLARA BAYARRI: --for a Compose activity that will set up all your dependencies and everything. So you don't need to go read random blog posts that will tell you how to set it up. You can try the new preview that we showcased at the keynote. You'll be able to preview what you're building with Compose. You'll be able to see the sample. So we published JetNews as our sample. It showcased everything that is there now and how you can use it to build this news article app. And then other things that you can try while you're here at the Dev Summit, there's two codelab sessions happening. The codelab is also available online if you're streaming. And we have two talks that you should go check out. What's New in Android Studio, where Tor and Ja will walk through the tools that we're building for Compose as part of that. And tomorrow we have a talk from Leland, who's in our compiler and runtime team. And will explain a lot more on how the insides of Compose work, and kind of demystify the compiler, and what we're doing behind the scenes. Thank you very much. ROMAIN GUY: Thanks. ADAM POWELL: Thanks. [APPLAUSE] [MUSIC PLAYING]
Info
Channel: Android Developers
Views: 57,992
Rating: 4.8449612 out of 5
Keywords: type: Conference Talk (Full production);, pr_pr: Android, purpose: Educate
Id: dtm2h-_sNDQ
Channel Id: undefined
Length: 41min 2sec (2462 seconds)
Published: Wed Oct 23 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.