AndroidのProGuard難読化からモバイルSDKを除外するにはどうすればよいでしょうか? Android SDKを難読化から除外するには、まずライブラリにコンシューマーProGuardルールが同梱されているかを確認します。その上で、リフレクションでアクセスされるエントリーポイント、コールバックインターフェース、JNIブリッジ、データモデルに対してスコープを絞った-keepおよび-keepclassmembersディレクティブを追加し、R8診断レポートと生のDEX検査を通じて保持されたシンボルを監査する必要があります。Android開発チームが本番リリースビルドでコード圧縮と最適化を有効にすると、R8コンパイラは未参照のバイトコードを削除し、クラス、フィールド、メソッドを短い識別子にリネームします。モバイルアトリビューションSDK(Software Development Kit)が動的なリフレクションやJNIコールバックに依存している場合、積極的なツリーシェイキングやリネームからこれらを保護しないと、非圧縮のデバッグビルドですべてのテストに合格していても、本番環境でディファードディープリンク(deferred deep linking)、JSONモデルのデシリアライズ、チャネルパッケージビルドがサイレントに失敗する可能性があります。モバイルアトリビューションおよびディープリンクプラットフォームであるOpoInstallは、R8で圧縮されたリリースパイプライン内で開発チームが設定および検証できるネイティブAndroid SDKアーカイブとチャネルパッケージのワークフローを提供しています。
最新のAndroid Gradle Plugin(AGP)ツールチェーンでは、keepルールファイル(レガシーDSLの
proguard-rules.pro、またはAGP 9.3以降のsrc/<variant>/keepRules/*.keep)がGoogleのR8コンパイラに対するルール設定の規約として機能します。サードパーティ製Android SDKを難読化から安全に除外するとは、パッケージ全体の広範すぎるkeepルールや未検証の警告抑制を避けつつ、ランタイムに必要な特定のパブリックAPIエントリーポイント、リフレクションコンストラクタ、コールバックインターフェース、JNIアップコール、モデルフィールドのみを保持することを意味します。
| 用語 | 定義 | 関連エンティティ | 検索意図の役割 |
|---|---|---|---|
| Android SDK | インストールアトリビューションなどの専門機能を提供するためにAndroidアプリに組み込まれる、コンパイル済みのJavaまたはKotlinライブラリ。 | classes.dex バイトコード |
ナビゲーショナル / トランザクショナル |
| ProGuard / R8 | ProGuardルール構文を使用して、未使用コードの圧縮、バイトコードの最適化、識別子の難読化を行うAndroidのビルド時ツール。 | proguard-rules.pro |
テクニカル / インフォメーショナル |
| Consumer ProGuard Rules | .aar(/proguard.txt)または.jar(/META-INF/proguard/)内に同梱され、R8に自動的にマージされる難読化ルール。 |
consumerProguardFiles |
テクニカル / トラブルシューティング |
| R8 Diagnostic Reports | シンボルのリネーム、一致したkeepシード、削除されたコードの詳細を示す出力ファイル(mapping.txt、seeds.txt、usage.txt、configuration.txt)。 |
リリースビルド監査 | テクニカル / トラブルシューティング |
R8とProGuardの難読化によってAndroid SDKのアトリビューションコールバックが破損する理由

デバッグとリリースの罠:テスト環境で機能したアトリビューションが本番環境で失敗する理由
モバイルユーザー獲得におけるリリースエンジニアリングの最も一般的なリグレッション(機能不全)の一つは、アプリのディファードディープリンク(handleDeepLink)やインストールパラメータの復元(getInstallParam)が、デバッグビルドでのQAテスト中は確実に動作するのに、署名付きリリースAPKまたはAndroid App Bundle(.aab)としてコンパイルされた途端に、空のペイロードを返したりコールバックの発火を停止したりする現象です。
根本的な原因は、Gradleで設定されたビルドタイプ間の差異にあります。デフォルトでは、debugビルドタイプはインクリメンタルビルドを高速化するためにコード圧縮を無効にしています。エンジニアがR8最適化を有効にしてreleaseバリアントをコンパイルすると、Android Gradle PluginはAndroid R8コンパイラを呼び出し、ホストアプリとリンクされたサードパーティ製ライブラリの両方に対して3つのバイトコード変換を実行します。
- コード圧縮(ツリーシェイキング):R8は宣言されたエントリーポイント(
AndroidManifest.xmlのコンポーネントや明示的な-keepシードなど)からアプリケーションのコールグラフを解析し、静的解析で到達不能と判断されたクラス、コンストラクタ、メソッド、フィールドをすべて削除します。 - バイトコードの最適化とアクセス修飾子の変更:R8はメソッドのインライン化、未使用パラメータの削除、クラスのマージを行います。さらに、Androidのデフォルト最適化設定にある
-allowaccessmodificationディレクティブと組み合わせることで、クラスやメンバーの可視性フラグを変更し(インライン化中にprivateやパッケージプライベートのメンバーをpublicに広げるなど)、Dalvik Executable(.dex)のサイズを削減してランタイムの実行速度を向上させます。 - 識別子の難読化(リネームと再パッケージ化):R8は残ったクラス、パッケージ、フィールド、メソッドをコンパクトな識別子(
a.a.b()など)にリネームし、難読化されたクラスをフラット化されたパッケージ階層に再パッケージ化することで、classes.dex内の文字列プールのオーバーヘッドを削減します。
R8のフルモード(Full Mode)は、Android Gradle Plugin(AGP)8.0以降でデフォルトで有効になっています。従来のProGuard互換モードと比較して、R8フルモードははるかに厳格な最適化を適用します。関連するクラスとメンバーが一致するkeepルールによって明示的に保持されない限り、デフォルトの引数なしコンストラクタ(<init>())の削除、メンバーのアクセス修飾子の変更、メタデータ属性の削除が行われます。.aarのバイナリ構造とSDK選定基準の基礎知識については、関連ガイドであるモバイルSDKの定義と技術的グロースプロトコルの理解および正確なアトリビューションのためのSDK(ソフトウェア開発キット)の選び方をご参照ください。
難読化されたアトリビューションSDKにおける6つの異なる障害モード

静的なコールグラフ解析では動的なランタイム検索を予測できないため、保護されていないAndroid SDKはR8フルモード下で6つの異なる障害モードを引き起こす可能性があります。それぞれに特定のProGuard保護ターゲットが必要となるため、エンジニアはこれらの障害モードを正確に区別する必要があります。
| ランタイム障害モード | 根底にあるR8バイトコードの原因 | 必要な具体的保護ターゲット |
|---|---|---|
1. リフレクションによるクラス検索の失敗 (ClassNotFoundException) |
Class.forName()を介して文字列名で動的にロードされるクラスを、R8がリネーム、再パッケージ化、または削除する |
-keep class ...を使用して、ターゲットクラスとその元の識別子を保持する |
2. リフレクションによるオブジェクトインスタンス化の失敗 (NoSuchMethodException: <init>) |
R8フルモードでは、クラスが保持されていても、コード内で直接呼び出されていない場合、デフォルトの引数なしコンストラクタ(<init>())が削除される |
クラスの-keepブロック内でコンストラクタシグネチャ(<init>();)を明示的に保持する |
| 3. 動的コールバックおよびアクセス修飾子変更の失敗 | R8がコールバックインターフェースのメソッドを削除・リネームするか、リフレクションのgetDeclaredMethod() / getDeclaredField()呼び出しが期待するメンバーの可視性フラグを変更する |
コールバックインターフェースのメソッド(-keep interface ... { <methods>; })と、リフレクションでアクセスされるすべてのメンバーを保持する |
| 4. コールバックリスナーにおけるジェネリックシグネチャメタデータの消失 | Javaのジェネリクスはコンパイル時に型消去されますが、バイトコードのSignature属性はランタイムリフレクション(ResultCallBack<OpoData>)のためにジェネリックメタデータを保持します。R8フルモードは、明示的に保持されない限りSignatureメタデータを削除します |
ジェネリックコールバックインターフェースとターゲットモデルクラスの両方を保持することに加えて、-keepattributes Signatureを保持する |
| 5. JSONデータモデルのデシリアライズ失敗 | R8がモデルクラスを削除する(リフレクションでのみインスタンス化される場合)か、フィールドをa、b、cにリネームするため、リフレクションベースのJSONパーサーがnullまたは空のフィールドを返す |
モデルが動的にインスタンス化される場合は-keep class ...を使用し、直接参照によってクラスがすでに保持されている場合は-keepclassmembersを使用する |
6. JNIダウンコールおよびネイティブからJavaへのアップコールの削除 (UnsatisfiedLinkError / NoSuchMethodError) |
デフォルトのAndroidルールは残存するJavaのnativeダウンコールの名前を保持しますが、未使用のRegisterNativesメソッドが圧縮されるのを防ぐことはできず、C/C++の.soコードがJNI(FindClass、GetMethodID)を介してJavaにコールバックするのを認識できません |
動的に登録されたnative宣言、およびC/C++からのJNIアップコールを介して呼び出されるJavaクラス、コンストラクタ、メソッドを明示的に-keepする |
[Android Release Build: R8 Full Mode Enabled]
|
v
[R8 Evaluates Bundled & Host Keep Rules]
|
+---------------+---------------+
| |
v (Missing / Mismatched Rules) v (Scoped Keep Rules + Attributes Active)
[SDK Symbols Stripped or Renamed] [Required SDK Entry Points & Models Preserved]
- Reflective () Removed - Confirmed in -printseeds (seeds.txt)
- JNI Upcall / Model Fields Lost - Verified in Raw DEX (apkanalyzer dex packages)
- Generic Signature Metadata Lost - Release Attribution Matches Debug Behavior
AARバンドル内のコンシューマールールと手動JARインポートの比較
AARおよびJARライブラリがコンシューマーProGuardルールを同梱する仕組み

ホストアプリケーションに手動の除外ルールを追加する前に、サードパーティ製Android SDKに組み込みのR8ルールがすでに同梱されているかどうかを確認してください。ライブラリの最適化に関するGoogle Androidデベロッパーガイドに記載されているとおり、AGPとR8は、バージョンをターゲットとしたR8ルールを含め、.aarと.jarの両方の配布形式にわたってコンシューマールールの自動検出をサポートしています。
| SDK配布形式 | 埋め込みコンシューマールールの場所 | Android Gradle Plugin / R8のビルド動作 |
|---|---|---|
Android Archive (.aar) |
AARルートの/proguard.txt(consumerProguardFiles経由)、または/classes.jar内の/META-INF/com.android.tools/r8-from-<X>-upto-<Y>/配下にあるバージョンターゲットルール |
コンパイル時に自動的に抽出され、ホストアプリのR8設定にマージされる |
ルール同梱のJava Archive (.jar) |
/META-INF/proguard/*.pro または /META-INF/com.android.tools/r8-from-<X>-upto-<Y>/(r8、r8-from-<X>、r8-upto-<Y>バリアントを含む) |
アクティブなR8コンパイラのバージョンに応じて、AGPとR8によって自動的に検出・適用される |
ルールが同梱されていないスタンドアロンの.jarまたは.aar |
アーカイブ内に存在しない | 同梱されたコンシューマー保護は提供されません。ランタイムリフレクション、JNI、シリアライズ、またはその他の動的にアクセスされるシンボルが明示的な保持を必要とする場合にのみ、ホストレベルのkeepルールを追加してください |
(Gsonのシリアライズルールに関する注記:ProGuardおよびR8向けGsonトラブルシューティングガイドに記載されているとおり、Gson 2.11.0以降では、TypeTokenおよび@SerializedNameアノテーションが付与されたフィールド用のR8ルールが同梱されています。ただし、これらのルールがあるからといって、リフレクションによるモデルアクセスの制限、互換性のある引数なしコンストラクタの提供、または圧縮されたリリースビルドでのすべてのシリアライズ・デシリアライズ経路のテストが不要になるわけではありません。)
ルールを記述する前にダウンロードしたSDKアーカイブを検査する
ProGuardとR8のルールマッチングは、大文字と小文字を厳密に区別し、パッケージパスが完全に一致している必要があります。SDKのバージョンや地域別の配布物によってパッケージの名前空間が異なる場合があり、古いチュートリアルからコピーしたルールのパッケージの大文字・小文字がアーカイブ内の実際の.classファイルと一致しない場合、R8は警告を出すことなく一致クラスをゼロとして処理します。
keepルールを編集する前に、ターゲットのSDKパッケージ(モバイルSDKダウンロードセンターのAndroid SDK v2.1.0など)をダウンロードし、正確な.class階層とともに、埋め込まれているルール(/proguard.txtとclasses.jar内の両方)を検査してください。
# 1. Inspect an .aar archive for root consumer rules (proguard.txt) and extract classes.jar to check META-INF rules
unzip -l opoinstall-android-sdk.aar
unzip -p opoinstall-android-sdk.aar proguard.txt
unzip -p opoinstall-android-sdk.aar classes.jar > temp_classes.jar
jar tf temp_classes.jar | grep -E "META-INF/proguard/|META-INF/com\.android\.tools/|\.class$"
# 2. If using a standalone .jar archive, inspect embedded META-INF rules and exact case-sensitive .class paths
jar tf libs/OpoInstall_SDK.jar | grep -E "META-INF/proguard/|META-INF/com\.android\.tools/|\.class$"
アトリビューションAndroid SDK向けのproguard-rules.proの設定
パッケージ全体のKeepアンチパターンと包括的な警告抑制の回避
カスタムルールを記述する際、エンジニアは最適化を不必要に無効化しないよう、Googleの保持するコードの選択(R8 Keepルール)のベストプラクティスに従う必要があります。
- パッケージ全体の
-keep class package.** { *; }アンチパターンよりもスコープを絞ったルールを優先する:包括的なパッケージ全体のルールを使用すると、R8はそのパッケージ内のすべての内部ヘルパークラスを保持し、最適化をスキップしてしまいます。ランタイムに動的にアクセスされる特定のクラス、コンストラクタ(<init>();)、コールバックインターフェース、JNIターゲット、モデルフィールドにルールを限定してください(または、自身が管理するコードではJetpackの@UnconditionallyKeepアノテーションを使用してください)。 - データモデルにおける
-keepと-keepclassmembersを区別する:-keepclassmembersは、それを含むクラス自体がコールグラフ内に保持されている場合にのみ、指定されたフィールドやメソッドを保持します。データモデル(OpoDataなど)がリフレクションのみを介してインスタンス化される場合や、型消去されたジェネリック型のみを介して参照される場合は、R8がクラス自体を削除しないように-keep class ...を使用してください。 - JNIにおける
-keepclasseswithmembernamesの制限を理解する:Androidのデフォルト最適化ルールには-keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; }が含まれています。GoogleのR8 KeepルールドキュメントおよびKeepディレクティブに関するProGuardマニュアルで定義されているとおり、-keepclasseswithmembernamesは-keepclasseswithmembers,allowshrinkingと同等です。これは圧縮フェーズを生き残った一致するクラスとメンバーの名前を保持しますが、未参照のクラスやnativeメソッドが圧縮中に削除されるのを防ぐことはできません。Javaからの直接の呼び出し箇所がなくRegisterNativesを介してネイティブメソッドが動的に登録される場合、またはC/C++コードがJNIアップコールを介してJavaメソッドを呼び出す場合は、ターゲットクラスのインスタンス化方法に応じて明示的な-keepまたは-keepclassmembersルールを使用する必要があります。 - 未検証の包括的な
-dontwarnディレクティブを避ける:-dontwarnは圧縮やリネームに対する保護を一切提供せず、ランタイムのNoClassDefFoundErrorクラッシュを隠蔽する可能性のある未解決の参照警告を抑制してしまいます。ビルドログで特定のコンパイル専用の欠落クラスが報告され、それがランタイムにオプションであることを確認した場合にのみ、スコープを絞った-dontwarnを追加してください。
| ProGuard / R8 ディレクティブ | 同等の完全構文 | 圧縮(削除) | 難読化(リネーム) | 推奨されるエンジニアリングスコープ |
|---|---|---|---|---|
-keep class ... |
-keep |
防止される(クラスと指定メンバーを保持) | 防止される(元の名前を保持) | パブリックSDKエントリーポイント、コールバックインターフェース、リフレクションまたはJNIを介して動的にインスタンス化されるクラス(<init>(...);を含む) |
-keepclassmembers class ... |
-keepclassmembers |
クラスが保持された場合のみメンバーを保持 | 一致したメンバーで防止される | Java/Kotlinで直接インスタンス化されているクラス上の<fields>;またはJNIアップコールメソッドの保持 |
-keepclasseswithmembernames |
-keepclasseswithmembers,allowshrinking |
未参照の場合は許可される(allowshrinking) |
クラスとメンバーが圧縮を生き残った場合に防止される | Javaコードがnativeメソッドを直接呼び出す場合のデフォルトのJNIダウンコール名の保持(native <methods>;) |
-keepnames class ... |
-keep,allowshrinking |
未参照の場合は許可される(allowshrinking) |
クラスが保持された場合に防止される | 未使用のクラスをclasses.dexに強制的に含めることなく、スタックトレース用に読み取り可能なクラス名を保持する |
-keepattributes ... |
-keepattributes |
保持されたアイテムのメタデータを保持 | 該当なし(バイトコード属性) | 明示的に保持されたクラス上のSignature(ジェネリクス)、InnerClasses、EnclosingMethod、*Annotation*の保持 |
-dontwarn <specific_class> |
-dontwarn |
圧縮には影響しない | リネームには影響しない | 検証済みのオプションであるコンパイル専用の依存関係に対してのみ、コンパイル時の警告を抑制する |
AGP 8.xおよびAGP 9.3以降のツールチェーンにおけるGradleの設定
Android Gradle Plugin 9.3では、簡素化されたoptimization DSLブロックとバリアント固有のkeepRulesディレクトリ(.keepファイル)が導入されました。一方、AGP 8.xから9.2(およびAGP 9.3以降でも引き続きサポートされているレガシー設定)では、isMinifyEnabledとproguardFilesを使用します。エンジニアは以下の3つの設定オブジェクトを明確に区別しておく必要があります。
| 設定オブジェクト | ファイルまたはDSLの場所 | R8ビルドにおけるアーキテクチャ上の役割 |
|---|---|---|
| レガシーDSL Keepルール (AGP 8.x–9.2 & レガシー 9.3+) | app/proguard-rules.pro(proguardFiles(...)を介して参照) |
getDefaultProguardFile("proguard-android-optimize.txt")とともにホストのkeepルールを宣言する |
| 新しいOptimization DSL Keepルール (AGP 9.3+) | app/src/main/keepRules/*.keep または app/src/<variant>/keepRules/*.keep |
optimization { enable = true }を使用する際に.keepファイルを宣言する(デフォルトのAndroidプラットフォームルールは自動的に適用されます。レガシーDSLも.keepソースセットをサポートしています) |
| R8診断レポートディレクティブ | 検証済みのR8設定ファイル(proguard-rules.pro)内の-printseeds、-printusage、-printconfiguration |
R8に対してapp/build/outputs/mapping/<buildVariant>/に監査レポートを出力するよう指示する |
// Option A: AGP 8.x - 9.2 Legacy DSL (also supported in AGP 9.3+) — app/build.gradle.kts
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
// Option B: AGP 9.3+ Simplified Optimization DSL — app/build.gradle.kts
// Place scoped class/member keep rules in app/src/main/keepRules/sdk-attribution.keep
android {
buildTypes {
release {
optimization {
enable = true
}
}
}
}
スコープを絞ったKeepルールと診断ディレクティブの例
OpoInstallの公開ドキュメントや過去のFAQページでは異なるパッケージプレフィックス(com.opoinstall.api.**、com.fm.OpoInstall.**、またはio.OpoInstall.**)が参照されているため、以下のルールセットはあくまで例示であり、OpoInstall SDK v2.1.0用の検証済み本番ルールセットではありません。まずダウンロードしたv2.1.0アーカイブ内の実際のクラスパスと同梱されているコンシューマールールを検査し、OpoInstall技術ドキュメントを参照してください(基礎となるJavaライフサイクルコードについては、SDKがモバイルアトリビューションとグロースを支える仕組みをご覧ください)。
# File: app/src/main/keepRules/sdk-attribution.keep (or app/proguard-rules.pro)
# IMPORTANT: Illustrative only — not a verified production rule set for OpoInstall SDK v2.1.0.
# Verify exact case-sensitive package and class names inside your downloaded v2.1.0 archive
# via `jar tf` or `apkanalyzer`, and check `configuration.txt` for existing consumer rules.
# ---------------------------------------------------------------------------
# 1. Preserve Bytecode Attributes on Explicitly Kept Classes
# Required so generic callback interfaces (e.g., ResultCallBack<OpoData>)
# retain their parameterized type metadata in R8 Full Mode.
# ---------------------------------------------------------------------------
-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*
# ---------------------------------------------------------------------------
# 2. Scoped SDK Entry Point, Callback Interface, and Data Model Rules
# ---------------------------------------------------------------------------
# Preserve public SDK entry point and any reflectively invoked constructors/methods:
-keep public class com.opoinstall.api.OpoInstall {
<init>(...);
public <methods>;
}
# Preserve callback listener interfaces and their callback methods:
-keep public interface com.opoinstall.api.ResultCallBack {
public <methods>;
}
# Model Scenario A: If OpoData / OpoError models are instantiated dynamically via reflection
# or referenced only via erased generics, use -keep to preserve both the class and its members:
-keep class com.opoinstall.api.OpoData {
<init>(...);
<fields>;
public <methods>;
}
-keep class com.opoinstall.api.OpoError {
<init>(...);
<fields>;
public <methods>;
}
# Model Scenario B: If a model class is already directly retained in your host call graph
# and only its fields require protection from renaming, use -keepclassmembers:
# -keepclassmembers class com.opoinstall.api.OpoData { <fields>; }
# ---------------------------------------------------------------------------
# 3. JNI Downcall and Native-to-Java Upcall Protection (Illustrative JNI Patterns)
# ---------------------------------------------------------------------------
# JNI Case A: If Java/Kotlin code already directly instantiates the JNI bridge class,
# use -keepclassmembers,includedescriptorclasses for C/C++ upcall methods, and keep
# any parameter model classes/constructors instantiated by native code:
-keepclassmembers,includedescriptorclasses class com.example.sdk.internal.JniBridge {
public void onNativeAttributionEvent(com.example.sdk.internal.NativeEventData);
}
-keep class com.example.sdk.internal.NativeEventData {
<init>(...);
}
# JNI Case B: If native C/C++ code dynamically looks up or instantiates the Java class
# itself via FindClass / NewObject, or registers native methods via RegisterNatives
# without direct Java call sites, use -keep to prevent class and method removal:
-keep,includedescriptorclasses class com.example.sdk.internal.DynamicNativeBridge {
<init>(...);
public native <methods>;
public void onNativeCallback(java.lang.String);
}
# File: app/proguard-rules.pro (Diagnostic Output Directives)
# Place R8 diagnostic report flags in your verified R8 configuration file.
# Paths resolve relative to the module working directory (`app/`).
# Adjust the directory name for product flavors (e.g., build/outputs/mapping/prodRelease/).
-printseeds build/outputs/mapping/release/seeds.txt
-printusage build/outputs/mapping/release/usage.txt
-printconfiguration build/outputs/mapping/release/configuration.txt
Androidチャネルパッケージの難読化と堅牢化(Hardening)の競合を解決する
ビルド時のR8コンパイル、APKアライメント、署名、チャネルパッケージの分離
Google Play以外のAndroid配布向けに、OpoInstallは専用のAndroidチャネルパッケージのエクスポートをサポートしています。これは、開発者がアップロードしたベースAPKを受け取り、個別のchannelCodeトラッキングメタデータを持つチャネル固有のAPKを生成する機能です。
エクスポートされたチャネルパッケージでチャネル統計の欠落やインストールの失敗をトラブルシューティングする際、エンジニアは2つの独立したリリース検証基準、すなわち暗号化されたAPK署名の有効性とベンダーのチャネルメタデータの整合性を切り離して考える必要があります。
- ビルド時のR8コード圧縮:R8は、ベースAPKがパッケージ化されて署名される前に、Java/Kotlinバイトコードを
classes.dexにコンパイルする際、Gradleビルドパイプライン内で実行されます。すでにエクスポートされたAPKに対してR8が実行されることは決してありません。 - Android APKアライメント(
zipalign)と暗号署名(apksigner):AGPによってビルドされた標準のリリースAPKは自動的にアライメントされます。ただし、リリースパイプラインで署名前にカスタムの後処理やサードパーティ製のAPK保護(強化)を適用する場合は、公式のAndroidzipalign仕様およびAndroidapksigner仕様に従ってAPKを再アライメントする必要があります。zipalignは非圧縮データを4バイト境界に配置します。APKに非圧縮のネイティブ.soライブラリが含まれている場合、チームは16 KBページアライメント要件を満たすためにzipalign -P 16 -f -v 4 infile.apk outfile.apkを使用し、zipalign -c -P 16 -v 4でアライメントを検証する必要があります。署名後にAPKを変更するとV2/V3のAPK全体の暗号署名が破壊されるため、zipalignはapksignerで署名する前に実行しなければなりません。 - ベンダー固有のチャネルメタデータと署名の検証:OpoInstallコンソールからチャネルAPKをエクスポートした後に、サードパーティ製のAPK保護(パッキング)や再パッケージ化ツールを実行すると、埋め込まれたチャネルメタデータが上書きされたり、APK署名が無効になったりする可能性があります。サードパーティ製のチャネルパッケージパーサーが特定のAPK署名スキーム(V2/V3のAPK全体署名と並行してV1 JAR署名を有効にするなど)を必要とする場合は、本番の署名設定を変更する前に、現在のOpoInstallコンソールドキュメントまたはベンダーサポートに直接その要件を確認してください。V1署名を普遍的なAndroid OSの要件として扱わないでください。
Androidチャネルパッケージの推奨リリースパイプライン順序
有効なAPK署名、R8バイトコードの最適化、および完全なベンダーチャネルメタデータを維持するには、リリースパイプラインを以下の厳密な順序で実行してください。
- R8によるベースAPKのコンパイルと圧縮:検証済みのkeepルールを使用して
./gradlew :app:assembleReleaseを実行し、R8がビルド時にclasses.dexを最適化するようにします。 - アップロード前の堅牢化、アライメント、署名の完了:セキュリティチームがサードパーティ製のAPK保護ツールを使用している場合は、それをベースAPKに適用し、(署名前に再アライメントが必要な場合は)
zipalign -P 16 -f -v 4を実行し、zipalign -c -P 16 -v 4でアライメントを検証してから、ベースAPKに署名します。 - ベースAPKのアップロードとエクスポートされたチャネルAPKの個別検証:署名および難読化されたベースAPKをOpoInstallコンソールにアップロードし、チャネルAPKをエクスポートします。一般公開する前に、エクスポートされたAPKに対して暗号署名の有効性(
apksigner verify --verbose)と実機でのチャネルアトリビューションの両方を個別に検証します。
[Recommended Channel Package Pipeline]: Source Code -> Gradle R8 Minification -> (Optional Pre-Upload Hardening + zipalign -P 16 + Signing) -> Upload Base APK to Console -> Export Channel APK -> Verify Both Signature (apksigner) & Channel Callback
R8レポートとAPK Analyzerを使用した保持SDKシンボルの監査
AGP 9.3以降でのスタンドアロンR8設定アナライザーの使用
Android Gradle Plugin 9.3以降では、keepルールがどのようにマージされ一致するかを監査するために、完全なリリースAPKやApp Bundleのコンパイルを待つ必要はありません。AGP 9.3以降では、専用のanalyze<Variant>R8Config Gradleタスク(たとえば、./gradlew :app:analyzeReleaseR8Config)が提供されており、コンシューマールールとホストのkeepルールを迅速に評価して設定の診断結果を出力するため、エンジニアはフルリリースビルドを実行する前に.keepルールの調整を反復して行うことができます。
configuration.txt、seeds.txt、usage.txt、mapping.txtのクロスチェック

./gradlew :app:assembleReleaseがビルドエラーなしで完了したという理由だけで、-keepルールが機能したと想定してはいけません。設定を確実に検証するには、app/build/outputs/mapping/<buildVariant>/に生成される4つのR8診断ファイルを検査してください(特定のシンボルが保持された理由をデバッグする場合は、-whyareyoukeeping class <ClassName>を使用します)。
configuration.txt:R8によって適用された完全なマージ済みルールセットが含まれます。これには、ホストのkeepルール、デフォルトのAndroidプラットフォームルール、AAPT2ルール、およびリンクされた.aarや.jarライブラリ内に同梱されている/proguard.txtまたは/META-INF/ルールがすべて組み合わされています。seeds.txt:アクティブな-keepルールに一致したすべてのクラス、コンストラクタ(<init>())、メソッド、フィールドがリストされます。seeds.txtでの一致はルール構文を確認するものですが、DEX検査およびランタイムテストと組み合わせる必要があることに注意してください。usage.txt:ツリーシェイキング中にR8がAPKから削除したデッドコードがリストされます。必要なSDKクラス、リフレクションコンストラクタ、またはJNIアップコールメソッドがusage.txtに含まれていないことを確認してください。mapping.txt:保持されたクラスとメンバーの変更前と変更後の識別子変換マップが記録されます。mapping.txtに存在しないからといって、クラスが元の名前で保持されたという証明にはならないことに注意してください。圧縮中に完全に削除された(そしてusage.txtにリストされた)可能性があります。常にmapping.txtをseeds.txt、usage.txt、およびコンパイル済みのDEXファイルと照合してください。
apkanalyzerを使用した生のDEX識別子と逆難読化されたレポート状態の検証
公式のGoogle Androidデベロッパー APK Analyzer CLIを使用してリリースAPKを検査する場合は、コンパイルされた生のDEX識別子とマッピングによって逆難読化された名前を区別してください。
- コンパイルされた生のDEX名の検査(
--proguard-mappingsなし):apkanalyzerに--proguard-mappings mapping.txtを渡すと、ツールは難読化されたDEX名(a.b.cなど)を元のソース名に変換して表示します。したがって、--proguard-mappingsが有効なときに元のクラス名が表示されても、そのシンボルがclasses.dex内でリネームを免れたという証明にはなりません。まずは--proguard-mappingsを付けずにapkanalyzer dex packagesを実行してください。また、R8のクラス再パッケージ化によってクラスがフラット化されたパッケージパスに移動することもあるため、パッケージのgrepだけに頼らないでください。生のDEXクラスツリーを検査し、リフレクションエントリーポイントのクラス名を確認し、JNIメソッド名とディスクリプタシグネチャをチェックし、さらにmapping.txtとクロスチェックして、必要なメソッドとフィールドがリネームされていないことを確認してください。 - R8保持状態の検査(
--proguard-mappings、--proguard-seeds、--proguard-usagesあり):2回目のパスでは、3つのR8レポートファイルをすべて指定して、すべてのパッケージ(P)、クラス(C)、メソッド(M)、フィールド(F)にわたる保持フラグ(シードルールによって保持された場合はk、圧縮中に削除された場合はx、参照されている場合はr、定義されている場合はd)を再構築します。
# Run from the Android project root directory (adjust variant path for product flavors, e.g., prodRelease)
# 1. (AGP 9.3+) Run the standalone R8 configuration analyzer for rapid rule verification, then build release APK
./gradlew :app:analyzeReleaseR8Config
./gradlew :app:assembleRelease
# 2. Inspect the merged R8 configuration, matched seeds, and stripped usage reports
grep -i -C 3 "opoinstall" app/build/outputs/mapping/release/configuration.txt
grep -i "opoinstall" app/build/outputs/mapping/release/seeds.txt
grep -i "opoinstall" app/build/outputs/mapping/release/usage.txt
# 3A. Inspect RAW compiled DEX identifiers (WITHOUT --proguard-mappings)
# Also inspect the full raw DEX tree and cross-check mapping.txt in case R8 repackaged classes
apkanalyzer dex packages app/build/outputs/apk/release/app-release.apk | grep -i "opoinstall"
# 3B. Inspect R8 retention state flags (k = kept, x = removed, r = referenced, d = defined) WITH R8 report files
apkanalyzer dex packages \
--proguard-mappings app/build/outputs/mapping/release/mapping.txt \
--proguard-seeds app/build/outputs/mapping/release/seeds.txt \
--proguard-usages app/build/outputs/mapping/release/usage.txt \
app/build/outputs/apk/release/app-release.apk | grep -i "opoinstall"
# 4. Verify 16 KB page alignment (if native .so files are present) and cryptographic APK signatures
zipalign -c -P 16 -v 4 app/build/outputs/apk/release/app-release.apk
apksigner verify --verbose app/build/outputs/apk/release/app-release.apk
リリースビルドのアトリビューショントラブルシューティングマトリックス
圧縮されたAndroidビルドにおけるサイレントなアトリビューションおよびディープリンク障害の診断
圧縮されたリリースビルドでのみ発生するアトリビューションの不整合をトラブルシューティングする場合は、以下の診断マトリックスを使用して根本原因を特定してください。
| 確認されたリリース時の症状 | 考えられるR8 / ビルドの根本原因 | 診断検証ステップ | 是正措置 |
|---|---|---|---|
リリースビルドでgetInstallParamまたはhandleDeepLinkコールバックがまったく発火しない |
パッケージの不一致により、SDKエントリーポイントクラス、リフレクションの<init>()コンストラクタ、またはResultCallBackメソッドが削除またはリネームされた |
seeds.txtとusage.txtを確認する。生のapkanalyzer dex packages出力(--proguard-mappingsなし)とmapping.txtを検査する |
jar tfで.jar / .aarを検査して大文字・小文字を区別する正確なクラスパスを見つけ、<init>(...);を含むスコープを絞った-keepルールを追加する |
コールバックは発火するが、OpoDataカスタムパラメータが空またはnullとしてパースされる |
OpoDataが-keepclassmembersのみで保護されていた(クラス自体の削除が許容された)か、ジェネリックのSignatureメタデータが失われた |
usage.txtとmapping.txtでOpoDataを確認し、configuration.txtで-keepattributes Signatureを確認する |
(OpoDataがリフレクションでインスタンス化される場合は)<init>(...); <fields>;を伴う-keep class ...を使用し、-keepattributes Signatureを保持する |
SDKが.soライブラリをロードする際にUnsatisfiedLinkErrorが発生するか、ネイティブコールバックが欠落する |
動的に登録されたRegisterNativesダウンコールが圧縮されたか、C/C++からJavaへのアップコールターゲットメソッドがR8によってリネームされた |
logcatを検査して欠落している正確なJNIシンボルを特定し、生のapkanalyzer dex packagesとmapping.txtでターゲットのJavaクラスを確認する |
JNIアップコールとRegisterNativesターゲットに対して、includedescriptorclassesを伴う-keep(またはブリッジクラスがJavaで直接インスタンス化される場合は-keepclassmembers)を使用する |
| エクスポートされたAndroidチャネルパッケージのインストールが失敗するか、チャネル統計が欠落する | エクスポート後のAPK変更により、APK署名が無効になったか、埋め込まれたチャネルメタデータが上書きされた | エクスポートされたチャネルAPKに対してzipalign -c -P 16 -v 4とapksigner verify --verboseを実行し、実機でテストする |
コンソールにアップロードする前に、ベースAPKですべてのR8圧縮、堅牢化、アライメント、署名を完了させる。エクスポート後に署名とアトリビューションの両方を検証する |
よくある質問(FAQ)
AndroidのProGuardおよびR8の難読化からモバイルSDKを除外するにはどうすればよいですか?
Android SDKのクラスがR8の難読化から除外されたことを検証するにはどうすればよいですか?
APKの堅牢化(Hardening)や後処理の後にAndroidチャネルパッケージの統計が機能しなくなるのはなぜですか?
まとめとリリースビルド検証チェックリスト
AndroidのProGuardおよびR8による難読化からモバイルSDKを除外する方法を理解することは、デバッグテストから本番リリースビルドへ移行する際のサイレントなアトリビューションのリグレッションを防ぐために不可欠です。同梱されているコンシューマールールを確認し、SDKアーカイブ内の大文字・小文字を区別する正確なクラスパスに一致するスコープを絞った-keepディレクティブを記述し、Androidチャネルパッケージのアップロード前のビルド順序を遵守し、R8の診断出力ファイルを生のDEXバイトコードとクロスチェックすることで、Androidエンジニアはディファードディープリンクやパラメータ復元を損なうことなく、完全なコード圧縮を維持できます。
圧縮されたAndroidリリースビルドをGoogle Playに公開する前、またはチャネルパッケージをエクスポートする前に、以下の4ステップの検証チェックリストを実行してください。
-
SDKアーカイブとマージ済み設定の検査:ダウンロードしたSDKアーカイブ(v2.1.0など)を
jar tfまたはunzip -lでチェックして正確なクラスパスと同梱されているコンシューマールールを検査し、./gradlew :app:analyzeReleaseR8Config(AGP 9.3以降)を実行するか、configuration.txtを確認します。 -
R8レポートと生のDEX識別子のクロスチェック:必要なSDKエントリーポイント、リフレクションの
<init>()コンストラクタ、OpoDataモデルフィールド、ResultCallBackメソッド、およびJNIシグネチャがseeds.txtに表示され、usage.txtには存在せず、生のapkanalyzer dex packages出力(--proguard-mappingsなしで実行)およびmapping.txtで元の名前を保持していることを確認します。 -
アップロード前の圧縮の徹底とチャネルAPK署名の検証:Androidチャネルパッケージをエクスポートするためにアップロードする前に、ベースAPKですべてのR8圧縮、サードパーティ製の堅牢化、
zipalign(zipalign -c -P 16 -v 4)、およびAPK署名を完了させ、エクスポートされたパッケージに対してapksigner verify --verboseを実行します。 -
実機でのリリーススモークテストの実行:署名および圧縮されたリリースAPKを実際のAndroidデバイスにインストールし、H5パラメータ付きインストール(
getInstallParam)とワンクリック起動(handleDeepLink)の両方を検証します。
開発チームは、OpoInstallデベロッパーコンソールでアプリケーションを登録し、難読化されたベースAPKをアップロードすることで、チャネルパッケージのエクスポートをテストし、リリースビルドでのパラメータ復元を検証できます。
関連資料
-
概念:Android R8コード圧縮、識別子の難読化、コンシューマーProGuardルール、チャネルパッケージのビルド順序
-
テクノロジー:Android Gradle Plugin (AGP)、R8コンパイラ、
proguard-rules.pro、Android Studio APK Analyzer -
プラットフォームおよび連携インターフェース:
isMinifyEnabled、optimization.enable、analyzeReleaseR8Config、consumerProguardFiles、seeds.txt、usage.txt、apkanalyzer dex packages、apksigner、zipalign -
公式ドキュメント&リファレンス:
Share this article



