混淆Dart代碼
Flutter 1.16.2 以上默認(rèn)支持混淆,不需要特殊設(shè)置,只需要在構(gòu)建命令后面加上
--obfuscate --split-debug-info=/<project-name>/<directory>
具體可看官方文檔:混淆Dart代碼
構(gòu)建發(fā)布包
具體配置請看官方文檔(只有很少的配置,很簡單):生成并發(fā)布Android應(yīng)用 、 生成并發(fā)布iOS應(yīng)用
構(gòu)建命令:
Android:
flutter build apk --obfuscate --split-debug-info=./symbols
Google目前建議Android使用 App Bundle 的發(fā)布格式,構(gòu)建命令如下:
flutter build appbundle --target-platform android-arm,android-arm64,android-x64
有不知道 App Bundle 發(fā)布格式的同學(xué)可以查看官方文檔(中文的): Android App Bundle 簡介
iOS:
flutter build ios --obfuscate --split-debug-info=./symbols
注意:
- 如果不想混淆代碼的話,iOS只需要去掉 “--obfuscate --split-debug-info=./symbols” 進行構(gòu)建就可以了,Android 的話單獨去掉 “--obfuscate --split-debug-info=./symbols” 是沒有用的,需要在構(gòu)建命令后面加上 “--no-shrink” 表示不混淆代碼:
flutter build apk --no-shrink - 如果你的程序入口不是“main.dart”,而是像我一樣改成“MyApp.dart”的話,需要在構(gòu)建命令后面加上 “--target=lib/MyApp.dart” 指定程序入口:
flutter build apk --target=lib/MyApp.dart 或者 flutter build ios --target=lib/MyApp.dart - 在Android上如果你覺得生成出來的apk包太大,或者覺得只需要適配單獨一種架構(gòu)的手機就可以了,你也可以單獨構(gòu)建一種架構(gòu)的apk包:
此命令會生成三種架構(gòu)單獨的apk包:app-armeabi-v7a-release.apk、app-arm64-v8a-release.apk、app-x86_64-release.apk,大大減少apk包的大小。flutter build apk --target=lib/MyApp.dart --no-shrink --split-per-abi 或者 flutter build apk --target=lib/MyApp.dart --no-shrink --target-platform android-arm,android-arm64,android-x64 --split-per-abi
問題
-
報錯如下:
Execution failed for task ':app:lintVitalRelease'. > Could not resolve all artifacts for configuration ':app:profileRuntimeClasspath'. > Failed to transform libs.jar to match attributes {artifactType=processed-jar, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime}. > Execution failed for JetifyTransform: 項目\build\app\intermediates\flutter\profile\libs.jar. > Transform's input file does not exist: 項目\build\app\intermediates\flutter\profile\libs.jar. (See https://issuetracker.google.com/issues/158753935)解決辦法是先運行一次 debug 或者 profile 模式:
// debug flutter run 和 // profile flutter run --profile像上面的報錯的意思是找不到 profile 模式下的 libs.jar 文件,我只需要命令行運行一次 profile 模式就可以了。
-
目前發(fā)現(xiàn)且未解決的問題是在混淆之后,應(yīng)用所依賴的第三方pub插件(sqflite、dio等)無法正常使用,具體還需進一步測試確認(rèn)。
- 2021年1月12日更新:
使用一個空包對dio和sqflite進行混淆測試,build.gradle 和 proguard-rules.pro 配置如下:android { ... buildTypes { release { signingConfig signingConfigs.debug minifyEnabled true useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
測試結(jié)果正常,但是發(fā)現(xiàn)混淆對泛型十分不友好,很可能因為泛型導(dǎo)致混淆之后APP不能正常使用,比如我在 Flutter Dio二次封裝 這篇文章里面的那個json轉(zhuǎn)換工廠 EntityFactory:#默認(rèn)的proguard-android.txt已經(jīng)增加了Annotation、native、view的setget方法、Activity參數(shù)為view的 方法、Enum枚舉、Parcelable、R,此處不再寫 #------------------------------------------通用區(qū)域---------------------------------------------------- #----------------------基本指令------------------------ -optimizationpasses 5 -dontusemixedcaseclassnames -dontskipnonpubliclibraryclasses -dontskipnonpubliclibraryclassmembers -dontpreverify -verbose -printmapping proguardMapping.txt -optimizations !code/simplification/cast,!field/*,!class/merging/* -keepattributes *Annotation*,InnerClasses -keepattributes Signature -keepattributes SourceFile,LineNumberTable #如果引用了v4或者v7包 -dontwarn android.support.** -keep class android.support.** { *; } -keep interface android.support.** { *; } -keep public class * extends android.support.** -dontwarn android.support.** #如果引用了androidx包 -keep class com.google.android.material.** {*;} -keep class androidx.** {*;} -keep public class * extends androidx.** -keep interface androidx.** {*;} -dontwarn com.google.android.material.** -dontnote com.google.android.material.** -dontwarn androidx.** #---------------------默認(rèn)保留------------------------- ## 基礎(chǔ)保留 ## -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.app.backup.BackupAgentHelper -keep public class * extends android.preference.Preference -keep public class com.android.vending.licensing.ILicensingService -keep 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*(...); } # 保持自定義控件類不被混淆 -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); } #保持自定義控件類不被混淆 -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } -keepclassmembers enum * { # 保持枚舉 enum 類不被混淆 public static **[] values(); public static ** valueOf(java.lang.String); } -keep class * implements android.os.Parcelable { # 保持 Parcelable 不被混淆 public static final android.os.Parcelable$Creator *; } -keep class * implements java.io.Serializable # 保持 Serializable 不被混淆 #保持 Serializable 不被混淆并且enum 類也不被混淆 -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(); } #不混淆資源類 -keepclassmembers class **.R$* { public static <fields>; } # 保持 native 方法不被混淆 -keepclasseswithmembernames class * { native <methods>; } #EventBus的注解 -keepclassmembers class * { @org.greenrobot.eventbus.Subscribe <methods>; } #WebView -keepclassmembers class * extends android.webkit.WebView {*;} -keepclassmembers class * extends android.webkit.WebViewClient {*;} -keepclassmembers class * extends android.webkit.WebChromeClient {*;} -keepclassmembers class * { @android.webkit.JavascriptInterface <methods>; } #-------------------------------------------項目定義區(qū)------------------------------------------------- #sqflite -keep class com.tekartik.sqflite.** { *; } #Flutter Wrapper #-dontwarn io.flutter.** #-keep class io.flutter.app.** { *; } #-keep class io.flutter.plugin.** { *; } #-keep class io.flutter.util.** { *; } #-keep class io.flutter.view.** { *; } #-keep class io.flutter.** { *; } #-keep class io.flutter.plugins.** { *; }
這里需要使用 T.toString() == "LoginEntity" 對泛型進行具體的判斷,但是混淆之后泛型 T 傳進來的是混淆之后的符號,所以導(dǎo)致 T.toString() == "LoginEntity" 判斷失敗無法使用具體的實體去解析 json,我想下一步需要找辦法配置對某些文件進行混淆排除。。class EntityFactory { static T generateOBJ<T>(json) { if (json == null) { return null; } else if (T.toString() == "LoginEntity") { return LoginEntity.fromJson(json) as T; } else { return json as T; } } }
- 2021年1月12日更新: