Dave Smith is a developer advocate at Google, focused on IoT and the Android Things platform. Over the past decade he has worked with low power M2M systems and wireless radio links, and also built custom applications and system components to run Android on embedded platforms. He has a passion for finding opportunities to make devices, and developers, smarter.View the profile
About the talk
As an Android Things OEM, you are building system components that integrate with the platform at a deeper level than the typical mobile app and often without any user interaction. How should you structure your code? Should you use activities or services? Should the code be running in the foreground or the background? Do you package all the code into one APK, or split components up into modules? Find answers to all of these questions and more with best practices for building OEM-level apps.
Good afternoon, everybody. Oh, come on. I got nothing. Good afternoon. Everybody. There we go. There we go. Well, thank you so much for spending your time with me today at my name is Dave Smith. I'm a developer advocate here at Google working on the Android things platform and I'm here today to talk to you a little bit about building apps for the Android things platform and how you can be more effective in the apps that you build using the Android SDK. So whether you're new to Android or whether you've been building Android app since the beginning
targeting Android things devices, it has some subtle differences from what you may or may not be used to and working with Android understanding these differences is what will ensure that you can build better apps on the platform before we jump into too much of that. Let me just do a quick overview of what Android things is maybe for the uninitiated. So Android things is a fully managed platform for building connected devices at scale. It's a variant of the Android platform is optimized for use and embedded devices. It enables you to build apps for embedded in iot using the same Android SDK
and Google Play services that you use to build for the Android mobile UK. Stop apps using the same tools such as Android Studio to deploy and debug your apps to devices as well. Includes the Android things developer console. This is a place where you can securely manage your software stability and security updates for your devices. You simply upload your apps choose the OS version that you want to run on your device and then deploy those updates to those devices over-the-air security updates are even deployed automatically to those devices for you. Android things also
supports powerful Hardware that suitable for Edge Computing in production capable of driving artificial intelligence and machine learning out to the edge. This Hardware is packaged into system on modules that make it easy for you to integrate into your final production designs. So when you look at all these things together, the process is a little bit different than when you were building apps on a mobile device building a typical app for Android devices means Distributing a single at binary through the Google Play Store typically apps have to work on multiple devices
made by multiple oems targeting multiple versions of the Android operating system typically requiring you to do various compatibility checks and other things like that to make sure that your app runs well across that entire breadth of ecosystem with Android things. You are the device OEM you control when the OS on your device gets upgraded and the various apps that are bundled into that system image along with it. And you do all of this through the Android things console instead of to the Google Play Store. This can greatly simplify your code because you don't need to incorporate a lot of
those same compatibility checks, but there are some things to consider that are going to be a little bit different. Let's start with this place. Android things displays are optional they're supported and you can use the full Android UI toolkit to build applications that have a graphical user interface whether it's touch-enabled or not, but we've removed a lot of the default system UI and disabled or rework some of the apis that assume the graphical displays are in place because many iot devices will not have these pieces and we don't want to place those requirements in their
the best example of this in fractals is app permissions do in Android things permissions are not granted a run time by the end-user because we can't assume that there's a graphical display to show things like this dialogue and we can't even really assume that user granting specific types of permissions is appropriate for an iot device. so instead these permissions are actually granted by you the developer using the Android things console Paso as the owner of your device, you're responsible for taking control of the apps that run on this device in the
permissions that those particular applications have now because of this permissions may not be granted by end-users. So that means you don't necessarily have to check whether or not those permissions were you don't have to request for those permission to be granted it run time. But since they are still granted dynamically, the best practice is still for your code to verify that you have that permission. Okay, because that permission could have been revoked by one of the console users and you don't want your application code to behave improperly in those
cases. So you still want to have checks like this one in your code. When you are accessing dangerous permissions that could be granted a revoked by the console, but you won't have to include the code that request those permissions upfront from the end-user. Not doing this will result in the same security exception that you would otherwise see by trying to access those protected permissions. If in fact that permission is disabled. Additionally an Android things 1.0 permissions are no longer granted automatically on reboot. This is something that we did and some of the earlier
previews and is no longer the case. So that means that as a developer, you can't simply just reboot your device to try and get all those permissions brought into your app. Automatically. You have to actually use the tooling to make that happen. So during development what you're going to want to do is provide the dash G flag when installing the applications on your device and this will grant all the permissions requested by your app by default. Android Studio actually does this for you automatically so whenever you click build and run out of the IDE this process is taken care of for you. But
if you want to do this from the command line, you're going to have to add that flag yourself. Another option is to use the p.m. Grant command to individually Grant or revoke promotion permissions inside of your application. You can do this during development or maybe just a test what the individual behavior is of a certain permission If you deny that inside of your application If you prefer to use the Gradle command line or perhaps you're running automated tests or other things were the ID is not involved. You can actually add this to your bill. Gradle file using an ATV options
block to apply that same Dash G flag any time your application is installed. Speaking of you. I we should probably talk a little bit about activities most developers think that activities are essentially screens. So if we remove displays, why do we need to keep them around that turns out that activities are a little bit more than that and activity represents a component of user focus and Android devices with graphical displays that does mean that it will render the contents of The View on to the window, but even for devices without displays activities also handle all of the user
input events weather that's coming from a touch screen input or maybe it's a game controller or keyboard or any other external input device that you may have connected. All of those events are going to be delivered to the foreground activity. So even without a graphical display activities are still a very important portion of Android user interface, even though the user interface might not actually include the graphical URI. It's important to note. Also when we're talkin about activities that are tivities are still vulnerable to configuration changes the same way that they are on
Android. So as an Android developer, you're probably used to at least at some point having to deal with an orientation change of a device and having that destroy your activity and recreate a new instance of it. That's effectively a very common configuration change on Android mobile devices. Well on Android things that specific instance probably is not very common. If it if it would happen at all. There are still a number of other configuration changes that might still happen on Android things devices things like changing the default Locale or connecting or disconnecting a keyboard a physical
keyboard from the device. All of these events have the same net effect in that that activity will be destroyed and recreated if it happens to be in the foreground, so generally speaking with your working with activities on Android things the same rules apply to act Cities in terms of the logic that you put into those components, they're effectively just as fragile in terms of their life cycle. So you're only going to want to have you based logic or user interface based logic inside of these activities try not to put too much additional State into these components. You're going to push
that out into other parts of your application. Android things even uses activities to launch your primary application as part of the boot process we do this using the home intent, which is the same intent that's used to trigger the app launcher on an Android mobile device. This intense starts your app automatically on boot and specifically the activity inside of that application started automatically on boot. And in addition to that if that application crashes or terminates for any reason Android is going to restart that application automatically. So this becomes the the main entry point
into your application that is automatically managed by the Android things platform. So we don't want to forget about activities just yet. couple other things about Android things devices Android things devices are also relatively memory constrained when you compare them to an Android phone a typical Android device may have 512 megabytes of Ram or so compare that with the multiple gigabytes of RAM that you would have on an Android phone like say a pixel or pixel 2 what this translates to for you the developer is that there's actually a much lower per process
Heap size for your individual application. So if you're not familiar with this idea Android sets a fixed Heap level on every application running on that device and it's significantly lower than the total available memory on that device. And since the Android things devices are relatively memory constrained that per process limit is significantly lower than it would be on an Android phone because of that if your porting code from an Android mobile device over to Android things you just have to realize that if you're using the same amount of memory in your app, there's going to be a lot
less free memory in that same process available to you. Okay, we have to keep that in mind and you also want to realize that this can also translate into a significantly larger amount of garbage collection events happening as you allocate new objects. Do you want to keep a close eye on object allocations how often you're doing object allocations because you may run into that ceiling much more quickly than you otherwise would on an Android device or you might see the garbage collector kicking in quite a bit more. The memory profiler in Android Studio is a really great resource to
help. You keep an eye on what's going on inside your memory. It will allow you to track those allocations overtime as well as see overlaid into it with the individual garbage collection events, so you can get a really good idea of whether or not your application is allocating too much memory and causing trouble. Some of the things that you can do to help understand your device a little bit better is use some of the activity manager methods to do some inspection on the memory capabilities of your particular device. So for example, you can use the memory class attribute on activity manager.
This will give you the exact Heap size that's available to your application the value of his return to the value in megabytes. That is how much memory you have the large memory class attribute is what your application would have if you added the large Heap attribute to your manifest, I would caution you against doing this on Android things generally speaking because Android things devices are memory constrained the memory class and the large memory class of these devices are generally configured to be the same value. So adding this attribute to your manifest is essentially not going
to do anything. You also want to inspect the low memory threshold of this device to get a sense for what that actually looks like when the available memory on the device Falls below that memory threshold. The device is in a state that we call memory pressure and we're going to talk a little bit more about what that means and why it's important in a little bit but just keep it in mind for now. I want you to notice something else about this diagram that I had up before because of this / process Heap limit that's a fixed value for a single application. If you try to put all of
your application code into a single process where a single APK you're going to be severely limited in your ability to fully utilize the memory that is available on this device. No, keep in mind with Android things the only apps on the device are your apps so you should be able to take full advantage of those memory resources as much as you possibly can the way to do that. Is to split your application into multiple processes, okay, because that limit essentially will apply to each one of those processes individually. So if you can Federer 8 the design of
your application out into multiple components that are actually running in separate apks you're going to have a much better ability to fully utilize the memory available on whatever device that you're running. To make the most effective use of our device. We're going to break this app up into multiple apks with the primary activity running in the foreground and additional apps running in the background with Support Services running inside. The additional benefit of running this architecture is it actually insulates these various components from one another? So in this scenario if a crash
happens in one of these components, it's localized just to that element and it won't bring down your entire application and have you have to restart all of that from the beginning so you can manage those individual issues just within that component and leave the rest of the applications are components running on your device to be unaffected. It also means that you can launch or relaunch these components individually as needed by your application. So you don't need to load everything at once at boot. You can launch the various services and components just as you need them.
Now it turns out the decision to put some of your components into background apps has consequences as well Android treats foreground and background processes a little bit differently and we need to be aware of what's going on under the hood hear Android Mark's application processes by priority based on how closely they are related to the foreground application. And this is very important because of a system process known as the low memory killer. Hello memory killer is a process that is constantly prowling in the background looking for new processes to devour its job is to ensure that
the free memory on the system is available to the foreground app at any given time. So if the foreground app needs new memory and the device happens to be in a system of memory pressure low memory killer is going to go hunting around for processes that can terminate to allocate that memory back to the foreground. Okay? On an Android device like a typical user-driven Android device. This can be somewhat of a nuisance to developers because their app may get terminated from the background, but at some point the users going to relaunch it later and everything will be fine on Android things the
low memory killer could mean that you have critical device functionality that is being terminated out from under you underneath you and you didn't even know it perhaps there's a device driver running in that service and Android killed that because it thought it was low enough priority in the background. Okay. So something to keep in mind as you're moving through this and Edition Android Oreo introduced execution limits for background apps. So applications can no longer be started into a background State. They must either be launched from the foreground app or bound to in some way.
It's because of these two things there's a number of different common ways that you may or may not have used in the past to launch components into the background. Okay, we're going to kind of walk through those a little bit. So the first that you might be familiar with is using the boot completed broadcast to listen for the final boot message coming from the Android framework saying that the system is up and running you can launch other apps if you would like do not use this on Android things. The primary reason is because of those background execution limits your background Services
actually can't be properly started into that state in a lot of cases. It won't even work. Okay, and in addition to that this background broadcast is Boo completed broadcast is very unpredictable in terms of its timing with a lot of cases this boot completed broadcast actually triggers much much later than when the home and tend to the home activity are fully up and running in the foreground. So if you're trying to synchronize between these two things, it's not a very good mechanism to rely on. In addition, I would recommend you don't you start service for a similar reason start Services
Limited by those same background execution limits, unless you are starting a service in foreground mode. Now for ground Services require you is a developer to actually build in a notification that would typically display to the user when that service is running. Well, we took away the system UI where that notification would display so your end up doing a bunch of work for displaying the service that it doesn't actually gain you anything. And in addition to that there are some difficulties with started Services when it comes to managing their life cycle. I started service if
you crack if that crashes for some reason you don't have a direct connection to understand that that occurred and that you need to restart that service so that you can manage that process a little bit better. Now Android does have this thing that Services can return this start sticky attribute and that's a way for applications to tell Android that this service is important and if it crashes or terminates for some reason I needed to be restarted. However, Android usually only does this about once or twice for a given service before they just sort of give up and realize that at some
point the user will launch this app again, maybe this will start again and everything will be fine. Okay, that type of thinking doesn't go well for those background services that have critical functionality in them like a like a device driver. So we recommend buying service instead by using bound services. This gives the background processes in active connection to that foreground app. Okay. So you have a you have a good indication of when that service is running and when that service has died for some reason so that you can manage that we launch it. If you need to do any of those things.
This also has the added benefit of a built-in Communication channel between the applications that are bound so you can do some more direct communication with that service without having to use Intense or other mechanisms like that to pass data back and forth. So looking at this diagram again, one of the other important reasons to use bounce Services is that pure background applications like those that would have been started by boot completed or just start service on its own are very low priority on the scale poke. Whereas bound service applications are almost
as high priority as the foreground app. They are literally the highest priority you can get without being the foreground app. So this ensures that those background processes stay safe from something like low memory killer if the device ever does get into a memory pressure situation you get better management of those services and you get better protection from a memory management perspective. Alright, let's take a look at what this would actually look like in code. So I have a just a basic example of a service here that has a device driver inside of it. In this case. This is just a
device driver to take some button inputs and convert them into key events. Like they were coming off of a keyboard. All of this logic can be fully encapsulated into this external service and can run on its own so we can build this service component and then from the foreground app, we can construct an intent to that service component and we can bind to it notice that I'm doing this from the application class and not from a the primary activity. Remember our discussion from activities before and the lifecycle associated with those if this is a service that needs to be as persistent
as possible, we want to buy into it from a component that it's expected to be around just as long K2 that you don't end up with life cycle issues where your activity gets destroyed recreated and your rebinding to that service unnecessarily doesn't necessarily cause a major problem, but it's not the best idea. In addition with bound services. This also means that you get this feedback mechanism coming through the service connection call back. So when you buy into a service you provide this call back as a service connection and when the service is up and running you will be notified through
the on service-connected method. So, you know exactly when this is now something you can interact with or communicate with if you need to in addition on service disconnected tells us any time that service stops unexpectedly maybe because it is crash or something else has occurred and at that point we probably need to take a look at restarting this especially if it's running some critical functionality on our device. So would we now have the information we need to properly manage this functionality from within our applications, which we wouldn't get with started services or other these
more independent mechanisms. So here's kind of a final picture of that architecture. Again. Android is going to manage for us automatically that foreground app using the home intent. It will launch it automatically on boots and it will relaunch it if that application crashes for any reason and then our application code can then manage these additional background Support Services through the balance service mechanism. All right. The last thing that I want to share with you today are just a couple of quick tips on doing this type of development from within Android
Studio or within the development tools. So you can manage multiple apks from within a single Android Studio project by adding each additional package as a new module. You can have multiple modules to the same project and all of those modules can represent an APK or an individual app process. This allows you to manage all of your code in one place, even though they're technically separate apps. Now by default Android Studio does not allow you to deploy an app module that does not contain a launcher activity they are an activity that has that main launcher
intent filter on it this doesn't work so well for background service apps that don't have any activities at all in them in some cases, but you can modify this behavior for a background Services app you can edit the Run configuration and simply adjust the launch options for that particular module set that Target to nothing instead of default activity this will enable Android Studio to deploy that service only app to your device and it won't complain You can also do this from the command line and one of the advantages of doing it this way is that Android Studio
does require that it you deploy only one module at a time by selecting that module from the Run configuration list in the you why so if you have an application that's constructed of four or five different modules all as individual apks, it can be a bit cumbersome. If you have to try and deploy them all individually all the time. What are the advantages of using the Gradle command line? Is that by default when you run a command like install debug with no other modifiers it builds and installs every module in that project. So with one command, you can deploy everything on the latest
version to that device and you can still do individual modules. If you would prefer to do that by just adding the module named to the command as well. Once you got the modules on the device, the other thing you can do directly from the command line that isn't really supported in Android Studio today is the ability to start those individual components, whether their activities or services. So using the a.m. Shell commands, you can trigger those Services manually. If you want to test out some of that behavior sort of independently from the rest of the system, even though they may be managed by
the foreground app in production. All right. So let's quickly review some of the tips that we've gone through here today. Don't assume a graphical URI. Design for your memory constraints on these devices break your a pup into modules buying your background services to the foreground app. Don't you start it services and use the griddle command line. If you want to have more control over deploying your modules to the device. Now if you're just as excited about Android things as we are, I want to remind everyone that we're doing a scavenger hunt here at Google Io if you
visit the link here or use the Google IO app, you can follow the instructions to find various items around the conference. And once you created what you completed those challenges you can then receive a free Android things developer kit to take home. To learn more about Android things visit the developer site and make sure to visit the codelabs office hours and other demos that we have here in the sandbox. Also, be sure to visit Android things with google.com Define featured Community projects and additional sample code. You'll also find a lot of the sample code available for some of
the demos that we have here the conference on Android things with google.com as well. Thank you everyone for your time today, and I'm really excited to see the apps that you build with Android things.
Buy this talk
Access to all the recordings of the event
Buy this video
With ConferenceCast.tv, you get access to our library of the world's best conference talks.