APK 構造#
アプリのサイズを小さくする方法を議論する前に、アプリ APK の構造を理解することが役立ちます。APK ファイルは、アプリを構成するすべてのファイルを含む Zip 圧縮ファイルで構成されています。これらのファイルには、Java クラスファイル、リソースファイル、およびコンパイルされたリソースを含むファイルが含まれます。
APK には以下のディレクトリが含まれています:
META-INF/
:CERT.SF
とCERT.RSA
の署名ファイル、およびMANIFEST.MF
のマニフェストファイルが含まれています。assets/
:アプリのリソースを含みます。アプリは AssetManager オブジェクトを使用してこれらのリソースを取得できます。res/
:resources.arsc
にコンパイルされていないリソースを含みます。lib/
:プロセッサ固有のソフトウェア層のコンパイル済みコードを含みます。このディレクトリには、armeabi
、armeabi-v7a
、arm64-v8a
、x86
、x86_64
、およびmips
の各プラットフォームタイプのサブディレクトリが含まれています。
APK には以下のファイルも含まれています。これらのファイルの中で、AndroidManifest.xml
のみが必須です。
resources.arsc
:コンパイルされたリソースを含みます。このファイルには、res/values/
フォルダのすべての構成の XML コンテンツが含まれています。パッケージツールはこの XML コンテンツを抽出し、バイナリ形式にコンパイルし、対応するコンテンツをアーカイブします。この内容には、言語文字列やスタイル、resources.arsc
ファイルに直接含まれていない内容(レイアウトファイルや画像など)のパスが含まれます。classes.dex
:Dalvik/ART 仮想マシンが理解できる DEX ファイル形式でコンパイルされたクラスを含みます。AndroidManifest.xml
:コア Android マニフェストファイルを含みます。このファイルには、アプリの名前、バージョン、アクセス許可、および参照されるライブラリファイルがリストされています。このファイルは Android のバイナリ XML 形式を使用しています。
リソースの数とサイズを減らす#
APK のサイズは、アプリの読み込み速度、使用されるメモリ量、および消費される電力に影響を与えます。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 {
{
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 は resConfig
および resConfigs
スタイルと defaultConfig
オプションを使用して、これらの情報をコンパイルシステムに渡します。その後、コンパイルシステムは、サポートされていない他の構成からのリソースが APK に表示されるのを防ぎ、APK のサイズを小さくします。この機能の詳細については、未使用の代替リソースを削除する を参照してください。
ライブラリ内のリソース使用を減らす#
Android アプリを開発する際、アプリの使いやすさや多機能性を向上させるために外部ライブラリを使用する必要があります。たとえば、Android サポートライブラリ を参照して古いデバイスでのユーザー体験を向上させたり、Google Play サービス を使用してアプリ内のテキストの自動翻訳を取得したりできます。
ライブラリがサーバーやデスクトップデバイス向けに設計されている場合、アプリが必要としない多くのオブジェクトやメソッドが含まれている可能性があります。アプリに必要なライブラリの部分のみを含めるには、ライブラリのファイルを編集することができます(該当するライセンスがライブラリの変更を許可している場合)。また、アプリに特定の機能を追加するために、他のモバイルデバイス向けのライブラリを使用することもできます。
注意:ProGuard は、ライブラリのインポートに伴う不要なコードをクリーンアップできますが、ライブラリの大規模な内部依存関係を削除することはできません。
特定の密度のみをサポートする#
Android は非常に幅広いデバイス(さまざまな画面密度を含む)をサポートしています。Android 4.4(API レベル 19)以降、フレームワークはさまざまな密度をサポートしています:ldpi
、mdpi
、tvdpi
、hdpi
、xhdpi
、xxhdpi
、および xxxhdpi
。Android はこれらすべての密度をサポートしていますが、すべての密度にラスタライズされたリソースをエクスポートする必要はありません。
特定の密度のデバイスを持つユーザーが少数であることがわかっている場合は、これらの密度をアプリにバンドルする必要があるかどうかを検討してください。特定の画面密度用のリソースを追加しない場合、Android は他の画面密度用に最初に設計された既存のリソースを自動的に拡大または縮小します。
アプリが拡大縮小された画像のみを必要とする場合、drawable-nodpi/
に画像の単一のバリアントを使用することで、さらに多くのスペースを節約できます。各アプリには少なくとも 1 つの xxhdpi
画像バリアントを含めることをお勧めします。
画面密度の詳細については、画面サイズと密度 を参照してください。
Drawable オブジェクトを使用する#
特定の画像には静的な画像リソースは必要ありません。フレームワークは、実行時に画像を動的に描画することができます。Drawable オブジェクト(XML では <shape>
)は、APK 内で少量のスペースを占有します。さらに、XML Drawable オブジェクトは、Material Design ガイドラインに準拠した単色画像を生成します。
リソースの再利用#
画像のバリアントに対して個別のリソースを追加することができます。たとえば、同じ画像の色調調整、影設定、または回転されたバージョンです。ただし、同じリソースセットを再利用し、実行時に必要に応じてカスタマイズすることをお勧めします。
Android はリソースの色を変更するためのいくつかのユーティリティを提供しており、各ユーティリティは Android 5.0(API レベル 21)以降で android:tint
および tintMode
属性を使用します。古いバージョンのプラットフォームでは、ColorFilter クラスを使用します。
別のリソースの回転等価物のリソースを省略することもできます。以下のコードスニペットは、画像の中心位置を回転させることで「親指を上に」を「親指を下に」に変える例を示しています。
<?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 で PNG ファイルに対してこのプロセスを無効にするためにcruncherEnabled
タグを使用できます:aaptOptions { cruncherEnabled = false }
PNG および JPEG ファイルを圧縮する#
pngcrush、pngquant または zopflipng などのツールを使用して、画質を損なうことなく PNG ファイルのサイズを小さくすることができます。これらのツールはすべて、目に見える画質を維持しながら PNG ファイルのサイズを小さくすることができます。
特に pngcrush
ツールは効果的です。このツールは、PNG フィルタと zlib(Deflate)パラメータを反復処理し、フィルタとパラメータの各組み合わせを使用して画像を圧縮します。その後、最小の圧縮出力を生成する構成を選択します。
JPEG ファイルを圧縮するには、packJPG や guetzli などのツールを使用できます。
WebP ファイル形式を使用する#
Android 3.2(API レベル 13)以降をターゲットにする場合、PNG または JPEG ファイルの代わりに WebP ファイル形式の画像を使用できます。WebP 形式は、JPEG のような損失圧縮と PNG のような透明度を提供しますが、JPEG や PNG と比較して、より優れた圧縮効果を提供します。
Android Studio を使用して、既存の BMP、JPG、PNG または静的 GIF 画像を WebP 形式に変換できます。詳細については、Android Studio を使用して WebP 画像を作成する を参照してください。
注意:Google Play は、ランチャーアイコン が PNG 形式である場合にのみ APK を受け入れます。
ベクターグラフィックスを使用する#
ベクターグラフィックスを使用して、解像度に依存しないアイコンやその他のスケーラブルなメディアを作成できます。これらのグラフィックスを使用すると、APK が占めるスペースを大幅に削減できます。ベクター画像は、Android で VectorDrawable オブジェクトの形式で表されます。VectorDrawable オブジェクトを使用すると、100 バイトのファイルから画面サイズに合わせた鮮明な画像を生成できます。
ただし、システムが各 VectorDrawable オブジェクトをレンダリングするには多くの時間がかかり、大きな画像は画面に表示されるまでにさらに長い時間がかかります。したがって、小さな画像を表示する場合にのみ、これらのベクターグラフィックスを使用することを検討してください。
VectorDrawable オブジェクトの使用に関する詳細は、Drawable リソースの使用 を参照してください。
アニメーション画像にベクターグラフィックスを使用する
AnimationDrawable を使用してフレームアニメーションを作成しないでください。なぜなら、アニメーションの各フレームに個別のビットマップファイルを追加する必要があり、これが APK のサイズを大幅に増加させるからです。
代わりに、AnimatedVectorDrawableCompat を使用して アニメーションベクター可描画リソース を作成するべきです。
ネイティブおよび Java コードを減らす#
アプリ内の Java およびネイティブコードライブラリのサイズを小さくするために、さまざまな方法を使用できます。
不要な生成コードを削除する
自動生成されたコードが占めるスペースを理解していることを確認してください。たとえば、多くのプロトコルバッファツールは過剰なメソッドやクラスを生成し、アプリのサイズを倍増させる可能性があります。
列挙型の使用を避ける
単一の列挙型は、アプリの classes.dex
ファイルを約 1.0 ~ 1.4 KB 増加させます。これらの増加したサイズは急速に累積し、複雑なシステムや共有ライブラリを生成します。可能であれば、@IntDef
注釈を使用し、ProGuard を使用して列挙型を削除し、整数に変換することを検討してください。この型変換は、列挙型のさまざまな安全性の利点を保持します。
ネイティブバイナリファイルのサイズを小さくする
アプリがネイティブコードと Android NDK を使用している場合、コードを最適化することでリリース版アプリのサイズを小さくすることもできます。デバッグシンボルを削除し、ネイティブライブラリを抽出しないことは、非常に実用的な技術です。
デバッグシンボルを削除する#
アプリが開発中であり、デバッグが必要な場合、デバッグシンボルを使用することは非常に適切です。Android NDK に付属する arm-eabi-strip
ツールを使用して、ネイティブライブラリから不要なデバッグシンボルを削除できます。その後、リリース版をコンパイルできます。
ネイティブライブラリの解凍を避ける#
リリース版アプリをコンパイルする際、アプリのマニフェストの application 要素に android:extractNativeLibs="false"
を設定することで、APK に未圧縮の .so
ファイルをパッケージ化できます。このフラグを無効にすると、PackageManager がインストール中に .so
ファイルを APK からファイルシステムにコピーするのを防ぎ、アプリの更新を小さくする追加の利点があります。
複数のスリム APK を維持する#
APK には、ユーザーがダウンロードするが使用しないコンテンツ(他の言語や画面密度向けのリソースなど)が含まれている可能性があります。ユーザーに最小のダウンロードファイルを提供するために、アプリを Android App Bundle を使用して Google Play にアップロードするべきです。App Bundle をアップロードすることで、Google Play は各ユーザーのデバイス構成に基づいて最適化された APK を生成し提供できるため、ユーザーはアプリを実行するために必要なコードとリソースのみをダウンロードします。異なるデバイスをサポートするために複数の APK をコンパイル、署名、および管理する必要がなくなり、ユーザーはより小さく、最適化されたダウンロードファイルを受け取ることができます。
アプリを Google Play に公開する予定がない場合は、アプリを複数の APK に分割し、画面サイズや GPU テクスチャサポートなどの要因に基づいて区別することができます。
ユーザーがアプリをダウンロードすると、デバイスはデバイスの機能と設定に基づいて正しい APK を受け取ります。このようにして、デバイスはデバイスが持っていない機能のためのリソースを受け取ることはありません。たとえば、ユーザーが hdpi
デバイスを持っている場合、より高密度のディスプレイを持つデバイス向けに提供される可能性のある xxxhdpi
リソースは必要ありません。
詳細については、APK スプリットの構成 および 複数の APK を維持する を参照してください。
ネイティブサイズを減らす#
android {
...
defaultConfig {
...
// defaultConfig ノードの下に externalNativeBuild ブロックを設定
externalNativeBuild {
// ndk-build の場合は、ndkBuild {} を使用
cmake { // cmake のコンパイルオプションを指定
// C コンパイラのオプションフラグを設定します。
cFlags "-fvisibility=hidden -ffunction-sections -fdata-sections"
// C++ コンパイラのオプションフラグを設定します。
cppFlags "-fvisibility=hidden -ffunction-sections -fdata-sections -std=c++11"
arguments "-DANDROID_TOOLCHAIN=gcc"// gcc コンパイラを使用するように設定、cmake はデフォルトで clang を使用
}
}
}
buildTypes {...}
}