DexKnifePlugin源码解析

LeonBowie 7年前
   <p>DexKnifePlugin 是一个简单的将指定使用通配符包名分包到第二个dex中gradle插件。</p>    <h2><strong>DexKnifePlugin</strong></h2>    <p>一个简单的将指定使用通配符包名分包到第二个dex中gradle插件。</p>    <p>同时支持 android gradle plugin 2.2.0 multidex.</p>    <h2><strong>使用</strong></h2>    <ol>     <li> <p>在 project 的 build.gradle 添加依赖:</p> <pre>  <code class="language-java">buildscript {          ....        dependencies {          ....          classpath 'com.android.tools.build:gradle:2.2.0-beta2'  // or other          classpath 'com.ceabie.dextools:gradle-dexknife-plugin:1.5.6'      }  }  </code></pre> <p>注意,请确保使用的gradle版本和android gradle plugin兼容,否则会出现同步错误,例如:Gradle sync failed: Unable to load class ‘com.android.builder.core.EvaluationErrorReporter’.</p> </li>     <li> <p>在 app module 下创建 dexknife.txt ,并填写要放到第二个 dex 中的包名路径的通配符:</p> <pre>  <code class="language-java">Patterns may include:    '*' to match any number of characters  '?' to match any single character  '**' to match any number of directories or files  Either '.' or '/' may be used in a pattern to separate directories.  Patterns ending with '.' or '/' will have '**' automatically appended.  </code></pre> <p>注意: 如果你要过滤内部类, 使用 $* ,例如: SomeClass$*.class</p> <p>其他配置:</p> <pre>  <code class="language-java">使用 # 进行注释, 当行起始加上 #, 这行配置被禁用.    # 全局过滤, 如果没设置 -filter-suggest 并不会应用到 建议的maindexlist.  # 如果你想要某个包路径在maindex中,则使用 -keep 选项,即使他已经在分包的路径中.  -keep android.support.v4.view.**    # 这条配置可以指定这个包下类在第二dex中.  android.support.v?.**    # 使用.class后缀,代表单个类.  -keep android.support.v7.app.AppCompatDialogFragment.class    # 不包含Android gradle 插件自动生成的miandex列表.  -donot-use-suggest    # 将 全局过滤配置应用到 建议的maindexlist中, 但 -donot-use-suggest 要关闭.  -filter-suggest    # 不进行dex分包, 直到 dex 的id数量超过 65536.  -auto-maindex    # dex 扩展参数, 例如 --set-max-idx-number=50000  # 如果出现 DexException: Too many classes in --main-dex-list, main dex capacity exceeded,则需要调大数值  -dex-param --set-max-idx-number=50000    # 显示miandex的日志.  -log-mainlist    # 如果你只想过滤 建议的maindexlist, 使用 -suggest-split 和 -suggest-keep.  # 如果同时启用 -filter-suggest, 全局过滤会合并到它们中.  -suggest-split **.MainActivity2.class  -suggest-keep android.support.multidex.**  </code></pre> </li>     <li> <p>在 app module 的 build.grade 增加:</p> <pre>  <code class="language-java">apply plugin: 'com.ceabie.dexnkife'  </code></pre> </li>     <li> <p>最后,在 app module 中设置:</p> <pre>  <code class="language-java">multiDexEnabled true  </code></pre> <p>注意:要在 defaultConfig 或者 buildTypes中打开 multiDexEnabled true,否则不起作用。</p> </li>    </ol>    <h2><strong>源码解析</strong></h2>    <p>因为 DexKnifePlugin 这个工程是一个 gradle 的插件,所以在看源码之前得对 gradle 有一些了解。</p>    <h3>DexKnifePlugin</h3>    <p>直接看 DexKnifePlugin.groovy 这个文件:</p>    <pre>  <code class="language-java">public class DexKnifePlugin implements Plugin<Project> {      @Override      void apply(Project project) {              }  }  </code></pre>    <p>这里就是该插件, apply 便是该插件的入口:</p>    <pre>  <code class="language-java">public class DexKnifePlugin implements Plugin<Project> {        @Override      void apply(Project project) {          //gradle配置阶段完成后调用          project.afterEvaluate {              for (variant in project.android.applicationVariants) {                  if (isMultiDexEnabled(variant)) {                      if (SplitToolsFor130.isCompat(variant)) {//1.3.0版本                          System.err.println("DexKnife: Compat 1.3.0.");                          SplitToolsFor130.processSplitDex(project, variant)                      } else if (SplitToolsFor150.isCompat()) {//1.5.0及之后的版本                          SplitToolsFor150.processSplitDex(project, variant)                      } else {                          System.err.println("DexKnife Error: DexKnife is not compatible your Android gradle plugin.");                      }                  } else {                      System.err.println("DexKnife : MultiDexEnabled is false, it's not work.");                  }              }          }      }        /**   * 是否开启的分包   * @param variant   * @return   */      private static boolean isMultiDexEnabled(variant) {          def is = variant.buildType.multiDexEnabled   if (is != null) {              return is;          }          is = variant.mergedFlavor.multiDexEnabled   if (is != null) {              return is;          }          return false      }  }  </code></pre>    <p>在 gradle 配置阶段完成之后,去判断当前 gradle 插件版本,然后分配去做操作。</p>    <p>我们先来看 1.3.0 的吧</p>    <h3><strong>SplitToolsFor130</strong></h3>    <pre>  <code class="language-java">public class SplitToolsFor130 extends DexSplitTools {        public static boolean isCompat(Object variant) {          try {              if (variant != null) {                  //看看是不是有这个dex的task                  variant.dex                  return true              }          } catch (RuntimeException e) {  // e.printStackTrace()          }          return false      }        public static void processSplitDex(Project project, Object variant) {          def dex = variant.dex          if (dex.multiDexEnabled) {//是否开启的分包              dex.inputs.file DEX_KNIFE_CFG_TXT              dex.doFirst {                  //log,记录当前时间                  startDexKnife()                  //通过解析dexknife.txt得到配置                  DexKnifeConfig dexKnifeConfig = getDexKnifeConfig(project)                    def scope = variant.getVariantData().getScope()                  File mergedJar = scope.jarMergingOutputFile//allclasses.jar                  File mappingFile = variant.mappingFile//mapping.txt                  File andMainDexList = scope.mainDexListFile//maindexlist.txt                  boolean minifyEnabled = variant.buildType.minifyEnabled//build.gradle中的『buildTypes』中的『release』或者『debug』中的minifyEnabled,debug和release的时候默认为false                    if (processMainDexList(project, minifyEnabled, mappingFile, mergedJar, andMainDexList, dexKnifeConfig)) {                      if (dex.additionalParameters == null) {                          dex.additionalParameters = []                      }                        dex.additionalParameters += '--main-dex-list=maindexlist.txt'                      dex.additionalParameters += dexKnifeConfig.additionalParameters//其他通过dexknife.txt设置的dx参数                  }                  //log,打印花费时间                  endDexKnife()              }          }      }  }  </code></pre>    <p>先找到 dex 这个 task ,然后主要的过程还是在 processMainDexList 中,进行完这个操作之后设置 additionalParameters 参数, processMainDexList 方法在父类 DexSplitTools 中:</p>    <h3><strong>DexSplitTools</strong></h3>    <pre>  <code class="language-java">public class DexSplitTools {          public static final String DEX_KNIFE_CFG_TXT = "dexknife.txt";        private static final String DEX_MINIMAL_MAIN_DEX = "--minimal-main-dex";        private static final String DEX_KNIFE_CFG_DEX_PARAM = "-dex-param";      private static final String DEX_KNIFE_CFG_SPLIT = "-split";      private static final String DEX_KNIFE_CFG_KEEP = "-keep";      private static final String DEX_KNIFE_CFG_AUTO_MAINDEX = "-auto-maindex";      private static final String DEX_KNIFE_CFG_DONOT_USE_SUGGEST = "-donot-use-suggest";      private static final String DEX_KNIFE_CFG_LOG_MAIN_DEX = "-log-mainlist";      private static final String DEX_KNIFE_CFG_FILTER_SUGGEST = "-filter-suggest";      private static final String DEX_KNIFE_CFG_SUGGEST_SPLIT = "-suggest-split";      private static final String DEX_KNIFE_CFG_SUGGEST_KEEP = "-suggest-keep";      private static final String DEX_KNIFE_CFG_LOG_FILTER_SUGGEST = "-log-filter-suggest";         /**   * get the config of dex knife   */      protected static DexKnifeConfig getDexKnifeConfig(Project project) throws Exception {          //读文件          BufferedReader reader = new BufferedReader(new FileReader(project.file(DEX_KNIFE_CFG_TXT)));          //申明变量,该变量存储文件中的信息          DexKnifeConfig dexKnifeConfig = new DexKnifeConfig();            String line;          boolean matchCmd;          boolean minimalMainDex = true;          Set<String> addParams = new HashSet<>();            Set<String> splitToSecond = new HashSet<>();          Set<String> keepMain = new HashSet<>();          Set<String> splitSuggest = new HashSet<>();          Set<String> keepSuggest = new HashSet<>();            while ((line = reader.readLine()) != null) {              line = line.trim();              if (line.length() == 0) {                  continue;              }                int rem = line.indexOf('#');//查找注释的地方              if (rem != -1) {                  if (rem == 0) {//该段落为注释                      continue;                  } else {                      line = line.substring(0, rem).trim();//获取出内容                  }              }                String cmd = line.toLowerCase();              matchCmd = true;                if (DEX_KNIFE_CFG_AUTO_MAINDEX.equals(cmd)) {//-auto-maindex(不进行dex分包, 直到 dex 的id数量超过 65536.)                  minimalMainDex = false;              } else if (matchCommand(cmd, DEX_KNIFE_CFG_DEX_PARAM)) {//-dex-param(dex 扩展参数, 例如 --set-max-idx-number=50000)                  String param = line.substring(DEX_KNIFE_CFG_DEX_PARAM.length()).trim();                  if (!param.toLowerCase().startsWith("--main-dex-list")) {                      addParams.add(param);                  }                } else if (matchCommand(cmd, DEX_KNIFE_CFG_SPLIT)) {//-split                  String sPattern = line.substring(DEX_KNIFE_CFG_SPLIT.length()).trim();                  addClassFilePath(sPattern, splitToSecond);                } else if (matchCommand(cmd, DEX_KNIFE_CFG_KEEP)) {//-keep                  String sPattern = line.substring(DEX_KNIFE_CFG_KEEP.length()).trim();                  addClassFilePath(sPattern, keepMain);                } else if (DEX_KNIFE_CFG_DONOT_USE_SUGGEST.equals(cmd)) {//-donot-use-suggest(不包含Android gradle 插件自动生成的miandex列表)                  dexKnifeConfig.useSuggest = false;                } else if (DEX_KNIFE_CFG_FILTER_SUGGEST.equals(cmd)) {//-filter-suggest(将 全局过滤配置应用到 建议的maindexlist中, 但 -donot-use-suggest 要关闭)                  dexKnifeConfig.filterSuggest = true;                } else if (DEX_KNIFE_CFG_LOG_MAIN_DEX.equals(cmd)) {//-log-mainlist(显示miandex的日志)                  dexKnifeConfig.logMainList = true;                } else if (DEX_KNIFE_CFG_LOG_FILTER_SUGGEST.equals(cmd)) {//-log-filter-suggest(显示过滤的日志)                  dexKnifeConfig.logFilterSuggest = true;                } else if (matchCommand(cmd, DEX_KNIFE_CFG_SUGGEST_SPLIT)) {//-suggest-split(要在主dex排除掉的类)                  String sPattern = line.substring(DEX_KNIFE_CFG_SUGGEST_SPLIT.length()).trim();                  addClassFilePath(sPattern, splitSuggest);                } else if (matchCommand(cmd, DEX_KNIFE_CFG_SUGGEST_KEEP)) {//-suggest-keep(要在主dex保留的类)                  String sPattern = line.substring(DEX_KNIFE_CFG_SUGGEST_KEEP.length()).trim();                  addClassFilePath(sPattern, keepSuggest);                } else if (!cmd.startsWith("-")) {                  addClassFilePath(line, splitToSecond);              } else {                  matchCmd = false;              }                if (matchCmd) {                  System.out.println("DexKnife Config: " + line);              }          }            reader.close();            if (minimalMainDex) {//添加--minimal-main-dex参数              addParams.add(DEX_MINIMAL_MAIN_DEX);          }            if (dexKnifeConfig.useSuggest) {              if (dexKnifeConfig.filterSuggest) {                  splitSuggest.addAll(splitToSecond);                  keepSuggest.addAll(keepMain);              }    // for (String s : splitSuggest) {  // System.out.println("Suggest: " + s);  // }                if (!splitSuggest.isEmpty() || !keepSuggest.isEmpty()) {                  dexKnifeConfig.suggestPatternSet = new PatternSet()                          .exclude(splitSuggest)                          .include(keepSuggest);              }          }              if (!splitToSecond.isEmpty() || !keepMain.isEmpty()) {  // for (String s : splitToSecond) {  // System.out.println(s);  // }              dexKnifeConfig.patternSet = new PatternSet()                      .exclude(splitToSecond)                      .include(keepMain);          } else {              dexKnifeConfig.useSuggest = true;              System.err.println("DexKnife Warning: NO SET split Or keep path, it will use Suggest!");          }            dexKnifeConfig.additionalParameters = addParams;            return dexKnifeConfig;      }        private static boolean matchCommand(String text, String cmd) {          Pattern pattern = Pattern.compile("^" + cmd + "\\s+");          return pattern.matcher(text).find();      }        /**   * add the class path to pattern list, and the single class pattern can work.   */      private static void addClassFilePath(String classPath, Set<String> patternList) {          if (classPath != null && classPath.length() > 0) {              if (classPath.endsWith(CLASS_SUFFIX)) {//以.class结尾                  classPath = classPath.substring(0, classPath.length() - CLASS_SUFFIX.length()).replace('.', '/') + CLASS_SUFFIX;//转化成路径形式              } else {                  classPath = classPath.replace('.', '/');//转化成路径形式              }              //添加到patternList中              patternList.add(classPath);          }      }          public static boolean processMainDexList(Project project, boolean minifyEnabled, File mappingFile,   File jarMergingOutputFile, File andMainDexList,   DexKnifeConfig dexKnifeConfig) throws Exception {          //当minifyEnabled为false的时候,那么jarMergingOutputFile必定存在          //当minifyEnabled为true的时候,那么jarMergingOutputFile可能不存在,因为此时可能打的release包,就不是allclass.jar了          if (!minifyEnabled && jarMergingOutputFile == null) {              System.out.println("DexKnife Error: jarMerging is Null! Skip DexKnife. Please report All Gradle Log.");              return false;          }          return genMainDexList(project, minifyEnabled, mappingFile, jarMergingOutputFile, andMainDexList, dexKnifeConfig);      }          private static boolean genMainDexList(Project project, boolean minifyEnabled,   File mappingFile, File jarMergingOutputFile,   File andMainDexList, DexKnifeConfig dexKnifeConfig) throws Exception {            System.out.println(":" + project.getName() + ":genMainDexList");            // get the adt's maindexlist          HashSet<String> mainCls = null;          if (dexKnifeConfig.useSuggest) {//使用gradle生成的maindexlist              PatternSet patternSet = dexKnifeConfig.suggestPatternSet;//-suggest-split和-suggest-keep              if (dexKnifeConfig.filterSuggest && patternSet == null) {                  patternSet = dexKnifeConfig.patternSet;//-split和-keep              }              mainCls = getAdtMainDexClasses(andMainDexList, patternSet, dexKnifeConfig.logFilterSuggest);              System.out.println("DexKnife: use suggest");          }            File keepFile = project.file(MAINDEXLIST_TXT);          keepFile.delete();            ArrayList<String> mainClasses = null;          if (minifyEnabled) {              System.err.println("DexKnife: From Mapping");              // get classes from mapping              mainClasses = getMainClassesFromMapping(mappingFile, dexKnifeConfig.patternSet, mainCls);          } else {              System.out.println("DexKnife: From MergedJar: " + jarMergingOutputFile);              if (jarMergingOutputFile != null) {                  // get classes from merged jar                  mainClasses = getMainClassesFromJar(jarMergingOutputFile, dexKnifeConfig.patternSet, mainCls);              } else {                  System.err.println("DexKnife: The Merged Jar is not exist! Can't be processed!");              }          }            if (mainClasses != null && mainClasses.size() > 0) {              BufferedWriter writer = new BufferedWriter(new FileWriter(keepFile));//写到app module的maindexlist.txt中              for (String mainClass : mainClasses) {                  writer.write(mainClass);                  writer.newLine();                  if (dexKnifeConfig.logMainList) {                      System.out.println(mainClass);                  }              }              writer.close();              return true;          }                    throw new Exception("DexKnife Warning: Main dex is EMPTY ! Check your config and project!");      }          private static HashSet<String> getAdtMainDexClasses(File outputDir, PatternSet mainDexPattern, boolean logFilter)   throws Exception {          if (outputDir == null || !outputDir.exists()) {              System.err.println("DexKnife Warning: Android recommand Main dex is no exist, try run again!");              return null;          }            HashSet<String> mainCls = new HashSet<>();          BufferedReader reader = new BufferedReader(new FileReader(outputDir));            ClassFileTreeElement treeElement = new ClassFileTreeElement();          //将mainDexPattern转成Spec<FileTreeElement>格式          Spec<FileTreeElement> asSpec = mainDexPattern != null ? getMaindexSpec(mainDexPattern) : null;            String line, clsPath;          while ((line = reader.readLine()) != null) {              line = line.trim();              int clsPos = line.lastIndexOf(CLASS_SUFFIX);              if (clsPos != -1) {                  if (asSpec != null) {//设置了过滤的情况                      clsPath = line.substring(0, clsPos).replace('.', '/') + CLASS_SUFFIX;//转路径                      treeElement.setClassPath(clsPath);//设置路径                      boolean satisfiedBy = asSpec.isSatisfiedBy(treeElement);                      if (!satisfiedBy) {                          if (logFilter) {                              System.out.println("DexKnife-Suggest: [Split] " + clsPath);                          }                          continue;                      }                      if (logFilter) {                          System.out.println("DexKnife-Suggest: [Keep] " + clsPath);                      }                  }                  //满足的加到mainCls中                  mainCls.add(line);              }          }          reader.close();          if (mainCls.size() == 0) {              mainCls = null;          }          return mainCls;      }          private static Spec<FileTreeElement> getMaindexSpec(PatternSet patternSet) {          Spec<FileTreeElement> maindexSpec = null;            if (patternSet != null) {              Spec<FileTreeElement> includeSpec = null;              Spec<FileTreeElement> excludeSpec = null;                if (!patternSet.getIncludes().isEmpty()) {                  includeSpec = patternSet.getAsIncludeSpec();              }                if (!patternSet.getExcludes().isEmpty()) {                  excludeSpec = patternSet.getAsExcludeSpec();              }                if (includeSpec != null && excludeSpec != null) {                  maindexSpec = Specs.or(includeSpec, Specs.not(excludeSpec));              } else {                  maindexSpec = excludeSpec != null ? Specs.not(excludeSpec) : includeSpec;              }          }            if (maindexSpec == null) {              maindexSpec = Specs.satisfyNone();          }            return maindexSpec;      }          /**   * Gets main classes from mapping.   *   * @param mapping the mapping file   * @param mainDexPattern the main dex pattern   * @param mainCls the main cls   * @return the main classes from mapping   * @throws Exception the exception   * @author ceabie   */      private static ArrayList<String> getMainClassesFromMapping(   File mapping,   PatternSet mainDexPattern,   HashSet<String> mainCls) throws Exception {            String line;          ArrayList<String> mainDexList = new ArrayList<>();          BufferedReader reader = new BufferedReader(new FileReader(mapping));            ClassFileTreeElement treeElement = new ClassFileTreeElement();          Spec<FileTreeElement> asSpec = getMaindexSpec(mainDexPattern);            while ((line = reader.readLine()) != null) {              line = line.trim();                if (line.endsWith(":")) {                  int flagPos = line.indexOf(MAPPING_FLAG);//找『 -> 』                  if (flagPos != -1) {                      String sOrg = line.substring(0, flagPos).replace('.', '/') + CLASS_SUFFIX;//获取前面的,是混淆前的                      treeElement.setClassPath(sOrg);//设置路径                        if (asSpec.isSatisfiedBy(treeElement)                              || (mainCls != null && mainCls.contains(sOrg))) {                          String sMap = line.substring(flagPos + MAPPING_FLAG_LEN, line.length() - 1).replace('.', '/') + CLASS_SUFFIX;//得到混淆后的                          mainDexList.add(sMap);//添加到mainDexList中                      }                  }              }          }          reader.close();          return mainDexList;      }          private static ArrayList<String> getMainClassesFromJar(   File jarMergingOutputFile, PatternSet mainDexPattern, HashSet<String> mainCls) throws Exception {          ZipFile clsFile = new ZipFile(jarMergingOutputFile);//allclass.jar          Spec<FileTreeElement> asSpec = getMaindexSpec(mainDexPattern);          ClassFileTreeElement treeElement = new ClassFileTreeElement();            ArrayList<String> mainDexList = new ArrayList<>();          Enumeration<? extends ZipEntry> entries = clsFile.entries();          while (entries.hasMoreElements()) {              ZipEntry entry = entries.nextElement();              String entryName = entry.getName();//得到全名称                if (entryName.endsWith(CLASS_SUFFIX)) {                  treeElement.setClassPath(entryName);                  if (asSpec.isSatisfiedBy(treeElement)                          || (mainCls != null && mainCls.contains(entryName))) {                      mainDexList.add(entryName);//写到mainDexList中                  }              }          }          clsFile.close();          return mainDexList;      }  }  </code></pre>    <p>先通过 getDexKnifeConfig() 来得到配置,然后通过 genMainDexList() 将配置中设置的一些类写入到 maindexlist.txt 中。这里需要注意一下 buildType 的 minifyEnabled,一般在 debug 的时候都没有设置这个参数,默认为 false,当 release 的时候设置该参数为 true,那么会进行混淆工作,所以这里如果该参数为 true 的话直接取读的 mapping 文件。</p>    <h3><strong>SplitToolsFor150</strong></h3>    <pre>  <code class="language-java">public class SplitToolsFor150 extends DexSplitTools {        public static boolean isCompat() {  // if (getAndroidPluginVersion() < 200) {  // return true;  // }            return true;      }        public static void processSplitDex(Project project, ApplicationVariant variant) {          //instantRun开启的话就跳过了          if (isInInstantRunMode(variant)) {              System.err.println("DexKnife: Instant Run mode, DexKnife is auto disabled!")              return          }            TransformTask dexTask  // TransformTask proGuardTask          TransformTask jarMergingTask            String name = variant.name.capitalize()//Debug或者Release          boolean minifyEnabled = variant.buildType.minifyEnabled            // find the task we want to process          project.tasks.matching {              ((it instanceof TransformTask) && it.name.endsWith(name)) // TransformTask          }.each { TransformTask theTask ->              Transform transform = theTask.transform              String transformName = transform.name  // if (minifyEnabled && "proguard".equals(transformName)) { // ProGuardTransform  // proGuardTask = theTask  // } else              if ("jarMerging".equalsIgnoreCase(transformName)) {//jarMerging                  jarMergingTask = theTask//transformClassWithJarMerging的时候执行              } else if ("dex".equalsIgnoreCase(transformName)) { // DexTransform                  dexTask = theTask//transformClassWithDex的时候执行              }          }            if (dexTask != null && ((DexTransform) dexTask.transform).multiDex) {              dexTask.inputs.file DEX_KNIFE_CFG_TXT                dexTask.doFirst {                  //记录开始时间                  startDexKnife()                    File mergedJar = null                  File mappingFile = variant.mappingFile//mapping.txt                  DexTransform dexTransform = it.transform                  File fileAdtMainList = dexTransform.mainDexListFile//maindexlist文件                    println("DexKnife Adt Main: " + fileAdtMainList)                    DexKnifeConfig dexKnifeConfig = getDexKnifeConfig(project)//获取配置                    // 非混淆的,从合并后的jar文件中提起maindexlist;                  // 混淆的,直接从mapping文件中提取                  if (minifyEnabled) {                      println("DexKnife-From Mapping: " + mappingFile)                  } else {                      if (jarMergingTask != null) {                          Transform transform = jarMergingTask.transform                          def outputProvider = jarMergingTask.outputStream.asOutput()                          mergedJar = outputProvider.getContentLocation("combined",                                  transform.getOutputTypes(),                                  transform.getScopes(), Format.JAR)//得到jar                      }                          println("DexKnife-From MergedJar: " + mergedJar)                  }                    //与130一样                  if (processMainDexList(project, minifyEnabled, mappingFile, mergedJar,                          fileAdtMainList, dexKnifeConfig)) {                        //得到version                      int version = getAndroidPluginVersion(getAndroidGradlePluginVersion())                      println("DexKnife: AndroidPluginVersion: " + version)                        // after 2.2.0, it can additionalParameters, but it is a copy in task  // if (version >= 220) {  // DexOptions dexOptions = project.android.dexOptions;  // InjectAndroidBuilder.mergeParams(dexOptions.getAdditionalParameters(),  // dexKnifeConfig.additionalParameters)  // }                        // 替换 AndroidBuilder                      InjectAndroidBuilder.proxyAndroidBuilder(dexTransform,                              dexKnifeConfig.additionalParameters)                        // 替换这个文件                      fileAdtMainList.delete()                      project.copy {                          from 'maindexlist.txt'                          into fileAdtMainList.parentFile                      }                  }                  //记录并打印执行时间                  endDexKnife()              }          }      }        private static boolean isInInstantRunMode(Object variant) {          try {              def scope = variant.getVariantData().getScope()              InstantRunBuildContext instantRunBuildContext = scope.getInstantRunBuildContext()              return instantRunBuildContext.isInInstantRunMode()          } catch (Throwable e) {          }            return false      }  }  </code></pre>    <p>主要的 processMainDexList 与 130 的一样,这里就不多说了,再来看看是怎么通过 maindexlist.txt 来实现分包的:</p>    <h3><strong>InjectAndroidBuilder</strong></h3>    <pre>  <code class="language-java">public class InjectAndroidBuilder extends AndroidBuilder {        /**   * addParams 就是130的dex的那些分包参数   * @param transform   * @param addParams   */      public static void proxyAndroidBuilder(DexTransform transform, Collection<String> addParams) {          if (addParams != null && addParams.size() > 0) {              //反射,替换成自己的              accessibleField(DexTransform.class, "androidBuilder")                      .set(transform, getProxyAndroidBuilder(transform.androidBuilder, addParams))          }      }        /**   * new一个自己的出来   * @param orgAndroidBuilder   * @param addParams   * @return   */      private static AndroidBuilder getProxyAndroidBuilder(AndroidBuilder orgAndroidBuilder,   Collection<String> addParams) {          InjectAndroidBuilder myAndroidBuilder = new InjectAndroidBuilder(                  orgAndroidBuilder.mProjectId,                  orgAndroidBuilder.mCreatedBy,                  orgAndroidBuilder.getProcessExecutor(),                  orgAndroidBuilder.mJavaProcessExecutor,                  orgAndroidBuilder.getErrorReporter(),                  orgAndroidBuilder.getLogger(),                  orgAndroidBuilder.mVerboseExec)            // if >= 2.2.0          def to = myAndroidBuilder.respondsTo("setTargetInfo", TargetInfo.class)          //分版本适配          if (to.size() > 0) {              myAndroidBuilder.setTargetInfo(orgAndroidBuilder.getTargetInfo())              myAndroidBuilder.setSdkInfo(orgAndroidBuilder.getSdkInfo())              myAndroidBuilder.setLibraryRequests(orgAndroidBuilder.mLibraryRequests)          } else {              myAndroidBuilder.setTargetInfo(                      orgAndroidBuilder.getSdkInfo(),                      orgAndroidBuilder.getTargetInfo(),                      orgAndroidBuilder.mLibraryRequests)          }          //将参数传入          myAndroidBuilder.mAddParams = addParams          myAndroidBuilder.mAndroidBuilder = orgAndroidBuilder  // myAndroidBuilder.mBootClasspathFiltered = orgAndroidBuilder.mBootClasspathFiltered  // myAndroidBuilder.mBootClasspathAll = orgAndroidBuilder.mBootClasspathAll            return myAndroidBuilder      }        @CompileStatic      private static Field accessibleField(Class cls, String field) {          Field f = cls.getDeclaredField(field)          f.setAccessible(true)          return f      }          Collection<String> mAddParams;      AndroidBuilder mAndroidBuilder;        public InjectAndroidBuilder(String projectId,   String createdBy,   ProcessExecutor processExecutor,   JavaProcessExecutor javaProcessExecutor,   ErrorReporter errorReporter,   ILogger logger,   boolean verboseExec) {          super(projectId, createdBy, processExecutor, javaProcessExecutor, errorReporter, logger, verboseExec)      }    // @Override // for < 2.2.0      public void convertByteCode(Collection<File> inputs,   File outDexFolder,   boolean multidex,   File mainDexList,   DexOptions dexOptions,   List<String> additionalParameters,   boolean incremental,   boolean optimize,   ProcessOutputHandler processOutputHandler)   throws IOException, InterruptedException, ProcessException {            println("DexKnife: convertByteCode before 2.2.0")          if (mAddParams != null) {              if (additionalParameters == null) {                  additionalParameters = new ArrayList<>()              }              //将参数添加到additionalParameters中              mergeParams(additionalParameters, mAddParams)          }            // groovy call super has bug          mAndroidBuilder.convertByteCode(inputs, outDexFolder, multidex, mainDexList, dexOptions,                  additionalParameters, incremental, optimize, processOutputHandler);      }    // @Override for >= 2.2.0      public void convertByteCode(Collection<File> inputs,   File outDexFolder,   boolean multidex,   File mainDexList,   final DexOptions dexOptions,   boolean optimize,   ProcessOutputHandler processOutputHandler)   throws IOException, InterruptedException, ProcessException {            println("DexKnife:convertByteCode after 2.2.0")            DexOptions dexOptionsProxy = dexOptions     if (mAddParams != null) {              List<String> additionalParameters = dexOptions.getAdditionalParameters()              if (additionalParameters == null) {                  additionalParameters = new ArrayList<>()              }                mergeParams(additionalParameters, mAddParams)          }            mAndroidBuilder.convertByteCode(inputs, outDexFolder, multidex, mainDexList, dexOptionsProxy,                  optimize, processOutputHandler);      }        @CompileStatic      @Override      List<File> getBootClasspath(boolean includeOptionalLibraries) {          return mAndroidBuilder.getBootClasspath(includeOptionalLibraries)      }        @CompileStatic      @Override      List<String> getBootClasspathAsStrings(boolean includeOptionalLibraries) {          return mAndroidBuilder.getBootClasspathAsStrings(includeOptionalLibraries)      }          @CompileStatic      static void mergeParams(List<String> additionalParameters, Collection<String> addParams) {          List<String> mergeParam = new ArrayList<>()          for (String param : addParams) {              if (!additionalParameters.contains(param)) {                  mergeParam.add(param)              }          }            if (mergeParam.size() > 0) {              additionalParameters.addAll(mergeParam)          }      }  }  </code></pre>    <p>通过反射将 AndroidBuilder 替换成自己的,将分包的参数加上,最终是通过 AndroidBuilder#convertByteCode() 写进去的。</p>    <h2><strong>总结</strong></h2>    <ul>     <li>通过插件的方式很赞,使用的人不需要做太多配置,只需要将插件设置进来便可</li>     <li>通过 dexknife.txt 方式来配置也很赞</li>     <li>1.5.0 之后的方式很赞</li>     <li>-split 和 -keep 以及 -suggest-split 和 -suggest-keep 等,参数实在过多</li>    </ul>    <p> </p>    <p>来自:http://yydcdut.com/2016/09/25/dexknifeplugin-analyse/</p>    <p> </p>