APK Analyzer でのビルドの分析

Android Studio に付属している APK Analyzer では、ビルドプロセスの完了後に APK の構成をすぐに分析できます。APK Analyzer を使用すると、DEX ファイルやアプリ内のリソースに関する問題のデバッグにかかる時間を短縮できるほか、APK のサイズを縮小することもできます。APK Analyzer は、コマンドラインで apkanalyzer を使って実行することもできます。

APK Analyzer では以下を行うことができます。

  • APK 内のファイル(DEX ファイルや Android リソース ファイルなど)の絶対サイズと相対サイズを表示する。
  • DEX ファイルの構成を把握する。
  • APK 内のファイル(AndroidManifest.xml ファイルなど)の最終バージョンをすばやく表示する。
  • 2 つの APK を対照比較する。

プロジェクトが開いているときに APK Analyzer にアクセスする方法には次の 3 つがあります。

  • Android Studio の [Editor] ウィンドウに APK をドラッグする。
  • [Project] ウィンドウで [Project] パースペクティブに切り替えて、デフォルトの build/output/apks/ ディレクトリで APK をダブルクリックする。
  • メニューバーで [Build] > [Analyze APK] を選択し、APK を選択する。

重要: デバッグビルドを分析するときには、[Build] > [Build APK] を選択して作成した APK、または gradle コマンドで作成した APK を使用します。ツールバーで [Run] をクリックすると、Instant Run 対応の APK が作成されますが、これらは開発専用であり、ほとんどのリソースを動的に読み込むため、APK Analyzer による最適化タスクではこれらの APK を使用しないでください。Instant Run APK は、APK 内に埋め込まれた instant-run.zip ファイルの有無によって特定できます。

ファイルとサイズに関する情報の表示

APK は ZIP ファイル形式のファイルです。APK Analyzer では、各ファイルまたはフォルダが、フォルダへの移動に使用できる拡張機能を備えたエンティティとして表示されます。エンティティの階層は、APK ファイルのファイルとフォルダの構造を反映したものです。

図 1 に示すように、APK Analyzer には、各エンティティの未加工のファイルサイズとダウンロード ファイルサイズの値が表示されます。[Raw File Size] にはディスク上でのエンティティの解凍サイズが表示されるのに対し、[Download Size] には Google Play から配信される際のエンティティの推定圧縮サイズが表示されます。[% of Total Download Size] は、APK の総ダウンロード サイズにおいてエンティティが占める割合を示します。

図 1. APK Analyzer に表示されるファイルサイズ

AndroidManifest.xml の表示

プロジェクトに複数の AndroidManifest.xml ファイル(プロダクト フレーバー用など)が含まれている場合、または、マニフェスト ファイルを提供するライブラリが含まれている場合、それらが APK 内の 1 つのファイルに統合されます。このマニフェスト ファイルは、通常は APK 内のバイナリファイルですが、APK Analyzer で選択されると、このエンティティが XML 形式で再構築されて表示されます。このビューアを使用すると、ビルド中にアプリに対して行われた変更をすべて把握できます。たとえば、アプリケーションが依存するライブラリの AndroidManifest.xml ファイルが最終的な AndroidManifest.xml ファイルにどのように統合されたかを確認できます。

また、このビューアには lint 機能がいくつか搭載されており、右上隅に警告やエラーが表示されます。図 2 では、選択したマニフェスト ファイルにエラーがあることが示されています。

図 2. 選択したマニフェスト ファイルに対するエラーアイコンが右端に表示されている

DEX ファイルの表示

APK Analyzer の DEX ファイル ビューアでは、アプリの DEX ファイルに関する基本情報にすばやくアクセスできます。ビューア内にクラス数、パッケージ数、総参照数、宣言数が表示され、これらを基にして、multidex を使用すべきかどうかや、依存関係を削除して 64K の DEX の制限を超えないようにする方法を決定できます。

図 3 は、64K の DEX の制限を下回る中くらいのサイズのアプリを示しています。DEX ファイル内の各パッケージ、クラス、メソッドの数が、[Defined Methods] 列と [Referenced Methods] 列に示されています。[Referenced Methods] 列は、DEX ファイルによって参照されているメソッドの総数です。これには通常、コード、依存関係ライブラリで定義されたメソッドと、コードで使用される標準の Java および Android パッケージで定義されたメソッドが含まれます。これらのメソッドは、各 DEX ファイルの 64K のメソッドの制限にカウントされます。[Defined Methods] 列は、DEX ファイルのいずれかで定義されたメソッドのみの数です。つまりこの数は、[Referenced Methods] の部分集合です。依存関係を APK にパッケージ化すると、依存関係で定義されたメソッドが両方のメソッド数に加算されます。また、軽量化と Proguard 圧縮により、ソースコードのコンパイル後に DEX ファイルの内容が大幅に変更されることがあります。

図 3. 中くらいのサイズのアプリ

DEX ファイル ツリービューのフィルタ

APK Analyzer では、[Class] リストのすぐ上にあるフィルタを、選択した DEX ファイルの内容の表示に適用できます。

図 4. BuildConfig のフィールドとメソッドを表示するように設定された DEX フィルタ

フィルタを使用してクラス内のすべてのメソッドとフィールドを表示する手順は次のとおりです。

  1. [File] リストで classes.dex ファイルを選択します。
  2. [Class] リストで、特定のクラスに移動して選択します。
  3. 選択したクラスを展開します。
  4. [Show fields] で、クラス フィールドの表示と非表示を切り替えます。
  5. [Show methods] で、クラスメソッドの表示と非表示を切り替えます。
  6. [Show all referenced methods or fields] で、参照されているパッケージ、クラス、メソッド、フィールドの表示と非表示を切り替えます。ツリービュー内の斜体で示されたノードは、選択した DEX ファイルで定義されていない参照です。

    DEX ファイルは、別のファイルで定義されているメソッドとフィールドを参照できます。たとえば System.out.println() は、Android フレームワークの println() への参照です。

