Android コンパイル最適化のための混乱構成

Android コンパイル最適化のための混乱構成

Android コンパイルの最適化

バックグラウンド

java8 およびその後の新しいバージョンの java の機能を使用するために、Google はコンパイル プロセスに脱糖 (糖化除去) というステップを追加しましたが、このステップではコンパイル時間が長くなります。そのため、Google は D8 および R8 コンパイラをリリースしました。コンパイル速度を最適化します。

脱糖とは何ですか?

糖化解除とは、基礎となるバイトコードで文法レベルでサポートされていない一部の機能を、コンパイル段階で基本的なバイトコード構造に変換することです (たとえば、List のジェネリック型は、糖化解除後のバイトコード レベルでは実際には Object になります)。Android ツールJava8 構文機能の脱糖プロセスはカラフルであると説明できますが、もちろん、最終的な目標は同じであり、新しい構文をすべてのデバイスで実行できるようにすることです。

D8

D8 の機能は Java バイトコードを dex コードに変換することであり、D8 は DX の代替品です。コンパイル プロセスを次の図に示します。
D8

Android Studio のバージョン 3.1 以降、D8 がデフォルトの Dex コンパイラとして使用されます。D8 を閉じたい場合は、gradle.properties に次の構成を追加できます。

android.enableD8=false
android.enableD8.desugaring=false

D8をオンにするメリット

  • コンパイルをより速く、より短時間で行う
  • DEX はコンパイル時に占めるコンテンツが少なくなります
  • .dex ファイルの方が小さい
  • D8 によってコンパイルされた .dex ファイルは、同等以上のパフォーマンスを備えています。

プロジェクトで既に Java 8 を使用して D8 コンパイルを可能な限り有効にしている場合は、コンパイル エラーが発生する可能性があります。

R8

オリジナルの Proguard 圧縮および最適化 (縮小、縮小、最適化) 部分の代替として、R8 は Proguard と同じ保持ルールを引き続き使用しており、新世代のコード圧縮ツールです。R8 は以前は D8+Proguard の形式でビルドされていましたが、R8 は難読化ツールと D8 ツールを統合して、ビルド時間を短縮し、出力 APK のサイズを削減しました。

R8

Gradle プラグインのバージョンが 3.4.0 以降になると、R8 はデフォルトでコードの最適化を開始します。R8 を有効にしたくない場合は、gradle.properties次の構成を追加できます。

android.enableR8=false

R8 をオンにする利点

  • コード削減: 64 参照制限の回避
  • リソース削減: 未使用のリソースを削除します。
  • 難読化されたコード: DEX ファイルのサイズを削減します。
  • コードの最適化: DEX ファイル サイズをさらに削減します。

参考:

https://developer.android.google.cn/studio/build/shrink-code

関連評価報告書
D8R8評価報告書

AS マルチモジュールの混乱構成

AS マルチモジュールでは、各モジュールの個別の構成方法を採用できます。つまり、proguard ファイルが各モジュールの下で構成され、それぞれの build.gradle ファイルに導入されます。または、以下の集中構成方法を使用して、集中的に構成することができます。各モジュールの proguard ファイルを A フォルダーに保存し、依存するモジュールの proguard ファイルをアプリ モジュールに一元的にインポートします。
アプリ モジュール cmcc_service を例として説明してみましょう。

buildTypes {
    
    
        release {
    
    
            minifyEnabled true //开启混淆
            shrinkResources true //无用资源去除
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro',
                    '../proguardDefine/common-rules.pro', '../proguardDefine/live_lib-rules.pro',
                    '../proguardDefine/gs_api_adapter-rules.pro', '../proguardDefine/cmcc_service.pro',
                    '../proguardDefine/lib_voiceassistant-rules.pro','../proguardDefine/cmcc_softprobe-rules.pro'
            if (propertyHaveSigningConfigs)
                signingConfig signingConfigs.release
        }
}

その中には、proguard-android-optimize.txtAndroid によってデフォルトで設定されている一般的な難読化ルール、proguard-rules.proアプリ コードの難読化ルール、proguardDefineフォルダー下の依存モジュールが均一に配置されるproguard場所などがあります。

注: インポートされたスリーパーティ ライブラリには、通常、対応する proguard ファイルが装備されているため、構成を繰り返す必要はありません。gradle をコンパイルすると、関連ファイルの proguard ファイルが 1 つのファイルにマージされ
ますconfiguration.txt
設定の場所

proguard-android-optimize.txt

# 禁用一些代码简化和优化,以及字段和类合并
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 运行优化 passes 的数量为 5,数值越高,混淆效果越好,但耗时也更长
-optimizationpasses 5
# 允许访问和修改保护代码
-allowaccessmodification

# 不允许使用大小写混合的类名
-dontusemixedcaseclassnames
# 不跳过非公共库类
-dontskipnonpubliclibraryclasses
# 详细输出
-verbose

