Android Studio を使用してパッケージ化する場合、Studio 自体に Java 言語の ProGuard が圧縮、最適化、難読化ツールとして統合されており、Gradle ビルド ツールとの併用が非常に簡単です。プロジェクトのアプリケーション ディレクトリの gradle ファイルで minifyEnabled を true に設定するだけです。次に、難読化ルールを proguard-rules.pro ファイルに追加できます。
ProGuard 効果の
圧縮 (縮小) : デフォルトで有効になっており、アプリケーションのサイズを削減し、未使用のクラスとメンバーを削除し、最適化アクションの実行後に再実行します (一部の未使用のクラスが最適化とメンバーの後に再び公開される可能性があるため)。圧縮をオフにしたい場合は、proguard-rules.pro ファイルに次の内容を追加します。
# 关闭压缩
-dontshrink
最適化: デフォルトで有効になっており、アプリケーションの実行を高速化するためにバイトコード レベルで最適化が実行されます。上記と同様に、最適化をオフにしたい場合は、proguard-rules.pro ファイルを追加します。
# 关闭优化
-dontoptimize
-optimizationpasses n 表示proguard对代码进行迭代优化的次数,Android一般为5
難読化: デフォルトで有効になっており、逆コンパイルの難易度が高くなります。クラスとクラス メンバーは、keep で保護されていない限り、ランダムに名前が付けられます。
# 关闭混淆
-dontobfuscate
難読化後、デフォルトでは、プロジェクト ディレクトリ app/build/outputs/mapping/release の下に、mapping.txt ファイルが生成されます。これが難読化ルールです。このファイルに従って、難読化されたコードをソース コードにプッシュできます。このファイルは非常に重要なので、しっかりと保護するように注意してください。原則として、難読化後のコードは混乱していて不規則であればあるほど良いのですが、場所によっては混乱を避ける必要があります。そうしないとプログラムが正しく動作しなくなるため、コードの一部の難読化を回避する方法を以下に説明します。ミスを防ぐことができます。
コードで難読化を有効にします。
アプリのモジュールの下にある build.gradle ファイルで minifyEnabled false を true に変更します。
不要なリソースを同時に削除するように設定することもできます。(無駄なリソースを削除)
ここを変更した後、アプリ モジュールの下の proguard-rules.pro ファイルを操作して、プロジェクトのパッケージ化の難読化オプションを定義する必要があります。
以下は私がよく使用する難読化テンプレートで、実際のニーズに応じて追加または削除できます。
#--------------------------1.实体类 ↓---------------------------------
-keep public class com.XX.XX.bean.** { *; }
#--------------------------1.实体类 ↑---------------------------------
#--------------------------2.第三方包 ↓-------------------------------
# Glide
-keep public class * implements com.bumptech.glide.module.AppGlideModule
-keep public class * implements com.bumptech.glide.module.LibraryGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
#gson
-keep class com.google.gson.** { *; }
-keep class com.google.**{ *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
#retrofit2
-dontwarn javax.annotation.**
-dontwarn javax.inject.**
# OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**
# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
# eventbus
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
#--------------------------2.第三方包 ↑-------------------------------
#--------------------------3.与js互相调用的类 ↓------------------------
-keep class **.AndroidJavaScript { *; }
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap);
public boolean *(android.webkit.WebView,java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebChromeClient {
public void *(android.webkit.WebView,java.lang.String);
}
-keepattributes JavascriptInterface
#-------------------------3.与js互相调用的类 ↑------------------------
#-------------------------4.基本不用动区域 ↓--------------------------
#指定代码的压缩级别
-optimizationpasses 5
#包明不混合大小写
-dontusemixedcaseclassnames
#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
#混淆时是否记录日志
-verbose
#优化 不优化输入的类文件
-dontoptimize
#预校验
-dontpreverify
# 保留sdk系统自带的一些内容 【例如:-keepattributes *Annotation* 会保留Activity的被@override注释的onCreate、onDestroy方法等】
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# 记录生成的日志数据,gradle build时在本项根目录输出
# apk 包内所有 class 的内部结构
-dump proguard/class_files.txt
# 未混淆的类和成员
-printseeds proguard/seeds.txt
# 列出从 apk 中删除的代码
-printusage proguard/unused.txt
# 混淆前后的映射
-printmapping proguard/mapping.txt
# 避免混淆泛型
-keepattributes Signature
# 抛出异常时保留代码行号,保持源文件以及行号
-keepattributes SourceFile,LineNumberTable
#-------------------------4.基本不用动区域 ↑--------------------------
#-------------------------5.默认保留区 ↓-----------------------
# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclassmembers public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(***);
}
#保持 Serializable 不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context,android.util.AttributeSet);
}
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context,android.util.AttributeSet,int);
}
# 保持自定义控件类不被混淆
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# 保持枚举 enum 类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,字段名要是被混淆了,id也就找不着了。
-keepclassmembers class **.R$* {
public static <fields>;
}
#如果引用了v4或者v7包
-dontwarn android.support.**
# 保持哪些类不被混淆
-keep public class * extends android.app.Application
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.preference.Preference
#-------------------------5.默认保留区 ↑-----------------------
# ============忽略警告,否则打包可能会不成功=============
-ignorewarnings
難読化に関するいくつかの基本的なルール:
-keep class com.XX.test.**
-keep class com.XX.test.*
星 1 つは
、パッケージの下のクラス名のみが保持され、サブパッケージの下のクラス名は依然として混乱することを意味します。星 2 つは、
この
パッケージとサブパッケージの下のクラス名が保持されることを意味します。上記を使用した後メソッドを使用して class を保持すると、クラス名は混乱していませんが、内部の特定のメソッドと変数の名前が変更されていることがわかります。
クラス名を保持し、内部のコンテンツが混乱しないようにしたい場合は、次を使用します。
-keep class com.XX.test.* {*;}
これに基づいて、Java の基本ルールを使用して、特定のクラスを混乱から保護することもできます (たとえば、extends やimplements などの Java ルールを使用できます)。次の例では、Activity から継承するすべてのクラスが混同されるのを防ぎます。
-keep public class * extends android.app.Activity
クラス内の内部クラスを混乱させないようにするには、$ 記号を使用する必要があります。次の例は、XxFragment 内部クラス JavaScriptInterface 内のすべてのパブリック コンテンツを混乱させないことを意味します。
-keepclassmembers class com.XX.ui.fragment.XxFragment$JavaScriptInterface {
public *;
}
クラス内のすべてのコンテンツを混乱から守りたくないが、クラス内の特定のコンテンツだけを保護したい場合は、次のように使用できます。
<init>; //匹配所有构造器
<fields>; //匹配所有域
<methods>; //匹配所有方法方法
<fields> または <methods> の前に private、public、native などを追加して、混乱しないコンテンツをさらに指定することもできます。たとえば、次のコードは、Demo という名前のクラスにあるすべての Public メソッドが混乱しないでください。
-keep class com.XX.test.Demo {
public <methods>;
}
たとえば、次のコードは、入力パラメータとして JSONObject を持つコンストラクタが混同されないことを示しています。
-keep class com.XX.test.Demo {
public <init>(org.json.JSONObject);
}
クラス名を保持する必要がなく、クラス内の特定のメソッドを混乱から守るだけでよい場合は、keep メソッドは使用できません。keep メソッドはクラス名を保持しますが、次のことを行う必要があります。 keepclassmembers を使用すると、クラス名は保持されません。
# -keep关键字
# keep:包留类和类中的成员,防止他们被混淆
# keepnames:保留类和类中的成员防止被混淆,但成员如果没有被引用将被删除
# keepclassmembers :只保留类中的成员,防止被混淆和移除。
# keepclassmembernames:只保留类中的成员,但如果成员没有被引用将被删除。
# keepclasseswithmembers:如果当前类中包含指定的方法,则保留类和类成员,否则将被混淆。
# keepclasseswithmembernames:如果当前类中包含指定的方法,则保留类和类成员,如果类成员没有被引用,则会被移除。
予防
1. jni メソッドはネイティブ メソッドと一貫性を保つ必要があるため、混同することはできません。
# 保持native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
2. リフレクションで使用されるクラスは混同されません (混同しない場合、リフレクションで問題が発生する可能性があります)。
3. AndroidMainfest のクラスは混乱しないため、4 つの主要なコンポーネントとアプリケーションのサブクラス、およびフレームワーク層の下のすべてのクラスはデフォルトで混乱せず、カスタム ビューもデフォルトで混乱しません。したがって、インターネット上に投稿されるカスタム ビューを除外したり、4 つの主要コンポーネントが混同されたりする多くのルールを Android Studio に追加する必要はありません。
4. サーバーと対話するとき、GSON、fastjson、およびその他のフレームワークを使用してサーバー データを解析する場合、記述された JSON オブジェクト クラスは混乱しません。そうでないと、JSON を対応するオブジェクトに解析できません。
5. サードパーティのオープンソース ライブラリを使用する場合、または他のサードパーティ SDK パッケージを参照する場合、特別な要件がある場合は、対応する難読化ルールを難読化ファイルに追加する必要もあります。
6. WebView を使用する JS 呼び出しでも、記述されたインターフェイス メソッドが混乱しないようにする必要があります。理由は最初のメソッドと同じです。
7. Parcelable のサブクラスは Creator の静的メンバー変数と混同されません。そうでない場合、Android.os.BadParcelableException が生成されます。
# 保持Parcelable不被混淆
-keep class * implements Android.os.Parcelable {
public static final Android.os.Parcelable$Creator *;
}
8. enum 型を使用する場合、次の 2 つのメソッドの混同を避けるために注意する必要があります。enum クラスの特殊性により、次の 2 つのメソッドはリフレクションによって呼び出されます。2 番目のルールを参照してください。
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
アプリをリリースするときに minifyEnabled を true に設定することに加えて、zipAlignEnabled を true に設定する必要もあります。たとえば、Google Play では、開発者に zipAligned である必要があるアプリをアップロードするよう強制します。ZipAlign は、インストール パッケージ内のリソースを 4 バイトで揃えることができます。実行時のアプリケーションのメモリ消費を削減できます。
参考:
Android パッケージングの混乱と詳細な構文ルール_wx60e80a6332641 の技術ブログ_51CTO ブログ