Google announced native custom font support at I/O 2017. With Support Library 26, the feature was backported to API 14 — fonts work as first-class resources rather than raw assets bundled in the APK. The library also introduced downloadable fonts: fonts served on demand from a provider (Google Play Services for Google Fonts), shared across applications, and never bundled at build time.

There are three approaches: fonts as resources, downloadable fonts via XML, and downloadable fonts requested programmatically. Here's how each works.


## Fonts as a Resource

Android Studio 3.0 adds a font directory under res/. Drop a .ttf or .otf file there and reference it directly in XML:

<android.support.v7.widget.AppCompatButton    android:id="@+id/action_xml"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_marginBottom="8dp"    android:text="@string/label_xml"    android:textSize="16sp"    app:fontFamily="@font/ubuntu_bold"/>

To load it programmatically:

Typeface typeface = getResources().getFont(R.font.ubuntu_bold);

Note

getFont() requires API 26+. The XML app:fontFamily attribute works on older APIs via the support library.

### Font Families

Group related weights and styles into a single font family XML:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
    <font
        android:font="@font/open_sans"
        android:fontStyle="normal"
        android:fontWeight="400"/>
    <font
        android:font="@font/open_sans_italic"
        android:fontStyle="italic"
        android:fontWeight="400"/>
</font-family>

### App-wide Font via Theme

Apply a font to your entire app by setting android:fontFamily in the theme:

<resources>    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">        <item name="colorPrimary">@color/colorPrimary</item>        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>        <item name="colorAccent">@color/colorAccent</item>        <item name="android:fontFamily">@font/open_sans</item>    </style></resources>

## Downloadable Fonts

Instead of bundling fonts in the APK, downloadable fonts are fetched from a Font Provider at runtime. Multiple apps sharing the same font download it only once, saving storage and data.

How downloadable fonts work end-to-end

Trade-offs

  • APK size is reduced — fonts are not bundled
  • Network failures fall back to system defaults
  • Requires Google Play Services as the font provider

### Selecting via Android Studio

The easiest path is through the layout editor: click the fontFamily attribute, choose "More Fonts...", and Android Studio sets up the XML automatically.

### XML Configuration

To set one up manually, create a font family XML pointing at the provider:

<?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="Open Sans"
    app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>
Attribute Purpose
fontProviderAuthority Unique URI identifying the Font Provider
fontProviderPackage Root package name of the provider
fontProviderQuery Font name, optionally with weight/width: name=Open Sans&weight=700&width=75
fontProviderCerts Provider's signing certificate array

### Certificate Configuration

Android Studio populates these automatically when you use the font picker. The structure:

<?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><!-- dev cert --></item>
    </string-array>
    <string-array name="com_google_android_gms_fonts_certs_prod">
        <item><!-- prod cert --></item>
    </string-array>
</resources>

## Preloading Fonts

By default fonts download synchronously when their containing layout inflates. Declaring them in the manifest tells the system to fetch them in the background before your app launches:

### Declare the font array

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="preloaded_fonts" translatable="false">
        <item>@font/open_sans</item>
        <item>@font/open_sans_bold</item>
        <item>@font/open_sans_light</item>
    </array>
</resources>

### Reference it in the manifest

<application>
    ...
    <meta-data
        android:name="preloaded_fonts"
        android:resource="@array/preloaded_fonts" />
</application>

You can also control the fetch strategy per font family:

<font-family
    ...
    app:fontProviderFetchStrategy="async"
    app:fontProviderFetchTimeout="500">
</font-family>

## Programmatic Font Requests

For dynamic font loading at runtime, use FontsContractCompat:

class MainActivity : AppCompatActivity() {    private var regular: AppCompatTextView? = null    private val certificate = R.array.com_google_android_gms_fonts_certs    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_labels_no_fonts)        regular = findViewById(R.id.tv_regular)        val sansRegularRequest = FontRequest(            PROVIDER_AUTHORITY,            PROVIDER_PACKAGE,            "Open Sans",            certificate        )        val sansRegularCallback = object : FontsContractCompat.FontRequestCallback() {            override fun onTypefaceRetrieved(typeface: Typeface?) {                regular!!.typeface = typeface            }            override fun onTypefaceRequestFailed(reason: Int) {                // handle failure — reason is an error code constant            }        }        FontsContractCompat.requestFont(this, sansRegularRequest, sansRegularCallback, Handler())    }    companion object {        private val PROVIDER_PACKAGE = "com.google.android.gms"        private val PROVIDER_AUTHORITY = "$PROVIDER_PACKAGE.fonts"    }}

Warning

The Handler passed to requestFont must not run on the UI thread.


## Result

Final app showing multiple custom fonts applied

This was a meaningful upgrade from the old approach of bundling font files as raw assets and loading them manually through Typeface.createFromAsset():

The old asset-based approach

Fonts as resources and downloadable fonts make the old approach unnecessary for almost every use case.

The full source is on GitHub.

support

Found this useful? You can buy me a coffee.

☕ buy me a coffee