Proguard マッピングの読み込み

フィルタ アイコンの横には Proguard マッピング アイコンがあります。これらのアイコンは、DEX ビューアに機能を追加する一連の Proguard マッピング ファイル(名前の難読化解除(mapping.txt)、削除されたノードの表示(usage.txt)、削除できないノードの指定(seeds.txt)など)を読み込むまでグレー表示されています。Proguard マッピング ファイルは、Proguard が有効な状態でビルドされた APK に適用され、また、APK を生成したのと同じビルドに由来するものである必要があります。

図 5. Proguard マッピングの読み込み

Proguard マッピング ファイルを読み込む手順は次のとおりです。

  1. [Load Proguard Mappings] をクリックします。
  2. マッピング ファイルが格納されているプロジェクト フォルダに移動し、すべてのファイル、ファイルのすべての組み合わせ、またはファイルが格納されているフォルダを読み込みます。

    通常、マッピング ファイルは project/app/build/outputs/mappings/release/ にあります。ファイル選択ツールがこのプロジェクト構造を検出すると、デフォルトで release フォルダに移動します。ファイル選択ツールはまず、mapping.txtseeds.txtusage.txt と完全に一致するファイル名があるかチェックします。次に、mappingusageseeds のようなテキストを含むファイル名や、.txt で終わるファイル名があるかチェックします。たとえば、release-seeds-1.10.15.txt は一致します。

ここで、マッピング ファイルについて説明します。

  • seeds.txt: Proguard の設定により圧縮時に削除されないノードが太字で表示されます。
  • mapping.txt: [Deobfuscate names] を有効にすると、Proguard によって難読化されたノードの元の名前を復元できます。たとえば、abc のように難読化されたノード名を MyClassMainActivitymyMethod() に復元できます。
  • usage.txt: [Show removed nodes] を有効にすると、圧縮時に Proguard によって削除されたクラス、メソッド、フィールドを表示できます。復元されたノードは取り消し線付きで表示されます。

    Proguard を使用してコードを難読化および最小化する方法について詳しくは、コードとリソースの圧縮をご覧ください。

バイトコードの表示、使用箇所の検索、保持ルールの生成

[Class] リストに表示されるノードには、以下のオプションから成るコンテキスト メニューがあります。これらのオプションを選択すると、バイトコードの表示、使用箇所の検索、選択したノードをコピーして貼り付けることができる Proguard ルールを表示するダイアログの表示が可能になります。[Class] リスト表示で任意のノードを右クリックすると、そのコンテキスト メニューが表示されます。

Show bytecode: 選択したクラス、メソッド、フィールドを逆コンパイルし、次のようにダイアログに smali(Java コードではない)バイトコード表現を表示します。

図 6. init メソッドの DEX バイトコード

Find usages: 選択したクラスまたはメソッドへの参照を持つ DEX コードの他の部分を表示します(図 7)。seeds.txt を読み込み済みの場合、太字で表示されるノードは、Proguard の設定により圧縮時に削除されないことを示します。

図 7. MyClass への参照

Generate Proguard Keep rule: Proguard の圧縮フェーズ中に特定のパッケージ、クラス、メソッド、フィールドが削除されないようにするために、プロジェクトの Proguard 構成ファイルにコピーして貼り付けることができる Proguard ルールを表示します(図 8)。詳しくは、保持するコードのカスタマイズをご覧ください。

図 8. ダイアログから Proguard 構成ファイルにコピーできる Proguard ルール

コードおよびリソース エンティティの表示

APK ファイル内の最終エンティティはさまざまなビルドタスクによって変更されます。たとえば、Proguard 圧縮ルールによって最終的なコードが変更されることがあります。また、画像リソースがプロダクト フレーバーのリソースによって上書きされることもあります。APK Analyzer では、ファイルの最終バージョンを簡単に表示できます。エンティティをクリックすると、図 9 に示すように、テキストまたは画像のエンティティのプレビューが表示されます。

図 9. 最終的な画像リソースのプレビュー

APK Analyzer では、各種のテキスト ファイルやバイナリ ファイルも表示できます。たとえば、resources.arsc エンティティ ビューアを使用すると、文字列リソースの言語翻訳など、設定固有の値を表示できます。図 10 では、各文字列リソースの翻訳が表示されています。

図 10. 翻訳された文字列リソースのプレビュー

APK ファイルの比較

APK Analyzer では、2 つの APK ファイル内のエンティティのサイズを比較できます。この機能は、以前のリリースに比べてアプリのサイズが大きくなった理由を把握する必要がある場合に役立ちます。更新された APK を公開する前に、以下の操作を行います。

  1. 公開しようとしている APK のバージョンを APK Analyzer にロードします。
  2. APK Analyzer の右上にある [Compare With] をクリックします。
  3. 選択ダイアログで、ユーザーに前回公開した APK を見つけて、[OK] をクリックします。

    図 11 のようなダイアログが表示され、更新版がユーザーに与える影響を評価できます。

図 11 は、特定のアプリのデバッグビルドとリリースビルドの違いを示しています。これらのビルドタイプ間で異なるビルド オプションが使用されていると、基盤となるエンティティが別々に変更されます。

図 11. デバッグ APK とリリース APK の違い