What is Downloadable Fonts, and why use it?

Downloadable Fonts allow the ability for apps to request fonts from a font provider application instead of including font files in the apk or downloading it themselves. A font provider application retrieves fonts and caches them locally so that other apps can request and share fonts. How cool is that!

Google Play Services (on devices with version 11+) is one such font provider, which I used for Lato as it is a Google Font. There isn’t documentation yet on how to implement your own font provider if you were to use a custom font not available through Google Play Services, however you can use fonts in XML which is still an improvement over what it used to be!

As you can see in the image above, apps using Downloadable Fonts make a FontRequest using the FontsContract API which retrieves the Typeface from the Font Provider. The Font Provider does not need to download fonts if it already exists in the Font cache.

Developers don’t need to use these APIs directly if using Downloadable Fonts through XML, which is described more in the next section.

Advantages of using Downloadable Fonts:

Reduced APK size — which can significantly impact your app installs!

Since the apps can share fonts from the same provider, this results in less usage of precious memory, disk space and cellular data. Can you guess how many Roboto font files are in your phone’s storage right now? 🙀 (yes there are apps that have them in their APKs).

Great, let’s get started!

There are three ways of implementing Downloadable Fonts in your app — using Android Studio & Google Play services, programmatically or via the Support Library. I used Android Studio to generate the required files, and used the Fonts in XML feature from the Support library to apply the downloaded fonts.

I decided to do it via XML because then you can declare the required fonts in your app’s manifest file, which allows the framework to download them ahead of time. If doing it programmatically, you can only request for fonts after the app is launched, which can cause a delay in the first layout time. Also, it is less work do it via XML!

1. If you want to use Android Studio to generate the required files, then you’ll need version 3.0+. Add the following (version 26+) to your module’s build.gradle:

implementation "com.android.support:support-compat:27.0.2"

2. Select a text view in your app that you want to apply the font to and click on the fontFamily attribute under Attributes in the graphical layout.

Select the “More Fonts…” at the bottom, which will open the dialog below.

3. Make sure to have “Create downloadable font” selected. This results in three files being downloaded — lato.xml, font_certs.xml and preloaded_fonts.xml.

lato.xml

This file contains the font attributes for loading a Typeface from the Google Fonts Provider Application.

<?xml version="1.0" encoding="utf-8"?>

<font-family xmlns:app="http://schemas.android.com/apk/res-auto"

app:fontProviderAuthority="com.google.android.gms.fonts"

app:fontProviderPackage="com.google.android.gms"

app:fontProviderQuery="Lato"

app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">

</font-family>

font_certs.xml

The system uses these certificates to verify the font provider’s identity, to avoid getting fonts from an unknown source. If using the steps above, Android Studio should have automatically generated the string certificates for dev and prod in font_certs.xml below.

<?xml version="1.0" encoding="utf-8"?>

<resources>

<array name="com_google_android_gms_fonts_certs">

<item>@array/com_google_android_gms_fonts_certs_dev</item>

<item>@array/com_google_android_gms_fonts_certs_prod</item>

</array>

<string-array name="com_google_android_gms_fonts_certs_dev">

<item>

<!-- string cert -->

</item>

</string-array>

<string-array name="com_google_android_gms_fonts_certs_prod">

<item>

<!-- string cert-->

</item>

</string-array>

</resources>

preloaded-fonts.xml

This file is referenced in the Android manifest which helps the framework pre-load fonts to avoid delays when the app is launched.

<?xml version="1.0" encoding="utf-8"?>

<resources>

<array name="preloaded_fonts" translatable="false">

<item>@font/lato</item>

<item>@font/lato_bold</item>

</array>

</resources>

4. Make sure this line is added to your app’s Manifest file, Android Studio should have done this automatically:

<meta-data

android:name="preloaded_fonts"

android:resource="@array/preloaded_fonts"/>

5. Great, now you are ready to apply the fonts in XML!

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

<item name="colorPrimary">@color/colorPrimary</item>

....

<item name="android:fontFamily">@font/lato</item>

</style>

All I had to do was set the font family in the app’s theme to get TextViews throughout the app to change to Lato, including parts that were bold or italicized. However, if you want to configure the weights, you can follow the same steps to get Lato Bold using Android Studio, and change the weight manually in lato_bold.xml that you can then apply in XML layouts:

app:fontProviderQuery="name=Lato&weight=700" //can modify the weight here

The whole thing felt like magic! But….

Gotchas

When using Support Library 26, I noticed that the toolbars throughout the app was still in Roboto, resulting in a hideous Roboto-Lato combination! Even explicitly setting the fontFamily of the toolbar in XML didn’t work. This is because Toolbar does not implement the fontFamily property, like a TextView does.

Including the fontFamily in an XML style and setting that to be the toolbar’s app:titleTextAppearance worked — the toolbar titles were now in Lato! Good news is that this seems to be fixed in Support Library 27, so this workaround shouldn’t be needed.

There were a couple of other places where Lato did not get applied — one where I was dynamically creating textviews with bold styling, and the bottom navigation view for which I used a third-party library. I initially thought that this would do the trick:

val typeface = ResourcesCompat.getFont(this, R.font.lato) //for the third-party bottom navigation view

bottomNavigationView.setDefaultTypeface(typeface) //for the bolded text view

textView.setTypeface(textView.getTypeface(), Typeface.BOLD)

I used the ResourcesCompat.getFont() method to load the Typeface and set it on both views, and voilà, there was no more Roboto! However, at one point I saw a ResourcesNotFound exception when setting the Typeface for the BottomNavigation using this method.

Essentially, if the font hasn’t been loaded by the time this method is called or the user doesn’t have the required version of Google Play Services, it will throw this exception. The safest way to avoid this is to load the font programmatically, if you are unable to set the style through XML. Here is the official documentation on how to do that.

There is also this crash with FontsContractCompat in 27.0.2, it has been fixed for a future release of support library and doesn’t seem to be an issue in 26.1.0

Other requirements and limitations worth noting:

Your compileSDK should be at least SDK 26, and if you want to use Android Studio to generate the font files, it should be version 3.0+ . If programmatically or using Fonts in XML to apply Downloadable Fonts, you must use at least Support Library 26.

. If programmatically or using Fonts in XML to apply Downloadable Fonts, you must use at least Support Library 26. Using Google Play Services as a font provider works on devices that have version 11 and above, otherwise it uses the default system font.

as a font provider works on devices that have and above, otherwise it uses the default system font. As mentioned earlier, you may not be able to use Downloadable Fonts if you’re using a custom font that isn’t available through Google Play Services. You might have to write your own custom FontProvider in this case which doesn’t have documentation yet.

I (surprisingly) enjoyed transitioning our app from Roboto to Lato thanks to Fonts in XML & Downloadable Fonts, hope you enjoy these features too! I’d love to hear from you in the comments or on Twitter, and don’t forget to 👏 the post :).