Skip to content

Most visited

Recently visited

navigation

コードとリソースの圧縮

APK ファイルを可能な限り小さくするには、圧縮を有効にして、リリースビルドから未使用のコードとリソースを削除する必要があります。このページでは、APK ファイルを小さくする方法と、ビルド時に保持または破棄するコードとリソースを指定する方法について説明しています。

ProGuard ではコードの圧縮が使用できます。ProGuard は、含まれているコード ライブラリなどにある未使用のクラス、フィールド、メソッド、および属性をパッケージ化されたアプリから検出して削除します(64K 参照制限を回避するための有用なツールです)。また、ProGuard はバイトコードを最適化して、未使用のコード指示を削除し、残りのクラス、フィールド、メソッドを短い名前で難読化します。コードを難読化すると、APK をリバース エンジニアリングすることが難しくなるため、アプリでライセンス認証などの高度なセキュリティ機能を使用している場合は特に有用になります。

Android Plugin for Gradle ではリソースの圧縮が使用できます。Android Plugin for Gradle は、コード ライブラリなどにある未使用のリソースをパッケージ化されたアプリから削除します。Android Plugin for Gradle はコードの圧縮と連携して機能するため、未使用のコードが削除されると、参照されなくなったリソースも安全に削除することができます。

このドキュメントで説明している機能は次のツールで提供されます。

コードの圧縮

ProGuard でコードの圧縮を有効にするには、build.gradle ファイルの適切なビルドタイプに minifyEnabled true を追加します。

コードを圧縮すると、ビルド時間が長くなることに注意してください。可能な限り、デバッグビルドでのコードの圧縮は避けてください。ただし、テストに使用する最終 APK ではコードの圧縮を有効にすることが重要になります。保持するコードのカスタマイズが不十分であると、コードの圧縮がバグの原因になる可能性があるためです。

たとえば、build.gradle ファイルの次のスニペットは、リリースビルドのコード圧縮を有効にします。

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

注: Instant Run を使用している場合、Android Studio は ProGuard を無効にします。

minifyEnabled プロパティに加えて、proguardFiles プロパティにより ProGuard のルールが定義されます。

