The Android Developer Challenge is back! Submit your idea before December 2.

アプリのサイズを削減

ユーザーはサイズが大きすぎると思われるアプリのダウンロードを避ける傾向にあります。この傾向が特に顕著なのが、接続にむらがある 2G、3G のネットワーク環境やバイト単位の支払いプランでデバイスが利用されている新興国の市場です。このページでは、より多くのユーザーにアプリをダウンロードしてもらえるようアプリのダウンロード サイズを減らす方法について説明します。

Android App Bundle を使用してアプリをアップロードする

Google Play に公開するときにアプリサイズをすぐに削減できるもっとも簡単な方法は、アプリを Android App Bundle としてアップロードすることです。これは新しいアップロード形式で、アプリのコンパイル済みコードとリソースはすべてここに含まれますが、APK の生成と署名は Google Play で行います。

Google Play の新しいアプリ配信モデルである「Dynamic Delivery」では、App Bundle を使用して、個々のユーザーのデバイス設定に合わせて最適化された APK を生成、配信します。そのためユーザーは、アプリの実行に必要なコードとリソースのみをダウンロードするだけで済みます。デベロッパー側では複数のデバイスをサポートするために異なる APK を生成、署名、管理する必要がなくなり、ユーザー側ではダウンロードが小さい容量で済み、より最適化されます。

Google Play では、App Bundle を使用して公開されているアプリに対して 150 MB 以下の圧縮ダウンロード サイズ制限が適用されるため、アプリのダウンロード サイズをできるだけ小さくするために、ここに記載されているガイドラインを適用することをおすすめします。

署名済み APK をアップロードして Google Play に公開するアプリの場合、圧縮ダウンロード サイズは 100 MB 以下に制限されます。

APK の構造を理解する

アプリのサイズを縮小する方法を説明する前に、アプリの APK の構造を理解しておくことをおすすめします。APK ファイルは、アプリを構成するすべてのファイルを含む ZIP アーカイブです。この ZIP には、Java クラスファイル、リソース ファイル、コンパイル済みリソースを含むファイルなどが含まれます。

APK のディレクトリ構成は次のとおりです。

  • META-INF/: CERT.SFCERT.RSA の署名ファイル、MANIFEST.MF マニフェスト ファイルが含まれます。
  • assets/: アプリが AssetManager オブジェクトを使用して取得できる、アプリのアセットが含まれます。
  • res/: resources.arsc にコンパイルされていないリソースが含まれます。
  • lib/: プロセッサのソフトウェア レイヤに固有のコンパイル済みコードが含まれます。このディレクトリには、プラットフォーム タイプごとのサブディレクトリ(armeabiarmeabi-v7aarm64-v8ax86x86_64mips など)が含まれます。

APK には次のファイルも含まれます。このうち、必須ファイルは AndroidManifest.xml のみです。

  • resources.arsc: コンパイル済みリソースが含まれます。このファイルには、res/values/ フォルダのすべての設定からの XML コンテンツが含まれます。この XML コンテンツは、パッケージング ツールによって抽出され、バイナリ形式にコンパイルされて、コンテンツとしてアーカイブされます。このコンテンツには、言語文字列やスタイルのほか、resources.arsc ファイルに直接含まれていないコンテンツのパス(レイアウト ファイル、画像など)が含まれます。
  • classes.dex: Dalvik / ART 仮想マシンで認識される DEX ファイル形式にコンパイルされたクラスが含まれます。
  • AndroidManifest.xml: コア Android マニフェスト ファイルが含まれます。このファイルには、アプリの名前、バージョン、アクセス権、参照ライブラリ ファイルがリストされます。このファイルは Android のバイナリ XML 形式です。

リソースの数とサイズを縮小する

APK のサイズは、アプリの読み込み時間、メモリの使用量、消費電力に影響します。APK を縮小するための簡単な方法の 1 つが、APK に含まれるリソースの数とサイズの削減です。特に、アプリで不要になったリソースの削除や、画像ファイルの代わりにスケーラブルな Drawable オブジェクトを使用できます。ここでは、これらの方法に加え、APK 全体のサイズを小さくするためにアプリ内のリソースを削減するその他の方法について説明します。