# 保留一些反射所需的属性
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
# 保留以下三个类及其公共成员
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
-keep public class com.google.android.vending.licensing.ILicensingService
# 忽略以下类,不输出 note 信息
-dontnote com.android.vending.licensing.ILicensingService
-dontnote com.google.vending.licensing.ILicensingService
-dontnote com.google.android.vending.licensing.ILicensingService

# 对于本地方法,参见 http://proguard.sourceforge.net/manual/examples.html#native
# 保留包含 native 方法的类和方法
-keepclasseswithmembernames,includedescriptorclasses class * {
    
    
    native <methods>;
}
# 保留公共的 View 子类的 set 和 get 方法
# 以便于使用属性动画
-keepclassmembers public class * extends android.view.View {
    
    
    void set*(***);
    *** get*();
}
# 保留 Activity 中可用于 XML 属性 onClick 中的方法
-keepclassmembers class * extends android.app.Activity {
    
    
    public void *(android.view.View);
}
# 对于枚举类,参见 http://proguard.sourceforge.net/manual/examples.html#enumerations
# 保留枚举类成员
-keepclassmembers enum * {
    
    
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
# 保留实现 Parcelable 接口的类的 CREATOR 静态成员
-keepclassmembers class * implements android.os.Parcelable {
    
    
    public static final ** CREATOR;
}
# 保留 JavaScript 接口方法上的注解
-keepclassmembers class * {
    
    
    @android.webkit.JavascriptInterface <methods>;
}
# 支持库包含对新平台版本的引用
# 在应用链接旧版平台版本时,不要发出警告,因为它们是安全的
-dontnote android.support.**
-dontnote androidx.**
-dontwarn android.support.**
-dontwarn androidx.**
# 该类已弃用,但仍然保留用于向后兼容
-dontwarn android.util.FloatMath
#这段混淆规则用于保护使用了@Keep注解的类和成员不被混淆,以及忽略特定的冗余类。
-keep class android.support.annotation.Keep
-keep class androidx.annotation.Keep
#保留使用了@Keep注解的类和接口的所有成员
-keep @android.support.annotation.Keep class * {
    
    ;}
-keep @androidx.annotation.Keep class * {
    
    ;}
#保留使用了@Keep注解的类的方法
-keepclasseswithmembers class * {
    
    
@android.support.annotation.Keep <methods>;
}
-keepclasseswithmembers class * {
    
    
@androidx.annotation.Keep <methods>;
}
#保留使用了@Keep注解的类的字段
-keepclasseswithmembers class * {
    
    
@android.support.annotation.Keep <fields>;
}
-keepclasseswithmembers class * {
    
    
@androidx.annotation.Keep <fields>;
}
#保留使用了@Keep注解的类的构造方法
-keepclasseswithmembers class * {
    
    
@android.support.annotation.Keep <init>(...);
}
-keepclasseswithmembers class * {
    
    
@androidx.annotation.Keep <init>(...);
}
#忽略特定的冗余类
#android.jar和org.apache.http.legacy.jar中的类重复
-dontnote org.apache.http.**
-dontnote android.net.http.**
#android.jar和core-lambda-stubs.jar中的类重复
-dontnote java.lang.invoke.**

一部のサードパーティは独自の難読化ルールを持っています

configuration.txtこれらのルールは、ファイルのRetrofit2 難読化ルールにもマージされます。

# The proguard configuration file for the following section is /home/cl/.gradle/caches/transforms-3/29b6aa006718d6829551a18646bf70bb/transformed/rules/lib/META-INF/proguard/retrofit2.pro
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    
    
    @retrofit2.http.* <methods>;
}

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.-KotlinExtensions

# End of content from /home/cl/.gradle/caches/transforms-3/29b6aa006718d6829551a18646bf70bb/transformed/rules/lib/META-INF/proguard/retrofit2.pro

RxJava2\RxAndroid の混同ルール

-dontwarn java.util.concurrent.Flow*

OKhttp3 難読化ルール

# The proguard configuration file for the following section is /home/cl/.gradle/caches/transforms-3/af3ecb4c3ae4accf6423845d738f047d/transformed/rules/lib/META-INF/proguard/okhttp3.pro
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*

# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform

# End of content from /home/cl/.gradle/caches/transforms-3/af3ecb4c3ae4accf6423845d738f047d/transformed/rules/lib/META-INF/proguard/okhttp3.pro

より多くの難読化構成が可能

# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 指定不去忽略非公共库的成员
-dontskipnonpubliclibraryclassmembers
# 混淆时不做预校验
-dontpreverify
# 忽略警告
-ignorewarnings
# 保留代码行号,方便异常信息的追踪
-keepattributes SourceFile,LineNumberTable
# appcompat库不做混淆
-keep class androidx.appcompat.**
#保留 AndroidManifest.xml 文件:防止删除 AndroidManifest.xml 文件中定义的组件
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-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
-keep public class * extends android.view.View

