DexKnifePlugin源码解析
LeonBowie
8年前
<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>