Flutter 混淆打包以及一些注意事项

混淆Dart代码

Flutter 1.16.2 以上默认支持混淆,不需要特殊设置,只需要在构建命令后面加上

--obfuscate --split-debug-info=//

具体可看官方文档:混淆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

注意:

  1. 如果不想混淆代码的话,iOS只需要去掉 “--obfuscate --split-debug-info=./symbols” 进行构建就可以了,Android 的话单独去掉 “--obfuscate --split-debug-info=./symbols” 是没有用的,需要在构建命令后面加上 “--no-shrink” 表示不混淆代码:
    flutter build apk --no-shrink
    
  2. 如果你的程序入口不是“main.dart”,而是像我一样改成“MyApp.dart”的话,需要在构建命令后面加上 “--target=lib/MyApp.dart” 指定程序入口:
    flutter build apk --target=lib/MyApp.dart
    或者
    flutter build ios --target=lib/MyApp.dart
    
  3. 在Android上如果你觉得生成出来的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
    
    此命令会生成三种架构单独的apk包:app-armeabi-v7a-release.apk、app-arm64-v8a-release.apk、app-x86_64-release.apk,大大减少apk包的大小。

问题

  1. 报错如下:

    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 模式就可以了。

  2. 目前发现且未解决的问题是在混淆之后,应用所依赖的第三方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'
              }
          }
      }
      
      #默认的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 (android.content.Context);
          public (android.content.Context, android.util.AttributeSet);
          public (android.content.Context, android.util.AttributeSet, int);
          public void set*(...);
      }
      # 保持自定义控件类不被混淆
      -keepclasseswithmembers class * {
          public (android.content.Context, android.util.AttributeSet);
          public (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 ;
          !private ;
          !private ;
          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 ;
      }
      # 保持 native 方法不被混淆
      -keepclasseswithmembernames class * {
          native ;
      }
      #EventBus的注解
      -keepclassmembers class * {
          @org.greenrobot.eventbus.Subscribe ;
      }
      #WebView
      -keepclassmembers class * extends android.webkit.WebView {*;}
      -keepclassmembers class * extends android.webkit.WebViewClient {*;}
      -keepclassmembers class * extends android.webkit.WebChromeClient {*;}
      -keepclassmembers class * {
          @android.webkit.JavascriptInterface ;
      }
      
      #-------------------------------------------项目定义区-------------------------------------------------
      #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.**  { *; }
      
      测试结果正常,但是发现混淆对泛型十分不友好,很可能因为泛型导致混淆之后APP不能正常使用,比如我在 Flutter Dio二次封装 这篇文章里面的那个json转换工厂 EntityFactory:
      class EntityFactory {
        static T generateOBJ(json) {
          if (json == null) {
            return null;
          }
          else if (T.toString() == "LoginEntity") {
            return LoginEntity.fromJson(json) as T;
          }
          else {
            return json as T;
          }
        }
      }
      
      这里需要使用 T.toString() == "LoginEntity" 对泛型进行具体的判断,但是混淆之后泛型 T 传进来的是混淆之后的符号,所以导致 T.toString() == "LoginEntity" 判断失败无法使用具体的实体去解析 json,我想下一步需要找办法配置对某些文件进行混淆排除。。

你可能感兴趣的:(Flutter 混淆打包以及一些注意事项)