#保留序列化,例如 Serializable 接口
-keepclassmembers class * implements java.io.Serializable {
    
    
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

#带有Context、View、AttributeSet类型参数的初始化方法
-keepclasseswithmembers class * {
    
    
    public <init>(android.content.Context);
}
-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);
}

#保留资源R类
-keep class **.R$* {
    
    *;}

#避免回调函数 onXXEvent 混淆
-keepclassmembers class * {
    
    
    void *(**On*Event);
    void *(**On*Listener);
    void *(**on*Changed);
}

#业务实体不做混淆,避免gson解析错误
-dontwarn com.grandstream.convergentconference.entity.**
-keep class com.grandstream.convergentconference.entity.** {
    
     *;}

#Rxjava、RxAndroid,官方ReadMe文档中说明无需特殊配置
-dontwarn java.util.concurrent.Flow*
#okhttp3、okio、retrofit,jar包中已包含相关proguard规则,无需配置
#其他一些配置

Android.bp の混乱設定

android_app {
    
    
    name: "MyApp",
    package_name: "com.example.myapp",
    srcs: ["java/**/*.java"],
    manifest: "AndroidManifest.xml",
    dex_preopt: {
    
    
        enabled: true,
    },
    apk_cert_permissions: [
        "android.permission.ACCESS_FINE_LOCATION",
        "android.permission.RECORD_AUDIO",
        "android.permission.READ_CONTACTS",
    ],
    certificates: ["platform"],
    resource_files: ["res/**/*"],
    enable_proguard: true,
    proguard_flags: ["proguard-android.txt"],
    dex_preopt_image_dir: "target/product/${TARGET_PRODUCT}/obj/dex_preopt_image",
}

このうち、enable_proguard は true に設定されてコードとリソースの難読化が有効になり、proguard_flags は Proguard 難読化ルール ファイルを指定し、ファイル内でコードとリソースの難読化ルールを指定できます。
resource_files は、AndroidManifest.xml ファイルを含むアプリケーションのリソース ファイルを指定します。

android.mkの混乱設定

LOCAL_PROGUARD_ENABLED := obfuscation optimization

LOCAL_PROGUARD_FLAG_FILES := proguard.flags
ifeq (eng,$(TARGET_BUILD_VARIANT))
    LOCAL_PROGUARD_FLAG_FILES += proguard-test.flags
else
    LOCAL_PROGUARD_FLAG_FILES += proguard-release.flags
endif

LOCAL_PROGUARD_ENABLEDいくつかの構成があります。

  1. LOCAL_PROGUARD_ENABLED := disabled: 難読化を無効にします。
  2. LOCAL_PROGUARD_ENABLED := obfuscate: 最適化を行わずにコードの難読化のみを有効にします。
  3. LOCAL_PROGUARD_ENABLED := optimize: 最適化のみを有効にし、コード難読化は行いません。
  4. LOCAL_PROGUARD_ENABLED := obfuscate optimize: コードの難読化と最適化を同時に有効にします。

デフォルトでは、ビルド システムは、LOCAL_PROGUARD_FLAG_FILESアプリ固有のproguardファイルを指定するために Android の一般的な難読化ファイルをロードする必要があります。
これらのオプションの設定が苦手な場合は、ネイティブ アプリケーションの難読化設定を確認してください。これは非常に参考になるでしょう。

難読化されたスタックの復元

Android の組み込み混乱スタック復元ツール proguardgui を使用して解決します

Android/Sdk/tools/proguard/bin$ ls
proguard.sh  proguardgui.sh  retrace.sh
/Android/Sdk/tools/proguard/bin$ ./proguardgui.sh

スクリプトの実行後、ProGuard デバッグ インターフェイスが開きます。
プロガード GUI ツール

混乱前後のマッピング ファイルmapping.txtと、復元する必要があるlogcat例外ログを入力し、[Retrace]ボタンをクリックして混乱前の例外情報を復元します。

結論は


つまり、Android はすでに一般的な難読化ルールをいくつか書いてくれています。私たちが気にしているのは、自分たちで開発するコードの一部の難読化ルールかもしれません。優れた難読化作業には、コードの安全性、パッケージ サイズの縮小、動作の高速化など、多くの利点があります。高速化されていますが、混乱により予期せぬ動作異常が発生する可能性もありますので、しっかりテストして、早めに利用していただければと思います。

参考

https://blog.csdn.net/Mr_dsw/article/details/90141647
https://blog.csdn.net/wwj_748/article/details/115874571
Android の混乱、新しく導入された D8 と R8 で何が変わったのか?
https://toutiao.io/posts/ijfhzkv/preview

おすすめ

転載: blog.csdn.net/u011897062/article/details/130603761