我们公司有个小哥对代码是比较严谨的。上一个方案他觉得不优雅,需要其他同事安装 python3 ,需要添加dx到PATH中。
为了更优雅,我决定此方案中所有的功能都在 build.gradle 里实现。原理还是那个:
在gradle 的 transformClassesWithDexFor* task 之后,自己生成 classes2.dex ,放到 classes.dex 所在目录
因为,build.gradle 会被翻译成 java 代码,所以,这些功能是肯定能被实现的。在很多次调试之后,下面就是可用的版本啦。
这个方案的缺陷还是: classes2.dex 所包含的代码没有经过全局的 proguard ,体积会稍稍大一些(也不会太大,如果单独做过合适的proguard后)。
class ClassesDexListener implements TaskExecutionListener { String dxCmd = ""; String dexBasePath = ""; String libs2Path = "" public ClassesDexListener(String dx, String path, String libs2Path) { this.dxCmd = dx; this.dexBasePath = path; this.libs2Path = libs2Path; } @Override void beforeExecute(Task task) { } @Override void afterExecute(Task task, TaskState taskState) { if(task.name.contains("transformClassesWithDexFor")) { generateCreateDex2Task(task.name) } } // copy ext dex String getDexFolder(basePath) { File baseFile = new File(basePath) //println "find cleasses.dex under:" + baseFile.absolutePath if(baseFile.exists()) { File[] files = baseFile.listFiles(); for(int i=0; i<files.size();i++) { def f = files[i] if (f.isFile() && f.name.equals("classes.dex")) { return f.parentFile.absolutePath; } else if(f.isDirectory()) { return getDexFolder(f.absolutePath) } } } return "" } void generateCreateDex2Task(String baseName) { println "generate classes2.dex for "+baseName println dxCmd String[] words = baseName.split("(?=\\p{Upper})") String last = words[words.length-1] last = last.toLowerCase() dexBasePath = dexBasePath + File.separator + last def destPath = getDexFolder(dexBasePath ) destPath = destPath + File.separator + "classes2.dex" println destPath ProcessBuilder b = null; if(dxCmd.endsWith(".bat")) { b = new ProcessBuilder(dxCmd, "--dex", "--output=\""+destPath+"\"", libs2Path); } else { b = new ProcessBuilder(dxCmd, "--dex", "--output="+destPath, libs2Path); } try { b.redirectErrorStream(true) Process p = b.start(); if(p.getOutputStream() != null ) { BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())) while(true) { String line = br.readLine() if(line == null) { break; } println line } p.waitFor() println "[dx] return: "+p.exitValue() } else { println "[dx] no output!" } } catch (Exception e) { println "exec dx error: "+e.class.toString() + e.getMessage() } } } String getDxCmd() { def sdkDir = android.getSdkDirectory().getAbsolutePath() def dxCmd = 'dx.bat' boolean found = false; for(File btools: file(sdkDir + File.separator + "build-tools").listFiles()) { if( btools.isDirectory()) { for(File file :btools.listFiles()) { if (file.isFile() && file.name.startsWith("dx")) { dxCmd = file.absolutePath found = true break } } if (found){ //break } } } return dxCmd } if (!overseaAutoMultidex || !chinaAutoMultidex) { def dxCmd = getDxCmd() String dexBasePath = file("build/intermediates/transforms/dex/").absolutePath if(!overseaAutoMultidex) { def libs2Path = file("libs2/oversea").absolutePath gradle.addListener new ClassesDexListener(dxCmd, dexBasePath+File.separator+"oversea", libs2Path) } if (!chinaAutoMultidex) { def libs2Path = file("libs2/china").absolutePath gradle.addListener new ClassesDexListener(dxCmd, dexBasePath+File.separator+"china", libs2Path) } }
把上面代码块添加到 build.gradle ,替换前一篇文章里的copy task 和 generate dex task 就可以了。