各ビルド バリアント固有の ProGuard ルールを追加するには、対応する productFlavor ブロックに別の proguardFiles プロパティを追加します。たとえば、次の Gradle ファイルは、flavor2-rules.proflavor2 プロダクト フレーバーに追加します。ここで、release ブロックのルールも適用されるため、flavor2 は 3 つすべての ProGuard ルールを使用しています。

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                   'proguard-rules.pro'
        }
    }
    productFlavors {
        flavor1 {
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

各ビルドで ProGuard は次のファイルを出力します。

dump.txt
APK のすべてのクラスファイルの内部構造を説明しています。
mapping.txt
クラス、メソッド、およびフィールドの元の名前と難読化後の名前の対応関係を提供します。
seeds.txt
難読化さていないクラスとメンバーを一覧にしています。
usage.txt
APK から削除されたコードを一覧にしています。

これらのファイルは <module-name>/build/outputs/mapping/release/ に保存されます。

保持するコードのカスタマイズ

状況によっては、デフォルトの ProGuard 設定ファイル(proguard-android.txt)で十分であり、ProGuard は未使用のコードのみをすべて削除します。ただし、多くの場合は、ProGuard による正確な分析が困難であり、アプリが実際に必要としているコードが削除される可能性があります。次のようなケースでは、ProGuard が誤ってコードを削除する可能性があります。

アプリをテストすることで、不適切に削除されたコードが原因で発生したエラーが明らかになりますが、<module-name>/build/outputs/mapping/release/ に保存されている usage.txt 出力ファイルを調べて、削除されたコードを特定することもできます。

エラーを修正して、ProGuard が特定のコードを保持するようにするには、ProGuard の設定ファイルに -keep 行を追加します。次に例を示します。

-keep public class MyClass

または、保持するコードに @Keep アノテーションを追加できます。クラスに @Keep を追加すると、エントリクラスがそのまま保持されます。このアノテーションをメソッドまたはフィールドに追加すると、メソッド / フィールド(および、その名前)に加えて、クラス名がそのまま保持されます。このアノテーションは、Annotations Support Library を使用しているときにのみ利用できることに注意してください。

-keep オプションを使用する場合は、考慮すべき事項が多くあります。設定ファイルのカスタマイズに関する詳細については、ProGuard マニュアルをご覧ください。トラブルシューティングのセクションには、コードを削除したときに発生する可能性のあるその他の問題の概要が記載されています。

難読化されたスタックトレースのデコード

ProGuard がコードを圧縮すると、メソッド名が難読化されるため、スタックトレースの読み取りが(不可能ではなくとも)難しくなります。幸いにも、ProGuard は実行のたびに mapping.txt ファイルを作成します。このファイルには、難読化された名前にマッピングされたクラス、メソッド、およびフィールドの元の名前が表示されています。ProGuard はこのファイルをアプリの <module-name>/build/outputs/mapping/release/ ディレクトリに保存します。

ProGuard でリリースビルドを作成するたびに、mapping.txt ファイルは上書きされることに注意してください。したがって、新しいリリースを公開するたびに、このファイルのコピーを保存する必要があります。各リリースビルドの mapping.txt ファイルのコピーを保持していると、ユーザーが古いバージョンのアプリから難読化されたスタックトレースを送信した場合でも、問題をデバッグできるようになります。

Google Play でアプリを公開するときに、各バージョンの APK の mapping.txt ファイルをアップロードすることができます。Google Play によって、ユーザーが報告した問題のスタックトレースが難読化解除されるため、Google Play Developer Console で問題を調べることができます。詳細については、クラッシュのスタックトレースを解読するに関するヘルプセンターの記事を参照してください。

難読化されたスタックトレースを自分で読み取り可能なスタックトレースに変換するには、retrace スクリプト(Windows の場合は retrace.bat、Mac/Linux の場合は retrace.sh)を使用します。このスクリプトは <sdk-root>/tools/proguard/ ディレクトリにあります。このスクリプトは mapping.txt ファイルとスタックトレースを取得し、新しい読み取り可能なスタックトレースを生成します。retrace ツールを使用するための構文は次のとおりです。

retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]

次に例を示します。

retrace.bat -verbose mapping.txt obfuscated_trace.txt

スタックトレース ファイルを指定していない場合、retrace ツールは標準の入力からスタックトレースを読み取ります。

リソースの圧縮

リソースの圧縮は、必ずコードの圧縮と連動して機能します。コード圧縮ツールにより未使用のコードが削除された後、リソース圧縮ツールで、アプリがまだ使用しているリソースを特定できます。これは、リソースが含まれるコード ライブラリを追加している場合に特に当てはまります。未使用のライブラリ コードを削除すると、ライブラリ リソースが参照されなくなります。したがって、リソース圧縮ツールによってライブラリ リソースを削除できます。

リソースの圧縮を有効にするには、build.gradle ファイルで shrinkResources プロパティを true に設定します(あわせて、コードを圧縮するために minifyEnabled を有効にします)。次に例を示します。

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

コード圧縮用の minifyEnabled を使ってアプリをまだビルドしていない場合、shrinkResources を有効にする前に、コードが圧縮されたアプリを作成してください。リソースの削除を開始する前に、動的に作成または呼び出されるクラスまたはメソッドを保持するために proguard-rules.pro ファイルを編集する必要がある場合があるためです。

注: 現在、リソース圧縮ツールは、values/ フォルダで定義されているリソース(文字列、寸法、スタイル、色など)を削除しません。これは、Android Asset Packaging Tool(AAPT)では、Gradle プラグインにより定義済みバージョンのリソースを指定することが許可されないためです。詳細については、問題 70869 を参照してください。

保持するリソースのカスタマイズ

特定のリソースを保持または破棄したい場合、プロジェクトに <resources> タグが付いた XML ファイルを作成し、保持する各リソースを tools:keep 属性に、破棄する各リソースを tools:discard 属性に指定します。どちらの属性も、リソース名のコンマ区切りのリストを受け入れます。また、アスタリスク文字をワイルドカードとして使用することができます。

次に例を示します。

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

このファイルをプロジェクト リソースに(たとえば、res/raw/keep.xml に)保存します。このファイルは、ビルドで APK にパッケージ化されません。

リソースを削除できる場合、破棄するリソースを指定する意味がないように思えますが、この作業はビルド バリアントを使用するときに役立つ場合があります。たとえば、特定のリソースがコードで使用されるが(したがって、圧縮ツールによって削除されない)、特定のビルド バリアントに対しては実際に使用されないことがわかっている場合は、すべてのリソースを共通のプロジェクト ディレクトリに格納してから、各ビルド バリアントに対して別個の keep.xml ファイルを作成します。

厳密な参照チェックの有効化

通常、リソース圧縮ツールでは、リソースが使用されるかどうかを正確に特定することができます。ただし、コードが Resources.getIdentifier() への呼び出しを実行する場合(または、いずれかのライブラリ(AppCompat ライブラリなど)がこの呼び出しを実行する場合)、コードは、動的に生成された文字列に基づいてリソース名を検索します。この処理が実行されると、リソース圧縮ツールはデフォルトで防御的に動作し、一致する名前形式を備えたすべてのリソースを、使用される可能性があるリソースとしてマークするため、リソースの削除が行えなくなります。

たとえば、次のコードは、img_ 接頭辞が付いたすべてのリソースを使用されるリソースとしてマークします。

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

リソース圧縮ツールはコードのすべての文字列定数とさまざまな res/raw/ リソースを調べて、file:///android_res/drawable//ic_plus_anim_016.png と類似した形式のリソース URL を検索します。このツールでは、このような文字列またはこのような URL の作成に使用されそうな他の文字列を検出した場合、それらの文字列を削除しません。

これらは、デフォルトで有効になっている安全な圧縮モードの例です。ただし、この「安全第一」の処理を無効にして、リソース圧縮ツールで、確実に使用されるリソースのみを保持するように指定することができます。そのためには、次のように、keep.xml ファイルで shrinkModestrict に設定します。

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

厳密な圧縮モードを有効にしており、上記のように、コードによって、動的に生成された文字列でリソースが参照される場合は、tools:keep 属性を使用して、これらのリソースを手動で保持する必要があります。

未使用の代替リソースの削除

Gradle のリソース圧縮ツールは、アプリのコードによって参照されないリソースのみを削除します。これは、異なる端末設定の代替リソースは削除されないことを意味します。必要に応じて、Android Gradle プラグインの resConfigs プロパティを使用して、アプリが必要としない代替リソース ファイルを削除することができます。

たとえば、言語リソースが含まれるライブラリ(AppCompat や Google Play サービスなど)を使用している場合、アプリの残りの部分が同じ言語に翻訳されるかどうかに関係なく、APK には、これらのライブラリにあるメッセージのすべての翻訳言語の文字列が含まれます。アプリが公式にサポートする言語のみを保持する場合は、resConfig プロパティを使用して、それらの言語を指定することができます。指定されていない言語のリソースは削除されます。

次のスニペットは、言語リソースを英語とフランス語のみに制限する方法を示しています。

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

同様に、APK の分割を使用して、どの画面密度や ABI リソースを APK に含めるかをカスタマイズして、さまざまな端末用にそれぞれ異なる APK をビルドすることができます。

重複リソースの結合

Gradle はデフォルトで、異なるリソース フォルダに存在する同じ名前のドローアブルなど、同一の名前を持つリソースを結合します。この動作は、shrinkResources プロパティによって制御することも、削除することもできません。これは、コードが検索している名前に一致するリソースが複数あったときのエラーを回避するために必要な動作です。

リソースの結合は、2 つ以上のファイルが同一のリソース名、タイプ、修飾子を共有しているときにのみ発生します。Gradle は、重複ファイルのうち最適であると判断したファイルを選択し(以下に説明する優先順位に基づいて)、APK ファイルでの配布用にその 1 つのリソースのみを AAPT に渡します。

Gradle は次の場所で重複リソースを検索します。

Gradle は、次の優先順位に従って重複リソースを結合します。

依存関係 → メイン → ビルド フレーバー → ビルドタイプ

たとえば、メイン リソースとビルド フレーバーの両方に重複リソースがある場合、Gradle はビルド フレーバー内のリソースを選択します。

同じソースセットに同一のリソースがある場合、Gradle はこれらのリソースを結合できないため、リソース結合エラーが出力されます。このエラーは、build.gradle ファイルの sourceSet プロパティに複数のソースセットを定義している場合に発生します。たとえば、src/main/res/src/main/res2/ の両方に同じリソースが含まれている場合です。

リソース圧縮のトラブルシューティング

リソースを圧縮すると、Gradle コンソールには、アプリ パッケージから削除されたリソースの概要が表示されます。次に例を示します。

:android:shrinkDebugResources
Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

また、Gradle は <module-name>/build/outputs/mapping/release/(ProGuard の出力ファイルと同じフォルダ)に resources.txt という名前の診断ファイルを作成します。このファイルには、どのリソースが他のリソースを参照したか、どのリソースが使用または削除されたかなどの詳細が含まれています。

たとえば、APK に @drawable/ic_plus_anim_016 が引き続き存在している理由を特定するには、resources.txt ファイルを開き、そのファイル名を検索します。次の例のように、そのファイルが別のリソースから参照されていることが確認できる場合があります。

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

ここで、@drawable/add_schedule_fab_icon_anim に到達できた理由を知るため、上方に検索すると、そのリソースが "The root reachable resources are:" の下にあることがわかります。つまり、add_schedule_fab_icon_anim に対するコード参照があります(その R.drawable ID は到達可能なコードで見つかりました)。

厳密なチェックを使用していない場合は、動的に読み込まれるリソースの名前の作成に使用されそうな文字列定数があると、リソース ID が到達可能であるとマークされる可能性があります。この場合、ビルド出力でそのリソース名を探すと、次のようなメッセージが見つかる場合があります。

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

これらのいずれかの文字列が表示され、特定のリソースの動的な読み込みにその文字列が使用されていないことが確実である場合、保持するリソースのカスタマイズに関するセクションで説明したように、tools:discard 属性を使用して、そのリソースを削除するようにビルドシステムに指定することができます。

This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.