未使用のリソースを削除する

lint は、Android Studio にある静的コード アナライザで、コードで参照されない res/ フォルダ内のリソースを検出するツールです。lint ツールによって、プロジェクト内に未使用と思われるリソースが検出されると、以下の例のようなメッセージが出力されます。

    res/layout/preferences.xml: Warning: The resource R.layout.preferences appears
        to be unused [UnusedResources]
    

注: lint ツールでは、assets/ フォルダ、リフレクションを介して参照されるアセット、アプリにリンクしたライブラリ ファイルはスキャンされません。また、通知の出力のみで、リソースは削除されません。

コードに追加したライブラリに、不要なリソースが含まれていることもあります。アプリの build.gradle ファイルで shrinkResources を有効にすると、Gradle によってリソースを自動的に削除できます。

    android {
        // Other settings

        buildTypes {
            release {
                minifyEnabled true
                shrinkResources true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    

shrinkResources を使用するには、コードの圧縮を有効にする必要があります。ビルドプロセス時に、まず ProGuard によって未使用のコードが削除されますが、未使用のリソースはそのまま残されます。この未使用のリソースが Gradle によって削除されます。

ProGuard や、Android Studio を利用して APK のサイズを縮小するその他の方法については、コードとリソースの圧縮についての説明をご覧ください。

Android Gradle Plugin 0.7 以上では、アプリでサポートする設定を宣言できます。Gradle は、resConfigresConfigs のフレーバーと defaultConfig オプションを使用してこの情報をビルドシステムに渡します。ビルドシステムは、サポートされない他の設定のリソースが APK に表示されることを防ぐため、これによって APK のサイズが縮小されます。この機能について詳しくは、未使用の代替リソースの削除についての説明をご覧ください。

ライブラリからのリソースの使用を最小限に抑える

Android アプリを開発する場合は、通常、外部のバイナリを使用してアプリのユーザビリティと汎用性を向上させます。たとえば、Android Support Library を参照して古いデバイスでのユーザー エクスペリエンスを改善したり、Google Play 開発者サービスを使用してアプリ内のテキストを自動的に翻訳したりできます。

サーバーやデスクトップに対応するよう作成されたライブラリには、アプリに必要ないオブジェクトやメソッドが数多く含まれている可能性があります。ライセンスでライブラリの変更が許可されていれば、ライブラリのファイルを編集することで、アプリに必要なライブラリの部分のみを含めることができます。また、別のモバイル向けライブラリを使用して、アプリに特定の機能を追加することも可能です。

注: ProGuard を使用すると、ライブラリと一緒にインポートされた不要なコードを削除できます。ただし、ライブラリの大規模な内部依存関係は削除できません。

特定の密度のみをサポートする

Android は非常に多くのデバイスをサポートし、さまざまな画面密度に対応しています。Android 4.4(API レベル 19)以降のフレームワークは、ldpimdpitvdpihdpi,xhdpixxhdpixxxhdpi など、さまざまな密度に対応しています。Android はこれらのすべての密度に対応していますが、それぞれの密度にラスター形式のアセットをエクスポートする必要はありません。

特定の密度のデバイスを使用しているユーザーが少数のみであることがわかっている場合は、アプリにその密度をバンドルする必要があるかどうかを検討しましょう。特定の画面密度のリソースを含めなかった場合は、Android により、その他の画面密度向けの既存のリソースが自動的にスケーリングされます。

スケーリングした画像だけで足りる場合は、drawable-nodpi/ 内の画像のバリアントを 1 つにすることでスペースを大幅に縮小できます。各アプリには少なくとも 1 つの xxhdpi 画像バリアントを含めることをおすすめします。

画面密度について詳しくは、画面サイズと画面密度についての説明をご覧ください。

ドローアブル オブジェクトを使用する

静的な画像リソースが必要ない画像もあります。フレームワークによって実行時に画像が動的に描画されます。APK で Drawable オブジェクト(XML では <shape>)が使用するスペースはごくわずかです。さらに、XML で Drawable オブジェクトを使用すると、マテリアル デザイン ガイドラインに準拠したモノクロの画像が生成されます。

リソースを再利用する

同じ画像の色合いやシェードが異なるバージョンや回転したバージョンなど、画像のバージョンごとに別々のリソースが含まれていることがあります。Google では、同じリソースセットを再利用し、実行時に必要に応じてカスタマイズすることをおすすめします。

Android にはアセットの色を変更するためのツールがいくつか用意されています。Android 5.0(API レベル 21)以降では、android:tint 属性または tintMode 属性を使用してアセットの色を変更できます。それ以前のバージョンでは、ColorFilter クラスを使用します。

また、別のリソースを回転させただけのリソースも削除できます。以下のコード スニペットは、画像の中央を中心に 180 度回転させることで「親指を立てる」画像を「親指を下げる」画像に変換する例です。

    <?xml version="1.0" encoding="utf-8"?>
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/ic_thumb_up"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fromDegrees="180" />
    

コードからレンダリングする

画像の手続き型レンダリングによっても APK のサイズを縮小できます。手続き型レンダリングを使用すると、APK に画像ファイルが保存されなくなるため、スペースが解放されます。

PNG ファイルをクランチする

aapt は、ビルドプロセス時に可逆圧縮によって res/drawable/ 内の画像リソースを最適化できるツールです。たとえば、aapt ツールを使えば、256 色以内で済むトゥルーカラーの PNG をカラーパレットを使用する 8 ビットの PNG に変換できます。この変換により、画質を変えずにメモリの容量を縮小できます。

なお、aapt には次の制限があります。

  • aapt ツールを使用して、asset/ フォルダ内の PNG ファイルを圧縮することはできません。
  • aapt ツールで画像ファイルを最適化するには、使用する色が 256 色以下である必要があります。
  • aapt ツールは、すでに圧縮されている PNG ファイルのサイズを大きくすることがあります。これを防ぐには、Gradle で cruncherEnabled フラグを使用して PNG ファイルに対するこのプロセスを無効にします。
    aaptOptions {
        cruncherEnabled = false
    }
    

PNG ファイルと JPEG ファイルを圧縮する

pngcrushpngquantzopflipng などのツールを使用すると、画質を損なわずに PNG ファイルのサイズを縮小できます。これはすべて、画質を保ちながら PNG ファイルのサイズを縮小できるツールです。

pngcrush ツールは特に効果的です。このツールは、PNG フィルタと zlib(Deflate)パラメータを順番に調べ、フィルタやパラメータを組み合わせて画像を圧縮し、圧縮後の出力が最も小さい設定を選択します。

JPEG ファイルを圧縮するには、packJPGguetzli などのツールを使用できます。

WebP ファイル形式を使用する

Android 3.2(API レベル 13)以降を対象とする場合は、PNG ファイルや JPEG ファイルの代わりに、WebP ファイル形式を画像に使用することもできます。WebP 形式には非可逆圧縮モード(JPEG の置き換え)と透過モード(PNG の置き換え)がありますが、圧縮率は JPEG や PNG より高くなります。

既存の BMP、JPG、PNG、静的 GIF 画像は、Android Studio を使用して WebP 形式に変換できます。詳しくは、Android Studio を使用した WebP 画像の作成についての説明をご覧ください。

注: Google Play で APK が承認されるのは、ランチャー アイコンが PNG 形式の場合のみです。

ベクター グラフィックを使用する

ベクター グラフィックを使用して、解像度に依存しないアイコンやその他のスケーラブルなメディアを作成できます。これらのグラフィックを使用することで APK の容量を大幅に縮小できます。ベクター画像は Android では VectorDrawable オブジェクトとして表示されます。VectorDrawable オブジェクトを使用した場合、100 バイトのファイルで画面サイズの鮮明な画像を生成できます。

ただし、各 VectorDrawable オブジェクトのレンダリングにはかなりの時間がかかります。画像が大きいほど、画面に表示されるまでの時間が長くなります。そのため、これらのベクター グラフィックは小さい画像を表示する場合にのみ使用することをご検討ください。

VectorDrawable オブジェクトの使い方について詳しくは、ドローアブルの操作についての説明をご覧ください。

アニメーション画像にベクター グラフィックを使用する

フレーム単位のアニメーションの作成に AnimationDrawable を使用しないでください。アニメーションのフレームごとに別々のビットマップ ファイルを含める必要があるため、APK のサイズが著しく増大します。

アニメーションのベクター ドローアブルを作成する場合は、代わりに AnimatedVectorDrawableCompat を使用してください。

ネイティブ コードと Java コードを削減する

アプリ内の Java コードベースとネイティブ コードベースを削減するのに役立つ方法がいくつかあります。

不要な生成コードを削除する

自動的に生成されるコードの容量をご確認ください。たとえば、多くのプロトコル バッファツールでは、生成されるメソッドやクラスの数が非常に多く、そのためにアプリのサイズが 2~3 倍になることがあります。

列挙を利用しない

1 つの列挙で、アプリの classes.dex ファイルのサイズが 1.0~1.4 KB ほど増えることがあります。システムが複雑だったり共有ライブラリがあったりすると、あっという間にサイズが大きくなる可能性があります。可能であれば、@IntDef アノテーションと ProGuard を使用して列挙を取り除き、整数に変換することをご検討ください。この型変換では、列挙の型の安全性がすべて保証されます。

ネイティブ バイナリのサイズを縮小する

アプリでネイティブ コードと Android NDK を使用している場合は、コードを最適化することでアプリのリリース バージョンのサイズも縮小できます。次に、デバッグ シンボルを削除する方法とネイティブ ライブラリを抽出しない方法の 2 つの便利な方法を紹介します。

デバッグ シンボルを削除する

アプリが開発中でまだデバッグが必要なうちは、デバッグ シンボルを使用するのは妥当なことです。ネイティブ ライブラリから不要なデバッグ シンボルを削除するには、Android NDK で提供されている arm-eabi-strip ツールを使用します。削除した後、リリースビルドをコンパイルできます。

ネイティブ ライブラリを抽出しない

アプリのリリース バージョンを作成する場合は、アプリのマニフェストの <application> 要素に android:extractNativeLibs="false" を設定し、APK で、圧縮されていない .so ファイルをパッケージ化します。このフラグを無効にすると、PackageManager がインストール時に APK からファイルシステムに .so ファイルをコピーできなくなり、アプリのアップデートを縮小できるというメリットもあります。

複数の小さい APK を維持する

APK には、追加の言語や画面密度ごとのリソースなど、ユーザーがダウンロードするけれども使用しないコンテンツが含まれることがあります。ユーザーのダウンロードを最小限に抑えるには、Android App Bundle を使用してアプリを Google Play にアップロードするのがおすすめです。App Bundle をアップロードすると、Google Play では個々のユーザーのデバイス設定に合わせて最適化された APK を生成、配信されます。そのためユーザーは、アプリの実行に必要なコードとリソースのみをダウンロードするだけで済みます。デベロッパー側では複数のデバイスをサポートするために異なる APK を生成、署名、管理する必要がなくなり、ユーザー側ではダウンロードが小さい容量で済み、より最適化されます。

アプリを Google Play に公開しない場合は、画面サイズや GPU テクスチャのサポートなどの要因で区別された、複数の APK にアプリを分割することができます。

ユーザーがアプリをダウンロードする場合は、デバイスに搭載されている機能や設定に基づいて適切な APK を受け取り、該当しない機能向けのアセットを受け取ることはありません。たとえば、ユーザーのデバイスが hdpi の場合、より高密度のディスプレイを搭載したデバイス向けの xxxhdpi リソースは必要ありません。

詳しくは、APK 分割の設定複数の APK の維持についての説明をご覧ください。