混淆Dart代码
Flutter 1.16.2 以上默认支持混淆,不需要特殊设置,只需要在构建命令后面加上
--obfuscate --split-debug-info=/<project-name>/<directory>
具体可看官方文档:混淆Dart代码
构建发布包
具体配置请看官方文档(只有很少的配置,很简单):生成并发布Android应用 、 生成并发布iOS应用
构建命令:
Android:
flutter build apk --obfuscate --split-debug-info=./symbols
Google目前建议Android使用 App Bundle 的发布格式,构建命令如下:
flutter build appbundle --target-platform android-arm,android-arm64,android-x64
有不知道 App Bundle 发布格式的同学可以查看官方文档(中文的): Android App Bundle 简介
iOS:
flutter build ios --obfuscate --split-debug-info=./symbols
注意:
- 如果不想混淆代码的话,iOS只需要去掉 “--obfuscate --split-debug-info=./symbols” 进行构建就可以了,Android 的话单独去掉 “--obfuscate --split-debug-info=./symbols” 是没有用的,需要在构建命令后面加上 “--no-shrink” 表示不混淆代码:
flutter build apk --no-shrink
- 如果你的程序入口不是“main.dart”,而是像我一样改成“MyApp.dart”的话,需要在构建命令后面加上 “--target=lib/MyApp.dart” 指定程序入口:
flutter build apk --target=lib/MyApp.dart 或者 flutter build ios --target=lib/MyApp.dart
- 在Android上如果你觉得生成出来的apk包太大,或者觉得只需要适配单独一种架构的手机就可以了,你也可以单独构建一种架构的apk包:
此命令会生成三种架构单独的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 模式就可以了。
-
目前发现且未解决的问题是在混淆之后,应用所依赖的第三方pub插件(sqflite、dio等)无法正常使用,具体还需进一步测试确认。
- 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' } } }
测试结果正常,但是发现混淆对泛型十分不友好,很可能因为泛型导致混淆之后APP不能正常使用,比如我在 Flutter Dio二次封装 这篇文章里面的那个json转换工厂 EntityFactory:#默认的proguard-android.txt已经增加了Annotation、native、view的setget方法、Activity参数为view的 方法、Enum枚举、Parcelable、R,此处不再写 #------------------------------------------通用区域---------------------------------------------------- #----------------------基本指令------------------------ -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.** #---------------------默认保留------------------------- ## 基础保留 ## -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>; } #-------------------------------------------项目定义区------------------------------------------------- #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 传进来的是混淆之后的符号,所以导致 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日更新: