[MUSIC PLAYING]
BEN WEISS: The Android app bundle
is the distribution format for Android applications
in the modern Android development world.
Within applications using it, we've
seen an average file size savings of 15%
on users' devices, compared to a universal APK.
And you can benefit from these savings
without having to make any changes to the app's code,
simply by switching over to Android app bundle.
To learn more about how this is made possible,
watch the previous video in the series.
I'll be waiting here.
All caught up?
Now you know how the Android app bundle
enables smaller installation sizes
by serving optimized APKs for each user's device
configuration.
And there's more to app bundles.
You can take matters into your own hands
to optimize installation even further
using play feature delivery.
But why should you care about this?
On the Play Store, we've seen that in reducing
the initial download size of an app by 3
megabytes can result in a jump in installations of about 1%.
In this video, I'll be talking about the advanced features
of Android app bundles that enable play feature delivery.
And I will introduce you to the APIs
that you can use to deliver features
conditionally or on demand, as well as multiple configuration
options.
Let's get started.
Here we are in Android Studio.
More precisely, in the playcore KTX sample
that you can find on GitHub and in the description below.
Currently, I'm looking at the Android manifest file
of the picture feature module.
We're going to configure the picture module
as an install time module.
When you start to modularize an app using feature modules,
this is your baseline.
Here you already reap benefits, such as improved
build speed and engineering velocity.
While Android Studio's new module dialog takes you
through the configuration, I will show you
what happens under the hood when you click the buttons
and how you can change your configuration later on.
For any distribution configuration to take place,
a distribution namespace is required.
Similar to the app namespace or tools
namespace that you are already familiar with,
it has to be added as an attribute to the manifest tag.
I'm using a live template that soon will be integrated
into Android Studio for this.
With this namespace declared, I can declare my module.
In the module tag itself, only the module's title is declared.
That title can be visible to users in several places
throughout your app-- for example,
when the module is downloaded.
The value usually is a good resource, and, as such,
can also be translated.
With the module declared, I can further
configure delivery options for this dynamic feature module.
All delivery options have to be declared within the module tag.
Let's open it again and make sure to close it
after ourselves.
The mechanism that enables future module installation
requires devices to run Android Lollipop 5.0 or newer.
So for older versions of Android,
the feature module's content can be placed into the base APK
itself.
To enable this, I set the fusing tag
and say we're going to include this module for older
versions of Android.
I set the value to true to enable fusing of this module.
Next up, delivery options.
Here, I can declare how I want the module to be
delivered to users of my app.
In a classic, monolithic world, only
install time delivery was available.
In the world of modern Android development,
it remains available as one of the delivery options.
In fact, it is the perfect delivery option
to get started when you modularize your app.
To use it, I declare my module as an install time module.
By default, every install time module
will be fused into the base module.
This makes them non-removable.
If you want to be able to remove an install time module later
on, all you have to do is to set the removable attributes
value to true.
Uninstalling modules can be useful for tutorials,
sign-up flows, or other modules with a large footprint
that you want to have available during initial installation
of your app but that are unlikely to be of further use
once the flow has been completed.
We provide the playcore API to request installation
and an installation of modules, and I
will cover that later in this video this module.
This module now is successfully set up
as an install time module, but there are more delivery options
available.
Next to install time delivery, conditional delivery
is another way to request a feature module.
How does a feature module get delivered in that case?
Well, it depends, literally.
To make use of conditional delivery,
we open the install time tag.
I start off by declaring the conditions attribute.
And here we have quite a few different options.
The installation can either depend
on the device's API level, user's country,
or device's features.
Let's go through them step by step.
The API level could be a lower bound, in which case,
we set the min API tag with a specified value.
But it also could be an upper bound.
In this case, we would set the max API
level with a specified value.
And the second deciding factor I want to mention
is conditional delivery based on device's features.
Here, you can make sure the features only deliver to a user
when the device has the technical capabilities
to use a feature.
So here I declare that this feature
should be downloaded and installed
whenever there is a camera module available by specifying
the device feature and then, by name,
using Android hardware camera.
There are a lot of features to pick from,
and you can choose from any device
feature available in the user's feature attribute.
In the classical world, an app only
could declare that it requires a feature to function.
This could have led to unintentionally
excluding some users.
With Android app bundles and conditional delivery,
you can make a feature available if the device has
these capabilities, and otherwise,
ship a different module.
While by default, each user is able to download every feature
wherever an app is available, you
can choose to make certain features only available
for certain countries.
This is a great way to localize your app.
To do so, I add to user countries tag.
And in that tag, I can specify a country code.
And to make it a list, I add another country.
Now we can decide whether a feature should
be included or excluded in the declared list using
the exclude tag on user countries.
If we set exclude to true, that would
mean that this module is available for all users
in all countries except for the countries in this list.
Setting the exclusive attribute to false
makes this module available only to users in the specified
countries.
Sometimes, all you have is a large asset
that you want to deliver to users, such
as, maybe, a TensorFlow model.
In cases where you have no code associated with a feature
module, make sure that you set has code to false
in the Android manifest.
This will let the compiler know that no dex
file should be created.
Missing to set this flag, if there is no code in the module,
will result in a runtime exception.
So it's important to remember.
This module contains code, so I delete this line again.
Install time and conditional installation of feature modules
are configured only through the Android manifest file
and already give you a lot of configuration options.
To take the exact installation time into your own hands,
you can make use of on-demand installation.
That means we can call an API to install the feature
module after the app has been downloaded and installed
on the user's device.
Doing that saves initial download
time and size on the device.
On-demand installation requires not only
configuration in the Android manifest, but also
handling of installation states within the app.
This is what we'll look at next.
When I set on demand in the delivery tag,
the feature module will be delivered on demand.
Then, I head to the module where I
want to request the feature module's installation from.
In the playcore KTX sample, that is in the app module.
The class that always handles calling the Play Store
and notifying your app of the module's installation state
is called split install manager.
In onAttach, it is initialized through the split
install manager factory, where I pass on the context
to attach to fragment.
The split install manager is manually
injected into the view model through the view model
pro editor factory.
When the Select Picture button is clicked,
the view model's invoke picture selection function is invoked.
As we wanted to separate concerns and make
sure our code is extensible, this then
calls into the attach fragment from feature module function,
which takes in the module name, as well as the fragment's
fully qualified class name.
At this point, we come back to the actual demand installation
code.
The manager has queried whether the module in question
is already installed.
In case it is, the fragment can be attached immediately.
Otherwise, the code leads to request module installation,
which calls split install manager's request
to install with the module names to install.
As you can see here, the split install
manager will enable you to download and install
multiple modules in a single request.
As this is the playcore KTX sample,
we use Kotlin extensions over plain playcore APIs.
To listen for changes in installation state,
we utilize the request progress flow extension.
As we only care for a single module name,
we filter by that module name and then respond
to the return states using the map function of flow.
As this is part of core routines, it's asynchronous,
meaning calls are not blocking.
Listeners are being registered and unregistered for you
underneath as the lifecycle progresses.
And in the end, a live data is returned that can be observed.
On a higher level, only the live data
needs to be requested and observed.
Within the install view model, the picture
module status life data is observed.
The omitted state is used to update the module button.
Depending on which state is observed,
the text and behavior of the button changes.
That was a fairly deep dive into the sample,
and I hope you learned something best practices
on how to request on-demand installation of a feature
module.
Before wrapping up this video, I want to talk about a quick win
when it comes to making sure your app is
even smaller, through minification and resource
shrinking.
To enable minification throughout your entire app,
including all feature modules, you
can set the minified enabled property
to true in the app space module.
On top of this, you can declare that unused resources
in a feature module should be removed.
To do this, you can set shrink resources to true
for each module where you want to enable this feature.
Now you know how to customize delivery
by configuring your Android manifest
and how to use the playcore API to download, install,
and uninstall features on demand.
These customizations can add a certain layer of complexity
to your application that has to be taken into account
and tested.
You might ask yourself things like,
does the right code get executed when
a feature is not installed?
Or, how does my app behave when the installation of a module
fails?
Well, we have you covered on answering
these and more questions in the next video of the series, which
is all about testing.
To get notified when new videos get published,
subscribe to the Android developers channel.
And if you have questions on this video's content,
please leave a comment.
Thank you very much for watching.
Bye.
[MUSIC PLAYING]