# Obfuscate tool - R8 * Gradle plugin 3.4以上預設已經不使用ProGuard執行編譯時的程式碼優化 * 現在預設是使用R8,配合ProGuard rule排除特定類別或函數 ### R8編譯時執行那些任務 * Code shrinking * 它會幫你移除dependencies裡面沒有使用到的類別、方法、屬性,比方說引用了retrofit,但只使用到@GET,那其他沒使用到的方法,在這個階段就會被移除。 * 如果dependency太多,就容易出現64K限制[官方解說](https://developer.android.com/studio/build/multidex),透過code shrinking可以盡可能避免64K限制 * Resource shrinking * 它幫你移除app內的library dependencies沒使用到的recources,通常搭配code shrinking * Obfuscation * 這邊會把class跟members重新命名,進而減少apk的大小 * Optimization * 這邊做程式碼優化,官網提到是if/ else區塊,如果只使用到if,它會自動把else移除, * 我在想是不是在kotlin我如果只寫if,在編譯時若沒用R8優化,在編譯完是不是也會包含一個else的空區塊... ### 如何使用呢 * 在build.gradle(Module:)。minifyEnable就是code shrinking * 底下在加一行是recources shrinking * 這樣就打開R8的基本功能了  ### 搭配ProGuadr rule * 每個新專案都會有一個空的rule: proguard-rules.pro * 這裡面要填一些要排除,不要進行混淆的類別跟成員 * 那它也可以針對不同階段的產品配置不同的規則,太深入..可以[參考官方](https://developer.android.com/studio/build/shrink-code)  #### 如何定義要保留的code不被混淆呢 * 方法有兩個 * 直接在class使用聲明註解 @Keep  * 在proguard-rules.pro定義 * com.androiddevs.fcm是我的專案名稱,PushNotification是資料類別 ```kotlin -keep class com.androiddevs.fcm.PushNotification ``` #### 語法 [保持命令] [類別名稱] { [成员] } * 保持命令 1. -keep # 防止類別和成員被移除或被混淆 2. -keepnames # 防止类和成員名稱被混淆 3. -keepclassmembers # 防止類別成員被移除或被混淆 4. -keepclassmembernames # 防止類別成員被混淆 5. -keepclasseswithmembers # 防止參照該成員的類別和成員被移除或被混淆 6. -keepclasseswithmembernames # 防止參照該成員的類別和成員被混淆 * 類別 1. 明確指定的類別 2. 修飾符 : public、private、protected 3. 通配符(*) : 匹配任意长度字符,但不包含包名分隔符(.) 4. 通配符(**) : 匹配任意长度字符,且包含包名分隔符(.) 5. extends : 繼承parent class的 child class 6. implements : 實例化特定介面類別 7. $ : 內部類別 * 成員 1. 建構子 : <init> 2. 變數 : <field> 3. 方法 : <methods> 4. 修飾符 : public、private、protected 5. 除了 * 和 ** 通配符外,還支持 *** 通配符,匹配任意参数類型 6. ... : 匹配任意長度的任意類型参数,如void test(...)可以匹配不同参数個數的test方法 ```kotlin= // keep fcm這個package底下的類別名稱,防止被刪除會混淆,但是fcm底下的子package不在此限 -keep class com.androiddevs.fcm.* // keep fcm這個package和子package底下的類別名稱,防止被刪除會混淆 -keep class com.androiddevs.fcm.** ## 上述兩種它的方法和變數還是會被混淆,底下加入成員 //保持fcm package類別名稱還有方法與變數不被混淆 -keep class com.androiddevs.fcm.*{*;} -keep class com.androiddevs.fcm.**{*;} ``` * 參考網路上一些相關的些法 ```kotlin= # 不混淆某个类的类名,及类中的内容 -keep class cn.coderpig.myapp.example.Test { *; } # 不混淆指定包名下的类名,不包括子包下的类名 -keep class cn.coderpig.myapp* # 不混淆指定包名下的类名,及类里的内容 -keep class cn.coderpig.myapp* {*;} # 不混淆指定包名下的类名,包括子包下的类名 -keep class cn.coderpig.myapp** # 不混淆某个类的子类 -keep public class * extends cn.coderpig.myapp.base.BaseFragment # 不混淆实现了某个接口的类 -keep class * implements cn.coderpig.myapp.dao.DaoImp # 不混淆类名中包含了"entity"的类,及类中内容 -keep class **.*entity*.** {*;} # 不混淆内部类中的所有public内容 -keep class cn.coderpig.myapp.widget.CustomView$OnClickInterface { public *; } # 不混淆指定类的所有方法 -keep cn.coderpig.myapp.example.Test { public <methods>; } # 不混淆指定类的所有字段 -keep cn.coderpig.myapp.example.Test { public <fields>; } # 不混淆指定类的所有构造方法 -keep cn.coderpig.myapp.example.Test { public <init>; } # 不混淆指定参数作为形参的方法 -keep cn.coderpig.myapp.example.Test { public <methods>(java.lang.String); } # 不混淆类的特定方法 -keep cn.coderpig.myapp.example.Test { public test(java.lang.String); } # 不混淆native方法 -keepclasseswithmembernames class * { native <methods>; } # 不混淆枚举类 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } #不混淆资源类 -keepclassmembers class **.R$* { public static <fields>; } # 不混淆自定义控件 -keep public class * entends android.view.View { *** get*(); void set*(***); public <init>; } # 不混淆实现了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(); } # 不混淆实现了parcelable接口的类成员 -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } ``` #### 注意事項 * Android底層元件和類別不可混淆 ```kotlin -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.ContentProvider -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.view.View -keep public class * extends android.preference.Preference ``` * jni方法不可混淆 ```kotlin -keepclasswithmembernames class *{ native <methods>; } ``` * 反射用到的類名和方法不可混淆 ```kotlin -keep public class com.example.ljz.** { public void set*(***); public *** get*(); public *** is*(); } ``` * 自定義View不可混淆(有查到其他有說系統默認不會混淆,不須額外設定) * 第三方框架不可混淆,像一些Retrofit、Glide..等等的,除非有提供混淆規則 * WebView和Js互調介面不可混淆 ```kotlin -keepclassmembers class ** { @android.webkit.JavascriptInterface public *; } ``` * 序列化的類別不可混淆 ```kotlin -keepclassmembers class * implements android.os.Parcelable { static ** CREATOR; <fields>; <methods>; } -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(); } ``` * 列舉類別 ```kotlin -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); public static ** valueOf(int); } ``` * 總之...還有很多很多場景,有需要用到再去查詢吧~~~ #### Recources shrinking * 這部分好像比較少用到,參考官網 * 在res路徑下建立 raw/keep.xml * tools:keep指定要保留的資源 * tools:discard指定要捨棄的資源 ```kotlin= <?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" /> ``` ### mapping table * 當你使用R8打包apk之後,會產生mapping file * 在Project/app/build/outputs/mapping/release/mapping.txt  * 這個文件可以在使用者發生系統崩潰產生的log,你必須上傳這個檔案到google play console,它可以協助去混淆,讓你方便閱讀log訊息 * [參考官方說明](https://support.google.com/googleplay/android-developer/answer/9848633?hl=zh-Hans&visit_id=637624395516347080-1010531705&rd=1) 參考資料 [Android混淆之Proguard的語法總結](https://www.itread01.com/content/1545201385.html) [知乎](https://zhuanlan.zhihu.com/p/383300926) ###### tags: `R8` `ProGuard` `obfuscate` `kotlin` `Android`
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.