Installed --> Instant
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Weiss, Ben
Android Instant Apps has been available to developers since Google I/O 2017. A lot has happened in the meantime. The amount of devices ready for Android Instant Apps is growing steadily, currently at more than 800 million activated devices, as is amount of available instant apps. Code samples have been published and conference sessions were held. Questions on forums were asked and answered. Bugs were raised and triaged. This session doesn’t cover anything of the above, but will show my approach when migrating the Topeka sample to an instant app with multiple, independent feature modules and what I learned. Topeka is a non-trivial sample application with custom sign in, animations, transitions and several Activities and Fragments. I’ll talk about the steps I took and explain the rationale behind them. This will enable you to experiment with Android Instant Apps until you feel your instant app is ready for deployment to production. If you're experimenting with instant apps, you'll have enough to get started and to make a demo for your team. If you're committed to making an instant app, you'll learn how to reduce friction during the refactoring while still maintaining a codebase you can ship from.
 Hello, it's great to be back in Berlin and see so many familiar faces and also so many new faces. Yeah, I'm very happy that I'm here today because I grew with this community and I consider it part of family to be here and it's always an honour to be here. Today I will talk about a little bit what I did with a sample I wrote and migrated that to an InstaNAP and also taken a couple of steps beyond that for another thing that I'm currently working on. So this is why it's not just install to InstaNAP but also beyond that. And it's not actually retrospective, it's kind of catchy title but mainly what it is is a story from what I learned. So firstly there was a rebranding from InstaNAP or Android InstaNAP to Google Play InstaNAP which is a lot better for users because they don't really care about the like InstaNAPs but it's part of Google Play. That's why the name that you will see in documentation is Google Play InstaNAP. But underneath the hood for developers, the plugin is going to be InstaNAP and it's still going to be InstaNAP so it's not going away, it's just been renamed. One of the things that was with discovering apps is a bit tricky. So it's just the background on why we made it and what we made with that. Like discovering apps has always been tricky and finding the right app and it involves a lot of searching and then, well, you first have to find the app and then you have to download the app and then you have to use it. As you can see there's a funnel so you lose users on the way, on that funnel, on the process between they actually found something they want to use and they actually use it in the end and have it installed on their device. By now device base is not a big issue for many but still for some. So if the app is big and bloated, well, that's probably going to be, retention is going to be a little bit lower for the app and would be for a smaller app. So that's one of the things that we're going to do with InstaNAP as well. Well, let's see what it does and how it actually can work. So basically I searched for the New York Times crossword and down here you can see on the search result it says it has an InstaNAP. And when I tap on the search result, what happens is the app opens immediately. I don't have to download it, I don't have to be downloaded, I don't have to install it, I don't have to go through the Play Store and actively decide I want to download and install this app but it can launch immediately and you can go to today's crossword straight away from that link. For the search result. There could be any link actually. It's available to more than a billion of devices and obviously we're expanding that more and more and it's getting more devices as we speak. Let's take a look at the couple of the requirements for development. Basically you have to target Lollipop or above, well, that's already quite a lot of devices because the platform changes that are required were introduced with Lollipop. So before that InstaNAP won't work. That doesn't mean that you have to go for Min SDK Lollipop for all of your app but basically for targeting, at least targeting it to be Lollipop for your InstaNAP. Android Studio 3.0 has been out for a while. It came with all the new features that we needed so it came with the SDK, it came with the Gradle plugins, the InstaNAP plugin and the feature plugin which are being used for developing an InstaNAP. Also came with the AppLinks Assistant which basically gives you an easy way to create a file that you can upload to your server and the Play Store can then use that file to check whether the fingerprint that is in that file matches the fingerprint of your certificate and then can download the app. So not anybody can claim any URL but you have to have ownership of the URL and this is the tool that you can use to easily create the file that you need to create this correlation. Also the emulator supports it so all the emulator with Google Play Services supports Android InstaNAP as well. And there's a refactoring tool which we're still heavily developing and for Kotlin we're working on that. And that's why I'm not going into too much depth of the refactoring tool but I will go to the manual processes and the thought processes that you have to go through and what usually should go in which place. Also one thing for InstaNAPs is that the downloadable bundle size has to be less than four megabytes. That means your base module and any given feature module that is on top of that is considered a downloadable bundle so you have a base module of two megabytes and another module on top of that which has one megabyte. That would mean three megabytes is fine. If you have one that is five megabytes and one that is four, it doesn't work. Well it would work but the policy says that you have to go for less than four megabytes. This is only relevant for production so while you're developing that doesn't really matter but once you want to go into production builds and want to have this shipped to your users you have to go for a low file size. I will show you a couple of tricks and tweaks that you can easily achieve that with without going into too much trouble with just stripping out all the things that you don't actually want to have in your app. So there is no need to limit yourself on functionality rather splitting it up into different pieces. So let's take a look at what I did. I migrated a real app. So Topeka is a sample that I have been working on for a while. It was my starter project when I joined Google and it is still a thing that I have been working on over time. What is it? It's a fun to play quiz that is available on GitHub. You can check it out. You can see the code that I will show you today all on GitHub already. And the UI is basically three pieces. First is a sign-in screen. Second is the categories that you can select. And the third is the actual quiz itself. That's what we're going to end up with in features as well. So firstly, it was a monolith and we're going to split up the app into those three different feature modules. Like I said, I've been working on it for a while. So I started 2014, then published it to GitHub in May 15, then added transitions API, added the lower diminished level to 16 from 21, and then down to 14. So it's available on virtually all Android devices. Then in last year, I published a version of that in Kotlin. So it's now completely rewritten in Kotlin. So you can take a look at that as well. And now it's available as an instant app. But how did I do that? That's what we're going to focus on. Firstly, I went to the documentation. Well, I read up on it and I saw what's going on, what are the limitations, what are the benefits, what do I have to do. A couple of the things I already talked about. And then I came up with a list. That list basically says, well, there's manual login in the app. Can't do that. So I have to take care of this. Also the app is not URL addressable. I don't have any URLs associated with my activities and with my entry points. Well, that wasn't supported by the time. By now there's an API that you can use to change that. But URL addressability generally is a good thing if you have the possibility to make your app addressable via URLs, makes it a lot easier in the long run to work with motorized features. Also the size was more than four megabytes, so that was not cool. It didn't work, at least for productions. Then I got cracking and started working and I just looked at what do I have to do and started diving in. Firstly, I looked at the smart lock API, which is the preferred way of logging your users in. I don't know if you've been using, for example, Netflix. If you signed in once on an Android device, it stores the credentials and then later on you can retrieve them. Obviously, it's an opt-in process for a user, but after that, when I install Netflix on another device, I don't have to sign in again. I just open it and it's like, oh, you've been signed in. It's quite a cool feature and it reduces the friction for your users to actually go through the process of remembering the password and typing everything. Still has been a good benefit to that as well, but this is even less friction. So it automatically signs in users like it says it's less friction and it's a lot faster to access the content. What you want to do is we want to be able to save the credentials the first time, then we want to later on be able to retrieve them and also we have to hook into the activity result because that's an external service from the app point of view and you have to get the result back whether everything was successful and you have to work with the credentials. So that's what we're going to do within the app as well. Firstly, within Kotlin, I created extension functions for the API client to just give me the API client. We have the two methods that are relevant, requesting and saving the credentials. And I also wrote a method function that allows me to intercept the or apply stuff to the activity result. Basically, I pass in two methods, the success and failure method and the success method basically gives me back the user and I can work with that, the failure method does actual failure handling as in there's no sign in data ready so you have to first store it and that's a whole different flow. And with that, I don't have to do that with an activity but all I have to do within the activity is implement the success method, pass it in and the failure method, pass it in. And just override the activity result passing that in. From the developer's point of view, the two methods that are actually relevant is requesting a login where the method gets passed in transparently, works its way through and also you can log out. So the API surface that I had to add is fairly small and again, extension functions can be quite handy with that. And I'm quite happy that we can do that with Kotlin this way. So first step done, cool, let's move on to the next one. That would be URL addressability. Well there's no website behind Swapika. There is a different quiz which does similar things but I haven't gotten down to converging to them so I just took a different URL. But basically what we're going to do is in this case, like I showed you before, behind that search result is a URL. And behind anything that can be an incident is a URL. And that URL just can come from basically anywhere, can come from within the system. Well there's one exception which means your default browser that will usually not link out to an incident but if you type in a URL in your browser, it will stay there and it will not try and navigate you out of there just for the sake of showing you the incident instead. So what I did is I changed the intent filters. The first important thing is to set this to auto-verify true so the Google Play Store can actually verify the URLs and the content in there. And then I add the data for the host and the path for that and schemes have to be both HTTP, HTTPS. There's multiple ways of declaring this. This is the one I found the least redundant so that's my preferred way of declaring these and they do that for every entry point. You don't have to do that for every activity or every service but for every part of the app that you want to be able to link directly into. Also since in the future this will be modularized, we won't be able to access the class explicitly anymore so that doesn't work. So intents have to be called a little bit differently. So we're passing the action view, we're adding a URL that we created before, also we set the category to default and browser and sometimes because we've got URLs and apps listen to URLs. So you get this dialog and in order to avoid that all we have to do is tell this intent that it is designated for the package that we're going to, that we want to have this in. If you do a modularization process, make sure that you actually pass in the package that it's designated to. So if my quiz activity would be in a sub-package called quiz, it has to be in the sub-package called quiz. So app is now URL addressable. Cool. Straight forward with a couple of nice easy features that have been there for a while. So that's nothing too new but next step is going to be a bit trickier. The size is more than four megabytes. So firstly I'm going to introduce the gradle plugins that I'm going to use for modularizing. Basically I guess every Android developer in here, who's an Android developer in here, by the way? Who's not? Okay. That is, yeah. Just briefly, this is the gradle plugin that you apply to your application. This is the gradle plugin that we will apply to an instant app. The instant app module, the gradle module will not host any code because that will simply be ignored so all the code has to be in different modules. And there is the feature plugin. A feature plugin is basically an iteration on the library plugin. So for an application it will create an AAR and that AAR will then be compiled into the application itself. For the instant app plugin it will create an APK which then will be packaged into the resulting file. So from that firstly existing app module I just moved that all to base. I just renamed it. And on top of that I created an install module and an instant module. Base is using the feature plugin and for, yeah, base is using the feature plugin and installed is the APK. So it uses the application plugin. Instant is the AAR, is the instant app module which then gets the AAR. And obviously you have to set those in settings gradle. Otherwise gradle won't know that they're there and you will have to add them anyway. But with that I didn't do anything for a size. It is the prerequisite that I have to do to just get around playing around with instant app. The cool thing is at this point if you don't want to ship it to users but work with it internally and even upload it to the Play Store to the development track, as long as your app is smaller than 20 megabytes you can do that already. So you can play around with it. You can already give that to your team, to your product owners and show them what you could do. And this is already your minimal viable product for working with an instant app and showcasing it. But this is not where we want to stop. So we want to go further. We want to see what we can do with feature modules and how we can go into the modularization bit. Like I said earlier, there's a couple of modules we're going to cut out. We've got the base module already and from that we're going to extract the categories module as well as the quiz module. So those can be downloaded later on separately. And all of those get bundled into the installed and instant modules later on. This was the point where I had to look into a lot of spaghetti code. The cool thing is after you're done with that, your architecture will be cleaner and your code will be cleaner because you can't have dependencies between those that are hard and you won't have any cyclic dependencies. And definitely after a modularizing app, it's going to be a cleaner code base to work with. On that note, if you don't want to go straight away into modularization but general refactoring, it makes sense to have your package hierarchy more feature-wise than layer-wise. And within those features, you can go wild with whatever you want to have, but it makes it easier to extract those modules and packages already into separate modules. So I've been shuffling stuff around. I basically took all the shared code, had that in base. So that means dependencies that I will expose with the API keyword. So many of you will be using the implementation keyword for Gradle. If you want to expose your dependencies to other modules, you just flip from implementation to API so you don't have to bundle them in every single package but just bundle them in the base. All the utilities are in there. So everything that's shared within utility classes and functions is in the base modules so other modules can access it as well. Also the shared model classes, the whole data layer is in there. I've seen different approaches for different apps which is fine as well. So if your app makes sense for your app to have different repositories and also different databases in different modules, fine. That's like this is not the one-size-fits-all approach. It's the approach that I took and I found worked really well for me and for those apps. Also there's going to be shared resources in there which means the application icon which means strings that are being reused throughout the app which means drawables and images that are being reused throughout the app. So just the stuff that's shared in there. So also colors and tithemes and styles. For some of your styles, you could just have a blank implementation in the base module which basically just means give it a name and the actual implementation is later on within the actual feature module. So you could do that to just keep it separate and clean on that level as well. All the drawables that are shared in there as well as strings and transitions that go from the base module to the feature modules. If there's a feature module to feature module transition, you can have that in the beginning or receiving feature module but you always have to be very aware of where your stuff is being called from because otherwise stuff won't work. Sometimes the system just doesn't display things as in the framework, smart enough to not have you have crash but if you have resources that have been moved around in package names as well, then you might just find that some things are not being displayed. And that has to do with those package names. One of the cool things about Kotlin again is that you can enable import LIS things so you don't have to have the complete package name every time you use the import. This is my R-base package name that I'm using so it's the R file of the base and I just used a drawable from this instead of from another one. So it happened to me, I've been bitten by it a couple of times that resources were there but well in the different package. Also while you're modernizing, Gradle caches are not your best of friends because they're caching the pre-built things and they will tell you stuff is there already. Well it's true because it's in the cache but actually it's been moved so you might find yourself in a bit of a weird situation sometimes with that. So make sure to disable those Gradle caches for the time you're modernizing if you want to save yourself a little bit of headache. So let's take a look at the other feature modules. I've been talking about a couple of things already but all the controllers are in there so all my activities, all my fragments, all my other things that are basically part of my controllers. Also the custom use that I wrote and the UIs and also the tests live, it makes sense to have the tests live within those feature modules as well and not have them in the base module but you can run each because basically essentially everything, the feature plugin allows you to create APKs as well so those can be installed on the device and you have a small test suite so you can parallelize your tests easier and have them run in this modularized fashion as well. Also each feature module has its separate Android manifest with the activity declared in it to have that running as well. Drawables and assets that are within that module have to be in those modules as well so we don't have, you don't load the base module but keep that lean as lean as possible and have the big files only in those feature modules. Yeah, and that's the package name changes. You saw I did one for base, I did one for categories and one for quizzes so I can easily see where stuff is coming from and how it actually works. But at this point still, the file size isn't any smaller, it's just distributed. We've got separate files but those files are still too big. That is where the configuration API comes in. Anybody has been dealing with multi-APKs? Okay, then just forget multi-APKs for this reason and for another reason that I'm going to show you in a moment. So there's the configuration API allows you to split the APK that's being created by screen density, user language or the processor architecture. So your native code can be quite substantial every now and then but probably for most apps the screen density, so the resources, the drawables and all that, that are specific to screen density are there but they're not being used. So, well, if we split that up, what happens is in your apps so far you have all of those but actually you just use one of them. With the configuration APKs API, what happens is there's different APKs for each screen density. And for a triple X, what happens is a base APK will be installed which is devoid of resources as well as this other APK with the triple X, APK will be installed. So no need to fumble with multi-APKs but on the other hand you can easily just have this with a very straightforward set of gradle roles in there. So instead of having those three APKs, there is loads and loads and loads of APKs that are being produced. All of them are being bundled in the InstaNAP zip file and can be uploaded to the Play Store as is and the Play Store decides what to ship to the user. I've heard a question before which is, oh, and what happens if the language changes, if the user changes language and I've got the German one installed and changes to English, already changed to English, what happens is that the Play Store tries and downloads that APK as soon as possible and as soon as it's ready and available, it will switch the resources over to your new language. That might require an application restart but it depends on the operating system level as well as on the way you integrate all that and how fast it is. That was being done transparently and in the background. So with that, we went under the file sales limit. So that's quite handy and I'm very happy that we went there with fairly little effort. It took a while but comparing it to not being able to use InstaNAPs on the one hand and on the other hand, not being able to have a clean architecture, this is completely fair enough and the tools help you a lot with that as well. So another thing is ProGuard. Who in here obfuscates their app or uses minification of ProGuard? Wow. Okay. One thing you have to keep in mind is with that, you have become a library developer and you expose APIs from one module to the other. So make sure that your API services are there because if they're not, if you obfuscate your classes and your methods that you're calling from a different module, the module doesn't know that it should work and you will get crashes at runtime. So you will have to have all the things in base, you have to have all your API services in there have to be kept by a great rules. By ProGuard rules. One of the cool things is you can use the APK inspector and just go in there and if you see a class that you want to keep, you can right click on it and say generate ProGuard rules and generate key rules and you can select a couple of kept rules and you can copy paste that into your ProGuard configuration file so you don't have to think, was it keep which package which is one or two S-Rx, how does this, it just generates it automatically for you. All you have to do is copy paste it. There's a good article as well from my colleague Wojtek on enabling ProGuard in an Android InstaNap. That was the first part of this presentation. So the takeaways so far are firstly make sure that you know what your prerequisites are. So it could be the URLs that you have to introduce, it could be smart log, there's a couple of APIs that are restricted to make sure that an InstaNap which is transparently downloaded and run on the user's device doesn't do any shenanigans, same with a couple of permissions that have been locked down. So for example, you can't access content providers that are not explicitly being exposed to an Android InstaNap. Also your content providers that you expose, well, they won't be accessible by local apps either. Also refactor one feature at a time. Don't try and do everything at once, just take the first feature that you think makes sense and put everything in a base module and then just take it step by step. Because if you go for everything at once, you will have an app that you can't ship anymore and that is the thing where usually you should have, like, that's a creeping feeling on the back of your neck when somebody says, oh, we have to ship. Yeah, we can ship in two weeks' time and all the users will see bad things in the meantime or new features are being shipped. So that would be really bad. So take one feature, refactor that and make sure that your app still works. Also you have to apply those new plugins and adhere to the size constraint of the Android InstaNap. The next steps would be linking it to an actual domain using app links, also uploading it to a Play Store and maybe working with Android App Bundle and Dynamic Delivery, which is a cool new thing that we introduced at IOTUS here, who here already uploads an app bundle to the Play Store. Okay. All right. I will show you why you should. We've been working on an app called Plaid. My colleague Nick Butcher has been the main contributor to it and he started the whole thing and we are currently rewriting it. There's a massive effort. So one of the parts is we are modularizing it, which I did just recently, so that's why it's not in the abstract and in the title of the presentation here, but not in the one under program. So we went under this whole massive refactoring. We're going to rewrite it. Basically it's going to be a complete massive overall of the app. What it does, it also showcases material design, transitions, animations in a real scenario, so it has external new sources that it pulls in data from. You can comment on stuff. You can, there's logins. There's like multiple things that are making it a way more real world scenario than Topeka, which is a local only app, so there is also data that comes from the Internet. And again, if you haven't seen it before, it's a beautiful app, play around with it, and it's all open source and GitHub, so you can just get it from there. One of the first things we did is use Android App Bundle. Android App Bundle is the new packaging format for Android apps, which we introduced and announced at IO 2018, so you can upload your app already in an app bundle. And we'll give you a couple of benefits. So I'll talk about the benefits later in this presentation. So again, getting started is you go to the documentation, and it's G.co. Android App Bundle, and from there you go towards the next step. What does App Bundle do? It's fairly similar to the configuration API, but with even less effort. So you've got one master APK, which is all your code that is not shared between different densities and languages or CPU architectures or the density as well. And that will be generated, and there's a multitude of APKs that you will end up with after creating an Android App Bundle, and for a triple XHTPI device, it only installs these three modules, these four modules, sorry, the master is there as well. And for just this change to build an app bundle, which is an actually you can do within Android Studio or even within Gradle directly, you can just say Gradle wrapper bundle, and you will get a bundle. We had an APK size of 8.6 megabytes, and for the same phone, so for a triple XHTPI device, we went down to 4.2 megabytes. So that's less than 50% of the starting size. And all I have to do is generate an app bundle instead of an APK. So that's big. Not anymore, but... But we don't just utilize app bundles. There's a couple of Gradle plugins that we use. There's new Gradle plugins as well. So there's the application plugin, there's a library plugin that we use for that, and there's a dynamic feature plugin, which basically is another feature for dynamic features we will talk about in a minute. And with these three plugins that we used within the modularization, we created good ways to have an unbundled architecture of the application. So what do we do? There's obviously the core of the app, which was the base module within Topeka, so I kept the same name. It's the base of the app. And it uses the application plugin for the stuff that was in app before, the home activity, and a few resources that are around that, which I already said earlier. I'm not going into too much detail here. Also there's a library plugin for a couple of things that are fairly close to the application, but could be unbundled in order to decrease build times. And if there is no changes in there, the Gradle doesn't have to rebuild everything every time. So everything in base is shared code and resources. It's a library plugin, so it can be reused throughout different feature modules later on. Also there is the external dependencies that all reside in base. So again, the API keyword instead of the implementation keyword. And there's search. Firstly, I had search in a dynamic feature module, but since search is one of those things that is transparently overlaid over the home activity, and there's a transition going on between that, we couldn't completely unbundle it. Because of that, we just put it into a library for now. We might completely unbundle it into a dynamic feature module, but currently that didn't work because there's still a couple of hard dependencies, and we wanted to get the first bits of the modularization out of the door as quickly as possible. So library plugins always is a good choice if you can't fully unbundle everything and you want to have dependencies that are included into a bigger APK later on. And there's the feature modules. Dynamic features is one of the cool things that we also introduced. It's currently in beta, so you can't really ship to your users with that yet, but you can play around with it locally. And what it does is it allows you to do dynamic code loading. So there is an official way to just call an API and tell the API, give me the new feature. And it downloads it, installs it, and then you can run it straight away. The user's dynamic feature plugin, all the separable activities are in there, and the resource is local to that. So in this case, it's three new sources, designer news, dribble, and the about screen. Well, about screen, why? Mainly because we could. And you can still, in the end, decide you want to bundle this within the initial application in the first place. But we were working on gathering data, how many people actually click on it. So features that are not being used by all of your users, but by a subset of them, be it expert users, or just a part of users that are maybe paying for parts of your service, you can put those in feature modules and download them later on. So not every user has to get them in the first place. And that's one of the reasons why we put all of those out there. And maybe if we add new sources in the future, we can easily, because we already have the API surface is quite clear, we can easily have new modules that we add on top of that. And instead of designer news, dribble, you could add material up or whatever to that as well and fairly quickly integrate it within the app. And keep it clean and encapsulated as well and download it if the user enabled those new sources on the app. So how does it look in a more graphical way? We've got the app, which uses the application plugin. We've got the three libraries, which is shared search and bypass, which is a native library, which, well, because of app bundles, like I said earlier, is being split up into different CPU architectures. You don't have to have all the CPU architecture as O files into one single APK, but you only get the one that you actually need. And on the other hand, there's the three dynamic features. The way that they depend on one another is everything, the app depends on the three library plugins. And within the app, there's also a declaration of the dynamic features. And the dynamic features also declare that they're dependent on the application. Well, that's not a hard dependency as in a compile dependency, but it's more, you can use the resources once you've done that. And you can work with stuff that is in base, in the base modules or in the app module, and you can use those straight away from there. I said earlier that you don't have to have URLs for instance apps to work anymore. That's the same thing, the same logic behind that and the same mechanism behind it that we use for dynamic delivery. That's the Google Play Core API. That's an API that enables you to get modules later on in your install cycle. So you can request modules and delete them and you can do that immediately or deferred. So you can say I want to download this now because the user clicked on something and I actually have to show a blocking action to say we're downloading this. Or on the other hand, you can download features deferred. So you can say, well, I'm pretty sure this user wants to get this at some point, so I'm just downloading it. So for example, you've got a game and with that game, you have the first 10 levels into the base module and it's already installed and you can add stuff later on and you can download that later on. Or even resources or assets that you don't want to download in the first place, you can just have them as dynamic features and download them later on within the process. How does it work? You've got the split install manager factory, you created it for an activity that you're running in. And then with an install method, what I did is I just checked if the module is already installed, if it's already installed, then I call unsuccessful load, which is my callback to just say this is done already, we can continue. And if not, what I do is I create a builder, add the module and build and then call the manager to start my installation request. So it's a fairly straightforward API as well and we can just go ahead from there. And within my unsuccessful load is what I do is I just call set class name for the package and class name. Again, this could be with URLs, it could just be the URL. Without URLs, I can't explicitly use the class that we had in the first place because, well, the base module doesn't know that it's there. And we start the activity. Straight forward Android development as you're already used to with a couple of benefits, which means dynamic code loading straight out of the box without. So like I said, to recap that, you can upload the Android app bundle already today. You can just build it, ship it. And your users will get smaller APKs, which are optimized for your device. You can use the bundle tool locally, which is a tool that's available on GitHub, which to build and deploy your bundles to your local app, to your devices locally in order to test all those configurations that you could have. So you cannot do everything locally already. And you can introduce dynamic delivery, which allows you to, well, be even more fine grained with that and allows users to only have stuff on the device that they actually need and that they're actually using. And you can install or delete modules as needed. With that, that's the second step of takeaways and I promise this will be the last slide on that. You can easily release the app bundle today. And if you don't want to, please talk to me later on and I'm happy to hear what's going on and why it doesn't work for your use case. Also, faster delivery makes your users happier because it makes the app a lot more usable and faster usable. And map out your plan before you start modularizing. Don't just jump in and modularize everything, but make sure that you kind of have a plan of where you're going. You can add dynamic delivery at any point. You can use a dynamic feature module in the first place and later on add dynamic delivery so you don't have to do that all in one go. But you have modules already and can easily switch from either a library module or a dynamic feature module to the, to also adding the Play Core API. And again, work with one feature at a time. Don't do everything at once. There's a couple of resources. The source code for both of the samples is available on GitHub. There's more samples for Android Instant Apps and for dynamic features. You can check them out. It's got all the codes that I showed in there. There's the blog post and there's documentation for Instant Apps and a bundle. Thank you very much. I don't know if we have time for questions. If not, I'm here. Okay. If you have a question, other than can I have a sticker, I've got stickers for questions. Ooh. Can I get a sticker? Yes, but not for this question and later on. Just find me later. So can I shift to an Android bundle? I release the Android bundle. There you are. So I release the Android bundle then, but I want to release the non-Android bundle APK. That is possible. Yeah. Okay. It's not a one-way road and you can upload both. Underneath the hood, what happens is it installs a multitude of APKs instead of one APK. So going back, I don't see why it would make sense other than you're experiencing issues. And if you're experiencing issues, please let me know. That's the only reason. If there is an issue and I want to revert and... Okay. Yeah. All right. Thanks. Hi. Hi. So my question is about the single activity pattern. Yeah. How do you approach that or are you going to approach that topic in the plaid or in the topic? There is not one activity within plaid and there's multiple of them. It makes sense. Sorry. Just first the questions and I'm going to give it more secrets later on. So it's not one activity within plaid at the moment and we don't plan to make it just one activity. The navigation component and the single activity pattern that is recommended in there works for several apps. It doesn't work for all apps and you don't... You should not refactor your application to have one single activity just for the sake of it. If it works for you, it works. But on the other hand, the navigation component gives you ways to easily link between activities as well. And I've got a sample currently under review which is just addressing that. How can you do multiple features, multiple modules with different activities and how can those be addressed? Okay. Thank you. Welcome. Hi. So, you're going to be talking about dynamic feature gradle plugin and how it correlates with the actual instant app feature plugin. Do they support each other or not? Yes. If you take a look at the documentation, it already says that coming soon we're going to converge those and it's going to be one thing that you can both... If you use dynamic delivery, you can already also have the instant app benefits. And one of the samples that is out there already... Right now, if I have an instant app, for instance, right now, can I just move to the app bundles or not? You can create an app bundle, yes. But dynamic delivery, you can't do that straight away. What you would have to do is you would have to take all the code that's in the feature modules and take that a level lower to a library and have dynamic feature and a feature plugin. I know it's a bit more work right now, but like I said, it's going to be converging in the future. So, we have some plans to... You have some plans to make it easier. Yes. We're going to converge and that's the plan. Yeah, cool. Thank you. Hey, do you have any data on app sizes and download rate or uninstall rate? Like I'm a developer and I want to convince my product manager that this is a thing we should do. Is there any data around that? Well, there definitely is data. I don't know what we have shared publicly on that, but as you saw, for our sample app, it decreased the file size for the largest configuration possible. We now have less than 50% of the file size that we started off with. So, even just for that, it's absolutely worth it. And... Whoa! Yeah, so it definitely decreases the file size by a lot and the data is... I'd rather not say that because I'm not 100% sure that this is right, but there is a strong correlation between file size and people not downloading the app or not going to keep it on their device. So, smaller apps basically means that your users will keep the app longer on their device as well as download is going to be more likely.
 Hello, it's great to be back in Berlin and see so many familiar faces and also so many  new faces.  Yeah, I'm very happy that I'm here today because I grew with this community and I consider  it part of family to be here and it's always an honour to be here.  Today I will talk about a little bit what I did with a sample I wrote and migrated that  to an InstaNAP and also taken a couple of steps beyond that for another thing that I'm  currently working on.  So this is why it's not just install to InstaNAP but also beyond that.  And it's not actually retrospective, it's kind of catchy title but mainly what it is  is a story from what I learned.  So firstly there was a rebranding from InstaNAP or Android InstaNAP to Google Play InstaNAP  which is a lot better for users because they don't really care about the like InstaNAPs  but it's part of Google Play.  That's why the name that you will see in documentation is Google Play InstaNAP.  But underneath the hood for developers, the plugin is going to be InstaNAP and it's still  going to be InstaNAP so it's not going away, it's just been renamed.  One of the things that was with discovering apps is a bit tricky.  So it's just the background on why we made it and what we made with that.  Like discovering apps has always been tricky and finding the right app and it involves  a lot of searching and then, well, you first have to find the app and then you have to  download the app and then you have to use it.  As you can see there's a funnel so you lose users on the way, on that funnel, on the process  between they actually found something they want to use and they actually use it in the  end and have it installed on their device.  By now device base is not a big issue for many but still for some.  So if the app is big and bloated, well, that's probably going to be, retention is going to  be a little bit lower for the app and would be for a smaller app.  So that's one of the things that we're going to do with InstaNAP as well.  Well, let's see what it does and how it actually can work.  So basically I searched for the New York Times crossword and down here you can see on the  search result it says it has an InstaNAP.  And when I tap on the search result, what happens is the app opens immediately.  I don't have to download it, I don't have to be downloaded, I don't have to install it,  I don't have to go through the Play Store and actively decide I want to download and install  this app but it can launch immediately and you can go to today's crossword straight away  from that link.  For the search result.  There could be any link actually.  It's available to more than a billion of devices and obviously we're expanding that more and  more and it's getting more devices as we speak.  Let's take a look at the couple of the requirements for development.  Basically you have to target Lollipop or above, well, that's already quite a lot of devices  because the platform changes that are required were introduced with Lollipop.  So before that InstaNAP won't work.  That doesn't mean that you have to go for Min SDK Lollipop for all of your app but basically  for targeting, at least targeting it to be Lollipop for your InstaNAP.  Android Studio 3.0 has been out for a while.  It came with all the new features that we needed so it came with the SDK, it came with  the Gradle plugins, the InstaNAP plugin and the feature plugin which are being used for  developing an InstaNAP.  Also came with the AppLinks Assistant which basically gives you an easy way to create  a file that you can upload to your server and the Play Store can then use that file to check  whether the fingerprint that is in that file matches the fingerprint of your certificate  and then can download the app.  So not anybody can claim any URL but you have to have ownership of the URL and this is the  tool that you can use to easily create the file that you need to create this correlation.  Also the emulator supports it so all the emulator with Google Play Services supports Android  InstaNAP as well.  And there's a refactoring tool which we're still heavily developing and for Kotlin we're  working on that.  And that's why I'm not going into too much depth of the refactoring tool but I will go  to the manual processes and the thought processes that you have to go through and what usually  should go in which place.  Also one thing for InstaNAPs is that the downloadable bundle size has to be less than four megabytes.  That means your base module and any given feature module that is on top of that is considered  a downloadable bundle so you have a base module of two megabytes and another module on top  of that which has one megabyte.  That would mean three megabytes is fine.  If you have one that is five megabytes and one that is four, it doesn't work.  Well it would work but the policy says that you have to go for less than four megabytes.  This is only relevant for production so while you're developing that doesn't really matter  but once you want to go into production builds and want to have this shipped to your users  you have to go for a low file size.  I will show you a couple of tricks and tweaks that you can easily achieve that with without  going into too much trouble with just stripping out all the things that you don't actually  want to have in your app.  So there is no need to limit yourself on functionality rather splitting it up into different pieces.  So let's take a look at what I did.  I migrated a real app.  So Topeka is a sample that I have been working on for a while.  It was my starter project when I joined Google and it is still a thing that I have been working  on over time.  What is it?  It's a fun to play quiz that is available on GitHub.  You can check it out.  You can see the code that I will show you today all on GitHub already.  And the UI is basically three pieces.  First is a sign-in screen.  Second is the categories that you can select.  And the third is the actual quiz itself.  That's what we're going to end up with in features as well.  So firstly, it was a monolith and we're going to split up the app into those three different  feature modules.  Like I said, I've been working on it for a while.  So I started 2014, then published it to GitHub in May 15, then added transitions API, added  the lower diminished level to 16 from 21, and then down to 14.  So it's available on virtually all Android devices.  Then in last year, I published a version of that in Kotlin.  So it's now completely rewritten in Kotlin.  So you can take a look at that as well.  And now it's available as an instant app.  But how did I do that?  That's what we're going to focus on.  Firstly, I went to the documentation.  Well, I read up on it and I saw what's going on, what are the limitations, what are the  benefits, what do I have to do.  A couple of the things I already talked about.  And then I came up with a list.  That list basically says, well, there's manual login in the app.  Can't do that.  So I have to take care of this.  Also the app is not URL addressable.  I don't have any URLs associated with my activities and with my entry points.  Well, that wasn't supported by the time.  By now there's an API that you can use to change that.  But URL addressability generally is a good thing if you have the possibility to make  your app addressable via URLs, makes it a lot easier in the long run to work with motorized  features.  Also the size was more than four megabytes, so that was not cool.  It didn't work, at least for productions.  Then I got cracking and started working and I just looked at what do I have to do and  started diving in.  Firstly, I looked at the smart lock API, which is the preferred way of logging your users  in.  I don't know if you've been using, for example, Netflix.  If you signed in once on an Android device, it stores the credentials and then later on  you can retrieve them.  Obviously, it's an opt-in process for a user, but after that, when I install Netflix on  another device, I don't have to sign in again.  I just open it and it's like, oh, you've been signed in.  It's quite a cool feature and it reduces the friction for your users to actually go through  the process of remembering the password and typing everything.  Still has been a good benefit to that as well, but this is even less friction.  So it automatically signs in users like it says it's less friction and it's a lot faster  to access the content.  What you want to do is we want to be able to save the credentials the first time, then  we want to later on be able to retrieve them and also we have to hook into the activity  result because that's an external service from the app point of view and you have to  get the result back whether everything was successful and you have to work with the credentials.  So that's what we're going to do within the app as well.  Firstly, within Kotlin, I created extension functions for the API client to just give  me the API client.  We have the two methods that are relevant, requesting and saving the credentials.  And I also wrote a method function that allows me to intercept the or apply stuff to the  activity result.  Basically, I pass in two methods, the success and failure method and the success method  basically gives me back the user and I can work with that, the failure method does actual  failure handling as in there's no sign in data ready so you have to first store it and  that's a whole different flow.  And with that, I don't have to do that with an activity but all I have to do within the  activity is implement the success method, pass it in and the failure method, pass it  in.  And just override the activity result passing that in.  From the developer's point of view, the two methods that are actually relevant is requesting  a login where the method gets passed in transparently, works its way through and also you can log  out.  So the API surface that I had to add is fairly small and again, extension functions can be  quite handy with that.  And I'm quite happy that we can do that with Kotlin this way.  So first step done, cool, let's move on to the next one.  That would be URL addressability.  Well there's no website behind Swapika.  There is a different quiz which does similar things but I haven't gotten down to converging  to them so I just took a different URL.  But basically what we're going to do is in this case, like I showed you before, behind  that search result is a URL.  And behind anything that can be an incident is a URL.  And that URL just can come from basically anywhere, can come from within the system.  Well there's one exception which means your default browser that will usually not link  out to an incident but if you type in a URL in your browser, it will stay there and it  will not try and navigate you out of there just for the sake of showing you the incident  instead.  So what I did is I changed the intent filters.  The first important thing is to set this to auto-verify true so the Google Play Store  can actually verify the URLs and the content in there.  And then I add the data for the host and the path for that and schemes have to be both  HTTP, HTTPS.  There's multiple ways of declaring this.  This is the one I found the least redundant so that's my preferred way of declaring these  and they do that for every entry point.  You don't have to do that for every activity or every service but for every part of the  app that you want to be able to link directly into.  Also since in the future this will be modularized, we won't be able to access the class explicitly  anymore so that doesn't work.  So intents have to be called a little bit differently.  So we're passing the action view, we're adding a URL that we created before, also we set the  category to default and browser and sometimes because we've got URLs and apps listen to  URLs.  So you get this dialog and in order to avoid that all we have to do is tell this intent  that it is designated for the package that we're going to, that we want to have this  in.  If you do a modularization process, make sure that you actually pass in the package that  it's designated to.  So if my quiz activity would be in a sub-package called quiz, it has to be in the sub-package  called quiz.  So app is now URL addressable.  Cool.  Straight forward with a couple of nice easy features that have been there for a while.  So that's nothing too new but next step is going to be a bit trickier.  The size is more than four megabytes.  So firstly I'm going to introduce the gradle plugins that I'm going to use for modularizing.  Basically I guess every Android developer in here, who's an Android developer in here,  by the way?  Who's not?  Okay.  That is, yeah.  Just briefly, this is the gradle plugin that you apply to your application.  This is the gradle plugin that we will apply to an instant app.  The instant app module, the gradle module will not host any code because that will simply  be ignored so all the code has to be in different modules.  And there is the feature plugin.  A feature plugin is basically an iteration on the library plugin.  So for an application it will create an AAR and that AAR will then be compiled into the  application itself.  For the instant app plugin it will create an APK which then will be packaged into the  resulting file.  So from that firstly existing app module I just moved that all to base.  I just renamed it.  And on top of that I created an install module and an instant module.  Base is using the feature plugin and for, yeah, base is using the feature plugin and  installed is the APK.  So it uses the application plugin.  Instant is the AAR, is the instant app module which then gets the AAR.  And obviously you have to set those in settings gradle.  Otherwise gradle won't know that they're there and you will have to add them anyway.  But with that I didn't do anything for a size.  It is the prerequisite that I have to do to just get around playing around with instant  app.  The cool thing is at this point if you don't want to ship it to users but work with it internally  and even upload it to the Play Store to the development track, as long as your app is smaller  than 20 megabytes you can do that already.  So you can play around with it.  You can already give that to your team, to your product owners and show them what you  could do.  And this is already your minimal viable product for working with an instant app and showcasing  it.  But this is not where we want to stop.  So we want to go further.  We want to see what we can do with feature modules and how we can go into the modularization  bit.  Like I said earlier, there's a couple of modules we're going to cut out.  We've got the base module already and from that we're going to extract the categories  module as well as the quiz module.  So those can be downloaded later on separately.  And all of those get bundled into the installed and instant modules later on.  This was the point where I had to look into a lot of spaghetti code.  The cool thing is after you're done with that, your architecture will be cleaner and your  code will be cleaner because you can't have dependencies between those that are hard and  you won't have any cyclic dependencies.  And definitely after a modularizing app, it's going to be a cleaner code base to work  with.  On that note, if you don't want to go straight away into modularization but general refactoring,  it makes sense to have your package hierarchy more feature-wise than layer-wise.  And within those features, you can go wild with whatever you want to have, but it makes  it easier to extract those modules and packages already into separate modules.  So I've been shuffling stuff around.  I basically took all the shared code, had that in base.  So that means dependencies that I will expose with the API keyword.  So many of you will be using the implementation keyword for Gradle.  If you want to expose your dependencies to other modules, you just flip from implementation  to API so you don't have to bundle them in every single package but just bundle them  in the base.  All the utilities are in there.  So everything that's shared within utility classes and functions is in the base modules  so other modules can access it as well.  Also the shared model classes, the whole data layer is in there.  I've seen different approaches for different apps which is fine as well.  So if your app makes sense for your app to have different repositories and also different  databases in different modules, fine.  That's like this is not the one-size-fits-all approach.  It's the approach that I took and I found worked really well for me and for those apps.  Also there's going to be shared resources in there which means the application icon  which means strings that are being reused throughout the app which means drawables and  images that are being reused throughout the app.  So just the stuff that's shared in there.  So also colors and tithemes and styles.  For some of your styles, you could just have a blank implementation in the base module  which basically just means give it a name and the actual implementation is later on  within the actual feature module.  So you could do that to just keep it separate and clean on that level as well.  All the drawables that are shared in there as well as strings and transitions that go  from the base module to the feature modules.  If there's a feature module to feature module transition, you can have that in the beginning  or receiving feature module but you always have to be very aware of where your stuff  is being called from because otherwise stuff won't work.  Sometimes the system just doesn't display things as in the framework, smart enough to  not have you have crash but if you have resources that have been moved around in package names  as well, then you might just find that some things are not being displayed.  And that has to do with those package names.  One of the cool things about Kotlin again is that you can enable import LIS things so  you don't have to have the complete package name every time you use the import.  This is my R-base package name that I'm using so it's the R file of the base and I just  used a drawable from this instead of from another one.  So it happened to me, I've been bitten by it a couple of times that resources were there  but well in the different package.  Also while you're modernizing, Gradle caches are not your best of friends because they're  caching the pre-built things and they will tell you stuff is there already.  Well it's true because it's in the cache but actually it's been moved so you might find  yourself in a bit of a weird situation sometimes with that.  So make sure to disable those Gradle caches for the time you're modernizing if you want  to save yourself a little bit of headache.  So let's take a look at the other feature modules.  I've been talking about a couple of things already but all the controllers are in there  so all my activities, all my fragments, all my other things that are basically part of  my controllers.  Also the custom use that I wrote and the UIs and also the tests live, it makes sense to  have the tests live within those feature modules as well and not have them in the base module  but you can run each because basically essentially everything, the feature plugin allows you to  create APKs as well so those can be installed on the device and you have a small test suite  so you can parallelize your tests easier and have them run in this modularized fashion  as well.  Also each feature module has its separate Android manifest with the activity declared  in it to have that running as well.  Drawables and assets that are within that module have to be in those modules as well  so we don't have, you don't load the base module but keep that lean as lean as possible  and have the big files only in those feature modules.  Yeah, and that's the package name changes.  You saw I did one for base, I did one for categories and one for quizzes so I can easily  see where stuff is coming from and how it actually works.  But at this point still, the file size isn't any smaller, it's just distributed.  We've got separate files but those files are still too big.  That is where the configuration API comes in.  Anybody has been dealing with multi-APKs?  Okay, then just forget multi-APKs for this reason and for another reason that I'm going  to show you in a moment.  So there's the configuration API allows you to split the APK that's being created by  screen density, user language or the processor architecture.  So your native code can be quite substantial every now and then but probably for most apps  the screen density, so the resources, the drawables and all that, that are specific to screen  density are there but they're not being used.  So, well, if we split that up, what happens is in your apps so far you have all of those  but actually you just use one of them.  With the configuration APKs API, what happens is there's different APKs for each screen  density.  And for a triple X, what happens is a base APK will be installed which is devoid of resources  as well as this other APK with the triple X, APK will be installed.  So no need to fumble with multi-APKs but on the other hand you can easily just have this  with a very straightforward set of gradle roles in there.  So instead of having those three APKs, there is loads and loads and loads of APKs that  are being produced.  All of them are being bundled in the InstaNAP zip file and can be uploaded to the Play Store  as is and the Play Store decides what to ship to the user.  I've heard a question before which is, oh, and what happens if the language changes,  if the user changes language and I've got the German one installed and changes to English,  already changed to English, what happens is that the Play Store tries and downloads that  APK as soon as possible and as soon as it's ready and available, it will switch the resources  over to your new language.  That might require an application restart but it depends on the operating system level  as well as on the way you integrate all that and how fast it is.  That was being done transparently and in the background.  So with that, we went under the file sales limit.  So that's quite handy and I'm very happy that we went there with fairly little effort.  It took a while but comparing it to not being able to use InstaNAPs on the one hand and  on the other hand, not being able to have a clean architecture, this is completely fair  enough and the tools help you a lot with that as well.  So another thing is ProGuard.  Who in here obfuscates their app or uses minification of ProGuard?  Wow.  Okay.  One thing you have to keep in mind is with that, you have become a library developer and  you expose APIs from one module to the other.  So make sure that your API services are there because if they're not, if you obfuscate your  classes and your methods that you're calling from a different module, the module doesn't  know that it should work and you will get crashes at runtime.  So you will have to have all the things in base, you have to have all your API services  in there have to be kept by a great rules.  By ProGuard rules.  One of the cool things is you can use the APK inspector and just go in there and if  you see a class that you want to keep, you can right click on it and say generate ProGuard  rules and generate key rules and you can select a couple of kept rules and you can copy paste  that into your ProGuard configuration file so you don't have to think, was it keep which  package which is one or two S-Rx, how does this, it just generates it automatically for  you.  All you have to do is copy paste it.  There's a good article as well from my colleague Wojtek on enabling ProGuard in an Android  InstaNap.  That was the first part of this presentation.  So the takeaways so far are firstly make sure that you know what your prerequisites are.  So it could be the URLs that you have to introduce, it could be smart log, there's a couple of  APIs that are restricted to make sure that an InstaNap which is transparently downloaded  and run on the user's device doesn't do any shenanigans, same with a couple of permissions  that have been locked down.  So for example, you can't access content providers that are not explicitly being exposed to an  Android InstaNap.  Also your content providers that you expose, well, they won't be accessible by local apps  either.  Also refactor one feature at a time.  Don't try and do everything at once, just take the first feature that you think makes  sense and put everything in a base module and then just take it step by step.  Because if you go for everything at once, you will have an app that you can't ship anymore  and that is the thing where usually you should have, like, that's a creeping feeling on the  back of your neck when somebody says, oh, we have to ship.  Yeah, we can ship in two weeks' time and all the users will see bad things in the meantime  or new features are being shipped.  So that would be really bad.  So take one feature, refactor that and make sure that your app still works.  Also you have to apply those new plugins and adhere to the size constraint of the Android  InstaNap.  The next steps would be linking it to an actual domain using app links, also uploading it to  a Play Store and maybe working with Android App Bundle and Dynamic Delivery, which is  a cool new thing that we introduced at IOTUS here, who here already uploads an app bundle  to the Play Store.  Okay.  All right.  I will show you why you should.  We've been working on an app called Plaid.  My colleague Nick Butcher has been the main contributor to it and he started the whole  thing and we are currently rewriting it.  There's a massive effort.  So one of the parts is we are modularizing it, which I did just recently, so that's why  it's not in the abstract and in the title of the presentation here, but not in the one  under program.  So we went under this whole massive refactoring.  We're going to rewrite it.  Basically it's going to be a complete massive overall of the app.  What it does, it also showcases material design, transitions, animations in a real scenario,  so it has external new sources that it pulls in data from.  You can comment on stuff.  You can, there's logins.  There's like multiple things that are making it a way more real world scenario than Topeka,  which is a local only app, so there is also data that comes from the Internet.  And again, if you haven't seen it before, it's a beautiful app, play around with it,  and it's all open source and GitHub, so you can just get it from there.  One of the first things we did is use Android App Bundle.  Android App Bundle is the new packaging format for Android apps, which we introduced and  announced at IO 2018, so you can upload your app already in an app bundle.  And we'll give you a couple of benefits.  So I'll talk about the benefits later in this presentation.  So again, getting started is you go to the documentation, and it's G.co.  Android App Bundle, and from there you go towards the next step.  What does App Bundle do?  It's fairly similar to the configuration API, but with even less effort.  So you've got one master APK, which is all your code that is not shared between different  densities and languages or CPU architectures or the density as well.  And that will be generated, and there's a multitude of APKs that you will end up with  after creating an Android App Bundle, and for a triple XHTPI device, it only installs  these three modules, these four modules, sorry, the master is there as well.  And for just this change to build an app bundle, which is an actually you can do within Android  Studio or even within Gradle directly, you can just say Gradle wrapper bundle, and you  will get a bundle.  We had an APK size of 8.6 megabytes, and for the same phone, so for a triple XHTPI  device, we went down to 4.2 megabytes.  So that's less than 50% of the starting size.  And all I have to do is generate an app bundle instead of an APK.  So that's big.  Not anymore, but...  But we don't just utilize app bundles.  There's a couple of Gradle plugins that we use.  There's new Gradle plugins as well.  So there's the application plugin, there's a library plugin that we use for that, and  there's a dynamic feature plugin, which basically is another feature for dynamic features we  will talk about in a minute.  And with these three plugins that we used within the modularization, we created good  ways to have an unbundled architecture of the application.  So what do we do?  There's obviously the core of the app, which was the base module within Topeka, so I kept  the same name.  It's the base of the app.  And it uses the application plugin for the stuff that was in app before, the home activity,  and a few resources that are around that, which I already said earlier.  I'm not going into too much detail here.  Also there's a library plugin for a couple of things that are fairly close to the application,  but could be unbundled in order to decrease build times.  And if there is no changes in there, the Gradle doesn't have to rebuild everything every time.  So everything in base is shared code and resources.  It's a library plugin, so it can be reused throughout different feature modules later  on.  Also there is the external dependencies that all reside in base.  So again, the API keyword instead of the implementation keyword.  And there's search.  Firstly, I had search in a dynamic feature module, but since search is one of those things  that is transparently overlaid over the home activity, and there's a transition going on  between that, we couldn't completely unbundle it.  Because of that, we just put it into a library for now.  We might completely unbundle it into a dynamic feature module, but currently that didn't work  because there's still a couple of hard dependencies, and we wanted to get the first bits of the  modularization out of the door as quickly as possible.  So library plugins always is a good choice if you can't fully unbundle everything and  you want to have dependencies that are included into a bigger APK later on.  And there's the feature modules.  Dynamic features is one of the cool things that we also introduced.  It's currently in beta, so you can't really ship to your users with that yet, but you  can play around with it locally.  And what it does is it allows you to do dynamic code loading.  So there is an official way to just call an API and tell the API, give me the new feature.  And it downloads it, installs it, and then you can run it straight away.  The user's dynamic feature plugin, all the separable activities are in there, and the  resource is local to that.  So in this case, it's three new sources, designer news, dribble, and the about screen.  Well, about screen, why?  Mainly because we could.  And you can still, in the end, decide you want to bundle this within the initial application  in the first place.  But we were working on gathering data, how many people actually click on it.  So features that are not being used by all of your users, but by a subset of them, be  it expert users, or just a part of users that are maybe paying for parts of your service,  you can put those in feature modules and download them later on.  So not every user has to get them in the first place.  And that's one of the reasons why we put all of those out there.  And maybe if we add new sources in the future, we can easily, because we already have the  API surface is quite clear, we can easily have new modules that we add on top of that.  And instead of designer news, dribble, you could add material up or whatever to that  as well and fairly quickly integrate it within the app.  And keep it clean and encapsulated as well and download it if the user enabled those  new sources on the app.  So how does it look in a more graphical way?  We've got the app, which uses the application plugin.  We've got the three libraries, which is shared search and bypass, which is a native library,  which, well, because of app bundles, like I said earlier, is being split up into different  CPU architectures.  You don't have to have all the CPU architecture as O files into one single APK, but you only  get the one that you actually need.  And on the other hand, there's the three dynamic features.  The way that they depend on one another is everything, the app depends on the three library  plugins.  And within the app, there's also a declaration of the dynamic features.  And the dynamic features also declare that they're dependent on the application.  Well, that's not a hard dependency as in a compile dependency, but it's more, you can  use the resources once you've done that.  And you can work with stuff that is in base, in the base modules or in the app module, and  you can use those straight away from there.  I said earlier that you don't have to have URLs for instance apps to work anymore.  That's the same thing, the same logic behind that and the same mechanism behind it that  we use for dynamic delivery.  That's the Google Play Core API.  That's an API that enables you to get modules later on in your install cycle.  So you can request modules and delete them and you can do that immediately or deferred.  So you can say I want to download this now because the user clicked on something and  I actually have to show a blocking action to say we're downloading this.  Or on the other hand, you can download features deferred.  So you can say, well, I'm pretty sure this user wants to get this at some point, so I'm  just downloading it.  So for example, you've got a game and with that game, you have the first 10 levels into  the base module and it's already installed and you can add stuff later on and you can  download that later on.  Or even resources or assets that you don't want to download in the first place, you can  just have them as dynamic features and download them later on within the process.  How does it work?  You've got the split install manager factory, you created it for an activity that you're  running in.  And then with an install method, what I did is I just checked if the module is already  installed, if it's already installed, then I call unsuccessful load, which is my callback  to just say this is done already, we can continue.  And if not, what I do is I create a builder, add the module and build and then call the  manager to start my installation request.  So it's a fairly straightforward API as well and we can just go ahead from there.  And within my unsuccessful load is what I do is I just call set class name for the package  and class name.  Again, this could be with URLs, it could just be the URL.  Without URLs, I can't explicitly use the class that we had in the first place because, well,  the base module doesn't know that it's there.  And we start the activity.  Straight forward Android development as you're already used to with a couple of benefits,  which means dynamic code loading straight out of the box without.  So like I said, to recap that, you can upload the Android app bundle already today.  You can just build it, ship it.  And your users will get smaller APKs, which are optimized for your device.  You can use the bundle tool locally, which is a tool that's available on GitHub, which  to build and deploy your bundles to your local app, to your devices locally in order to test  all those configurations that you could have.  So you cannot do everything locally already.  And you can introduce dynamic delivery, which allows you to, well, be even more fine grained  with that and allows users to only have stuff on the device that they actually need and  that they're actually using.  And you can install or delete modules as needed.  With that, that's the second step of takeaways and I promise this will be the last slide  on that.  You can easily release the app bundle today.  And if you don't want to, please talk to me later on and I'm happy to hear what's going  on and why it doesn't work for your use case.  Also, faster delivery makes your users happier because it makes the app a lot more usable  and faster usable.  And map out your plan before you start modularizing.  Don't just jump in and modularize everything, but make sure that you kind of have a plan  of where you're going.  You can add dynamic delivery at any point.  You can use a dynamic feature module in the first place and later on add dynamic delivery  so you don't have to do that all in one go.  But you have modules already and can easily switch from either a library module or a dynamic  feature module to the, to also adding the Play Core API.  And again, work with one feature at a time.  Don't do everything at once.  There's a couple of resources.  The source code for both of the samples is available on GitHub.  There's more samples for Android Instant Apps and for dynamic features.  You can check them out.  It's got all the codes that I showed in there.  There's the blog post and there's documentation for Instant Apps and a bundle.  Thank you very much.  I don't know if we have time for questions.  If not, I'm here.  Okay.  If you have a question, other than can I have a sticker, I've got stickers for questions.  Ooh.  Can I get a sticker?  Yes, but not for this question and later on.  Just find me later.  So can I shift to an Android bundle?  I release the Android bundle.  There you are.  So I release the Android bundle then, but I want to release the non-Android bundle APK.  That is possible.  Yeah.  Okay.  It's not a one-way road and you can upload both.  Underneath the hood, what happens is it installs a multitude of APKs instead of one APK.  So going back, I don't see why it would make sense other than you're experiencing issues.  And if you're experiencing issues, please let me know.  That's the only reason.  If there is an issue and I want to revert and...  Okay.  Yeah.  All right.  Thanks.  Hi.  Hi.  So my question is about the single activity pattern.  Yeah.  How do you approach that or are you going to approach that topic in the plaid or in the  topic?  There is not one activity within plaid and there's multiple of them.  It makes sense.  Sorry.  Just first the questions and I'm going to give it more secrets later on.  So it's not one activity within plaid at the moment and we don't plan to make it just  one activity.  The navigation component and the single activity pattern that is recommended in there works  for several apps.  It doesn't work for all apps and you don't...  You should not refactor your application to have one single activity just for the sake  of it.  If it works for you, it works.  But on the other hand, the navigation component gives you ways to easily link between activities  as well.  And I've got a sample currently under review which is just addressing that.  How can you do multiple features, multiple modules with different activities and how  can those be addressed?  Okay.  Thank you.  Welcome.  Hi.  So, you're going to be talking about dynamic feature gradle plugin and how it correlates  with the actual instant app feature plugin.  Do they support each other or not?  Yes.  If you take a look at the documentation, it already says that coming soon we're going  to converge those and it's going to be one thing that you can both...  If you use dynamic delivery, you can already also have the instant app benefits.  And one of the samples that is out there already...  Right now, if I have an instant app, for instance, right now, can I just move to the  app bundles or not?  You can create an app bundle, yes.  But dynamic delivery, you can't do that straight away.  What you would have to do is you would have to take all the code that's in the feature  modules and take that a level lower to a library and have dynamic feature and a feature plugin.  I know it's a bit more work right now, but like I said, it's going to be converging in  the future.  So, we have some plans to...  You have some plans to make it easier.  Yes.  We're going to converge and that's the plan.  Yeah, cool.  Thank you.  Hey, do you have any data on app sizes and download rate or uninstall rate?  Like I'm a developer and I want to convince my product manager that this is a thing we  should do.  Is there any data around that?  Well, there definitely is data.  I don't know what we have shared publicly on that, but as you saw, for our sample app,  it decreased the file size for the largest configuration possible.  We now have less than 50% of the file size that we started off with.  So, even just for that, it's absolutely worth it.  And...  Whoa!  Yeah, so it definitely decreases the file size by a lot and the data is...  I'd rather not say that because I'm not 100% sure that this is right, but there is a strong  correlation between file size and people not downloading the app or not going to keep it  on their device.  So, smaller apps basically means that your users will keep the app longer on their device  as well as download is going to be more likely.
_DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_0_755.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_756_1043.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_1044_1691.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_1692_2087.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_2088_3419.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_3420_3947.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_3948_4175.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_4176_4283.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_4284_4871.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_4872_6611.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_6612_6791.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_6792_8123.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_8124_8255.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_8256_8711.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_8712_8843.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_8844_8903.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_8904_9263.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_9264_10031.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_10032_10211.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_10212_10511.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_10512_11459.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_11460_11615.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_11616_13247.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_13248_15131.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_15132_15719.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_15720_16571.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_16572_18875.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_18876_22979.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_22980_24071.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_24072_32963.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_32964_34307.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_34308_39467.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_39468_40331.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40332_40391.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40392_40439.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40440_40487.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40488_40607.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40608_40655.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40656_40703.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40704_40751.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40752_40847.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40848_40907.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40908_40967.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_40968_41027.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_41028_41111.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_41112_41579.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_41580_42059.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_42060_42275.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_42276_42323.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_42324_42719.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_42720_42911.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_42912_43907.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_43908_49487.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_49488_50951.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_50952_52715.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_52716_54419.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_54420_55559.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_55560_57647.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_57648_64439.png _DCBERLIN18_206_Weiss_MIGRATING_FROM_INSTALLED_TO__sOR-Uf8Q0Xw_64440_64451.png