diff --git a/.gitignore b/.gitignore index 20c8c87..fa5f81f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /DexKnifePlugin/.idea/qaplug_profiles.xml /DexKnifePlugin/.idea/runConfigurations.xml /DexKnifePlugin/.idea/vcs.xml +.idea/ diff --git a/DexKnifePlugin/app/build.gradle b/DexKnifePlugin/app/build.gradle index 6a761f9..aa8d0ba 100644 --- a/DexKnifePlugin/app/build.gradle +++ b/DexKnifePlugin/app/build.gradle @@ -2,18 +2,28 @@ apply plugin: 'com.android.application' apply plugin: 'dexknifePlus' android { - compileSdkVersion Integer.parseInt(project.COMPILE_SDK_VER) - buildToolsVersion project.BUILD_TOOLS_VER + compileSdkVersion 23 + buildToolsVersion '23.0.3' defaultConfig { - minSdkVersion Integer.parseInt(project.MIN_SDK_VER) - targetSdkVersion Integer.parseInt(project.TARGET_SDK_VERSION) + minSdkVersion 14 + targetSdkVersion 23 versionCode 1 versionName "1.0" multiDexEnabled true } + //签名 + signingConfigs { + key { + storeFile file('mutidex.jks') + storePassword '123456' + keyAlias 'king' + keyPassword '123456' + } + } + productFlavors{ dev{ @@ -26,12 +36,13 @@ android { buildTypes { release { - minifyEnabled false + minifyEnabled true + signingConfig signingConfigs.key proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { -// minifyEnabled true + minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -48,7 +59,7 @@ dexKnife{ //1.如果没有可选参数,将根据enabled决定是否分包。 //2.如果有可选参数,需满足必选参数和可选参数的条件才允许分包 productFlavor 'mock' - buildType 'debug' + //buildType 'debug' /* *eg:当前productFlavors = dev,buildType = debug, diff --git a/DexKnifePlugin/app/dexknife.txt b/DexKnifePlugin/app/dexknife.txt index a377126..c6e76f4 100644 --- a/DexKnifePlugin/app/dexknife.txt +++ b/DexKnifePlugin/app/dexknife.txt @@ -1,10 +1,10 @@ #为注释符 -#-----------主Dex中必要依赖的脚本配置----------- -#默认保留四大组件中其他三大组件(并计算其依赖树),Activity组件选择性保留(使用-just activity 选项),若为空不保留任何Activity +#-----------主Dex中必要依赖的脚本配置-----------(支持依赖检测) +#默认保留四大组件中Service,ContentProvider,BroadcastReceiver三大组件,Activity组件选择性保留,若为空不保留任何Activity -just activity com.ceabie.demo.MainActivity -#-----------附加类----------- +#-----------附加类-----------(不支持依赖检测) # 如果你想要某个包路径在maindex中,则使用 -keep 选项,即使他已经在分包的路径中.若为空,不保留任意类 #-keep com.ceabie.demo.** @@ -24,7 +24,7 @@ # 表明限制的方法数小于main dex的必要方法数,调大到合适数值即可 -dex-param --set-max-idx-number=4000 -# 不进行dex分包, 直到 dex 的id数量超过 65536.(设置自动执行分包策略) +# 注释掉将执行自定义dex分包,否则执行默认分包. #-auto-maindex # 显示miandex的日志. diff --git a/DexKnifePlugin/app/mutidex.jks b/DexKnifePlugin/app/mutidex.jks new file mode 100644 index 0000000..ed54544 Binary files /dev/null and b/DexKnifePlugin/app/mutidex.jks differ diff --git a/DexKnifePlugin/app/src/main/res/values/strings.xml b/DexKnifePlugin/app/src/main/res/values/strings.xml index 8d40eb8..0a5576f 100644 --- a/DexKnifePlugin/app/src/main/res/values/strings.xml +++ b/DexKnifePlugin/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - demo + MultiDexPluginApp Hello world! Settings diff --git a/DexKnifePlugin/build.gradle b/DexKnifePlugin/build.gradle index d88678c..0e9a366 100644 --- a/DexKnifePlugin/build.gradle +++ b/DexKnifePlugin/build.gradle @@ -1,12 +1,13 @@ buildscript { repositories { + //maven { url uri('./repo') } jcenter() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0' - classpath 'com.library.tangxiaolv:dexknife-plus:1.0.1' + classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.library.tangxiaolv:dexknife-plus:1.0.3' } } diff --git a/DexKnifePlugin/dexknife-plus/build.gradle b/DexKnifePlugin/dexknife-plus/build.gradle index 3aafa1b..02e3c83 100644 --- a/DexKnifePlugin/dexknife-plus/build.gradle +++ b/DexKnifePlugin/dexknife-plus/build.gradle @@ -25,6 +25,7 @@ configurations { dependencies { compile gradleApi() compile localGroovy() + compile 'com.ceabie.dextools:gradle-dexknife-plugin:1.6.2' provided 'com.android.tools.build:builder:2.1.3' provided 'com.android.tools.build:gradle-core:2.1.3' } diff --git a/DexKnifePlugin/dexknife-plus/gradle.properties b/DexKnifePlugin/dexknife-plus/gradle.properties index 184c457..2f24490 100644 --- a/DexKnifePlugin/dexknife-plus/gradle.properties +++ b/DexKnifePlugin/dexknife-plus/gradle.properties @@ -1,6 +1,6 @@ #Gradle:groupId:libraryName:publicVersion LOCAL_VERSION=0.0.5 -PUBLIC_VERSION=1.0.1 +PUBLIC_VERSION=1.0.4 GROUP_ID=com.library.tangxiaolv #Modelһ LIBRARY_NAME=dexknife-plus diff --git a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/ClassFileTreeElement.java b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/ClassFileTreeElement.java deleted file mode 100644 index f08b325..0000000 --- a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/ClassFileTreeElement.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2016 ceabie (https://github.com/ceabie/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ceabie.dexknife; - -import org.gradle.api.file.FileTreeElement; -import org.gradle.api.file.RelativePath; -import org.gradle.internal.nativeintegration.filesystem.FileSystem; - -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Mock the file tree element of Filter. - * - * @author ceabie - */ -public class ClassFileTreeElement implements FileTreeElement { - private RelativePath mRelativePath; - private File mFile; - - public void setClassPath(String name) { - mFile = new File(name); - mRelativePath = RelativePath.parse(!isDirectory(), name); - } - - @Override - public File getFile() { - return mFile; - } - - @Override - public boolean isDirectory() { - return false; - } - - @Override - public long getLastModified() { - return 0; - } - - @Override - public long getSize() { - return 0; - } - - @Override - public InputStream open() { -// try { -// return mZipFile.getInputStream(mZipEntry); -// } catch (IOException e) { -// e.printStackTrace(); -// } - - return null; - } - - @Override - public void copyTo(OutputStream outputStream) { - } - - @Override - public boolean copyTo(File file) { - return true; - } - - @Override - public String getName() { - return mFile.getName(); - } - - @Override - public String getPath() { - return mFile.getPath(); - } - - @Override - public RelativePath getRelativePath() { - return mRelativePath; - } - - @Override - public int getMode() { - return isDirectory() - ? FileSystem.DEFAULT_DIR_MODE - : FileSystem.DEFAULT_FILE_MODE; - } -} diff --git a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifeConfig.java b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifeConfig.java deleted file mode 100644 index 8fc9018..0000000 --- a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifeConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 ceabie (https://github.com/ceabie/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ceabie.dexknife; - -import org.gradle.api.tasks.util.PatternSet; - -import java.util.Set; - -/** - * The type Dex knife config. - * - * @author ceabie - */ -public class DexKnifeConfig { - PatternSet patternSet; - PatternSet suggestPatternSet; - boolean useSuggest = true; - boolean filterSuggest = false; - boolean logMainList = false; - boolean logFilterSuggest = false; - Set additionalParameters; -} diff --git a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexSplitTools.java b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexSplitTools.java deleted file mode 100644 index a545af3..0000000 --- a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexSplitTools.java +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright (C) 2016 ceabie (https://github.com/ceabie/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.ceabie.dexknife; - -import org.gradle.api.Project; -import org.gradle.api.file.FileTreeElement; -import org.gradle.api.specs.Spec; -import org.gradle.api.specs.Specs; -import org.gradle.api.tasks.util.PatternSet; - -import java.io.*; -import java.util.*; -import java.util.regex.Pattern; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * the base of spilt tools. - * - * @author ceabie - */ -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"; - - private static final String MAINDEXLIST_TXT = "maindexlist.txt"; - private static final String MAPPING_FLAG = " -> "; - private static final int MAPPING_FLAG_LEN = MAPPING_FLAG.length(); - private static final String CLASS_SUFFIX = ".class"; - - private static long StartTime = 0; - - protected static void startDexKnife() { - System.out.println("DexKnife Processing ..."); - StartTime = System.currentTimeMillis(); - } - - protected static void endDexKnife() { - String time; - long internal = System.currentTimeMillis() - StartTime; - if (internal > 1000) { - float i = internal / 1000; - if (i >= 60) { - i = i / 60; - int min = (int) i; - time = min + " min " + (i - min) + " s"; - } else { - time = i + "s"; - } - } else { - time = internal + "ms"; - } - - System.out.println("DexKnife Finished: " + time); - } - - public static boolean processMainDexList(Project project, boolean minifyEnabled, - File mappingFile, - File jarMergingOutputFile, File andMainDexList, - DexKnifeConfig dexKnifeConfig) throws Exception { - - 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); - } - - /** - * get the config of dex knife - * - * @param project project - * @return DexKnifeConfig - * @throws Exception exception - */ - 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 addParams = new HashSet<>(); - - Set splitToSecond = new HashSet<>(); - Set keepMain = new HashSet<>(); - Set splitSuggest = new HashSet<>(); - Set 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)) { - minimalMainDex = false; - } else if (matchCommand(cmd, DEX_KNIFE_CFG_DEX_PARAM)) { - 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)) { - String sPattern = line.substring(DEX_KNIFE_CFG_SPLIT.length()).trim(); - addClassFilePath(sPattern, splitToSecond); - - } else if (matchCommand(cmd, DEX_KNIFE_CFG_KEEP)) { - String sPattern = line.substring(DEX_KNIFE_CFG_KEEP.length()).trim(); - addClassFilePath(sPattern, keepMain); - - } else if (DEX_KNIFE_CFG_DONOT_USE_SUGGEST.equals(cmd)) { - dexKnifeConfig.useSuggest = false; - - } else if (DEX_KNIFE_CFG_FILTER_SUGGEST.equals(cmd)) { - dexKnifeConfig.filterSuggest = true; - - } else if (DEX_KNIFE_CFG_LOG_MAIN_DEX.equals(cmd)) { - dexKnifeConfig.logMainList = true; - - } else if (DEX_KNIFE_CFG_LOG_FILTER_SUGGEST.equals(cmd)) { - dexKnifeConfig.logFilterSuggest = true; - - } else if (matchCommand(cmd, DEX_KNIFE_CFG_SUGGEST_SPLIT)) { - String sPattern = line.substring(DEX_KNIFE_CFG_SUGGEST_SPLIT.length()).trim(); - addClassFilePath(sPattern, splitSuggest); - - } else if (matchCommand(cmd, DEX_KNIFE_CFG_SUGGEST_KEEP)) { - 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) { - 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 patternList) { - if (classPath != null && classPath.length() > 0) { - if (classPath.endsWith(CLASS_SUFFIX)) { - classPath = classPath.substring(0, classPath.length() - CLASS_SUFFIX.length()) - .replace('.', '/') + CLASS_SUFFIX; - } else { - classPath = classPath.replace('.', '/'); - } - - patternList.add(classPath); - } - } - - private static Spec getMaindexSpec(PatternSet patternSet) { - Spec maindexSpec = null; - - if (patternSet != null) { - Spec includeSpec = null; - Spec 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 { - if (excludeSpec == null) { - maindexSpec = Specs.satisfyAll(); - } else { - maindexSpec = Specs.not(excludeSpec); - } - } - } - - if (maindexSpec == null) { - maindexSpec = Specs.satisfyAll(); - } - - return maindexSpec; - } - - /** - * generate the main dex list - */ - 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 mainCls = null; - if (dexKnifeConfig.useSuggest) { - - PatternSet patternSet = dexKnifeConfig.suggestPatternSet; - if (dexKnifeConfig.filterSuggest && patternSet == null) { - patternSet = dexKnifeConfig.patternSet; - } - - mainCls = getAdtMainDexClasses(andMainDexList, patternSet, - dexKnifeConfig.logFilterSuggest); - System.out.println("DexKnife: use suggest"); - } - - File keepFile = project.file(MAINDEXLIST_TXT); - keepFile.delete(); - - ArrayList 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)); - - 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 ArrayList getMainClassesFromJar( - File jarMergingOutputFile, PatternSet mainDexPattern, HashSet mainCls) - throws Exception { - ZipFile clsFile = new ZipFile(jarMergingOutputFile); - Spec asSpec = getMaindexSpec(mainDexPattern); - ClassFileTreeElement treeElement = new ClassFileTreeElement(); - - ArrayList mainDexList = new ArrayList<>(); - Enumeration 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); - } - } - } - - clsFile.close(); - - return mainDexList; - } - - /** - * 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 getMainClassesFromMapping( - File mapping, - PatternSet mainDexPattern, - HashSet mainCls) throws Exception { - - String line; - ArrayList mainDexList = new ArrayList<>(); - BufferedReader reader = new BufferedReader(new FileReader(mapping)); - - ClassFileTreeElement treeElement = new ClassFileTreeElement(); - Spec 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); - } - } - } - } - - reader.close(); - - return mainDexList; - } - - /** - * get the maindexlist of android gradle plugin - */ - private static HashSet 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 mainCls = new HashSet<>(); - BufferedReader reader = new BufferedReader(new FileReader(outputDir)); - - ClassFileTreeElement treeElement = new ClassFileTreeElement(); - Spec 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.add(line); - } - } - - reader.close(); - - if (mainCls.size() == 0) { - mainCls = null; - } - - return mainCls; - } - - static int getAndroidPluginVersion(String version) { - int size = version.length(); - int ver = 0; - for (int i = 0; i < size; i++) { - char c = version.charAt(i); - if (Character.isDigit(c) || c == '.') { - if (c != '.') { - ver = ver * 10 + c - '0'; - } - } else { - break; - } - } - - return ver; - } -} diff --git a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/InjectAndroidBuilder.groovy b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/InjectAndroidBuilder.groovy deleted file mode 100644 index 5651173..0000000 --- a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/InjectAndroidBuilder.groovy +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2016 ceabie (https://github.com/ceabie/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ceabie.dexknife - -import com.android.build.gradle.internal.transforms.DexTransform -import com.android.builder.core.AndroidBuilder -import com.android.builder.core.DexOptions -import com.android.builder.core.ErrorReporter -import com.android.builder.sdk.TargetInfo -import com.android.ide.common.process.JavaProcessExecutor -import com.android.ide.common.process.ProcessException -import com.android.ide.common.process.ProcessExecutor -import com.android.ide.common.process.ProcessOutputHandler -import com.android.utils.ILogger -import groovy.transform.CompileStatic - -import java.lang.reflect.Field - -/** - * proxy the androidBuilder that plugin 1.5.0 to add '--minimal-main-dex' options. - * - * @author ceabie - */ -public class InjectAndroidBuilder extends AndroidBuilder { - - Collection 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 inputs, - File outDexFolder, - boolean multidex, - File mainDexList, - DexOptions dexOptions, - List 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<>() - } - - 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 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 additionalParameters = dexOptions.getAdditionalParameters() - if (additionalParameters == null) { - additionalParameters = new ArrayList<>() - } - - mergeParams(additionalParameters, mAddParams) - } - - mAndroidBuilder.convertByteCode(inputs, outDexFolder, multidex, mainDexList, dexOptionsProxy, - optimize, processOutputHandler); - } - - @CompileStatic - @Override - List getBootClasspath(boolean includeOptionalLibraries) { - return mAndroidBuilder.getBootClasspath(includeOptionalLibraries) - } - - @CompileStatic - @Override - List getBootClasspathAsStrings(boolean includeOptionalLibraries) { - return mAndroidBuilder.getBootClasspathAsStrings(includeOptionalLibraries) - } - - - @CompileStatic - static void mergeParams(List additionalParameters, Collection addParams) { - List mergeParam = new ArrayList<>() - for (String param : addParams) { - if (!additionalParameters.contains(param)) { - mergeParam.add(param) - } - } - - if (mergeParam.size() > 0) { - additionalParameters.addAll(mergeParam) - } - } - - - public static void proxyAndroidBuilder(DexTransform transform, Collection addParams) { - if (addParams != null && addParams.size() > 0) { - accessibleField(DexTransform.class, "androidBuilder") - .set(transform, getProxyAndroidBuilder(transform.androidBuilder, addParams)) - } - } - - private static AndroidBuilder getProxyAndroidBuilder(AndroidBuilder orgAndroidBuilder, - Collection 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 - } -} diff --git a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/SplitToolsFor130.groovy b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/SplitToolsFor130.groovy deleted file mode 100644 index 58fdba5..0000000 --- a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/SplitToolsFor130.groovy +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2016 ceabie (https://github.com/ceabie/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ceabie.dexknife - -import org.gradle.api.Project - -/** - * the spilt tools for plugin 1.3.0. - * - * @author ceabie - */ -public class SplitToolsFor130 extends DexSplitTools { - - public static boolean isCompat(Object variant) { - try { - if (variant != null) { - 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 { - startDexKnife() - - DexKnifeConfig dexKnifeConfig = getDexKnifeConfig(project) - - def scope = variant.getVariantData().getScope() - File mergedJar = scope.jarMergingOutputFile - File mappingFile = variant.mappingFile - File andMainDexList = scope.mainDexListFile - boolean minifyEnabled = variant.buildType.minifyEnabled - - 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 - } - - endDexKnife() - } - } - } -} \ No newline at end of file diff --git a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/SplitToolsFor150.groovy b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/SplitToolsFor150.groovy deleted file mode 100644 index e555460..0000000 --- a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/SplitToolsFor150.groovy +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2016 ceabie (https://github.com/ceabie/) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.ceabie.dexknife - -import com.android.build.api.transform.Format -import com.android.build.api.transform.Transform -import com.android.build.gradle.api.ApplicationVariant -import com.android.build.gradle.internal.incremental.InstantRunBuildContext -import com.android.build.gradle.internal.pipeline.TransformTask -import com.android.build.gradle.internal.transforms.DexTransform -import org.gradle.api.Project - -/** - * the spilt tools for plugin 1.5.0. - * - * @author ceabie - */ -public class SplitToolsFor150 extends DexSplitTools { - - public static boolean isCompat() { -// if (getAndroidPluginVersion() < 200) { -// return true; -// } - - return true; - } - - public static void processSplitDex(Project project, ApplicationVariant variant) { - 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() - 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)) { - jarMergingTask = theTask - } else if ("dex".equalsIgnoreCase(transformName)) { // DexTransform - dexTask = theTask - } - } - - if (dexTask != null && ((DexTransform) dexTask.transform).multiDex) { - dexTask.inputs.file DEX_KNIFE_CFG_TXT - - dexTask.doFirst { - startDexKnife() - - File mergedJar = null - File mappingFile = variant.mappingFile - DexTransform dexTransform = it.transform - File fileAdtMainList = dexTransform.mainDexListFile - - println("DexKnife Adt Main: " + fileAdtMainList) - - DexKnifeConfig dexKnifeConfig = getDexKnifeConfig(project) - - // 非混淆的,从合并后的jar文件中提起mainlist; - // 混淆的,直接从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) - } - - println("DexKnife-From MergedJar: " + mergedJar) - } - - if (processMainDexList(project, minifyEnabled, mappingFile, mergedJar, - fileAdtMainList, dexKnifeConfig)) { - - int version = getAndroidPluginVersion(getAndroidGradlePluginVersion()) - println("DexKnife: AndroidPluginVersion: " + version) - - // after 2.2.0, it can additionalParameters, but it is a copy in task - - // 替换 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 - } -} \ No newline at end of file diff --git a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifeExtension.groovy b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/tangxiaolv/dexknife/DexKnifeExtension.groovy similarity index 77% rename from DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifeExtension.groovy rename to DexKnifePlugin/dexknife-plus/src/main/groovy/com/tangxiaolv/dexknife/DexKnifeExtension.groovy index dffcbf0..31795a9 100644 --- a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifeExtension.groovy +++ b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/tangxiaolv/dexknife/DexKnifeExtension.groovy @@ -1,4 +1,4 @@ -package com.ceabie.dexknife +package com.tangxiaolv.dexknife class DexKnifeExtension { boolean enabled = false diff --git a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifePlugin.groovy b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/tangxiaolv/dexknife/DexKnifePlusPlugin.groovy similarity index 75% rename from DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifePlugin.groovy rename to DexKnifePlugin/dexknife-plus/src/main/groovy/com/tangxiaolv/dexknife/DexKnifePlusPlugin.groovy index c7086b6..c19971b 100644 --- a/DexKnifePlugin/dexknife-plus/src/main/groovy/com/ceabie/dexknife/DexKnifePlugin.groovy +++ b/DexKnifePlugin/dexknife-plus/src/main/groovy/com/tangxiaolv/dexknife/DexKnifePlusPlugin.groovy @@ -13,15 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.ceabie.dexknife +package com.tangxiaolv.dexknife +import com.ceabie.dexknife.DexKnifePlugin +import com.ceabie.dexknife.DexSplitTools import org.gradle.api.Plugin import org.gradle.api.Project /** * the spilt tools plugin. */ -public class DexKnifePlugin implements Plugin { +public class DexKnifePlusPlugin implements Plugin { @Override void apply(Project project) { @@ -48,21 +50,11 @@ public class DexKnifePlugin implements Plugin { filterActivity(project); - if (isMultiDexEnabled(variant)) { - if (SplitToolsFor130.isCompat(variant)) { - System.err.println("DexKnife: Compat 1.3.0."); - SplitToolsFor130.processSplitDex(project, variant) - } else if (SplitToolsFor150.isCompat()) { - 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."); - } + DexKnifePlugin.dexKnifeProcessVariant(project, variant) } + } else { + printf "-DexKnifePlugin Enable = false\n"; } - printf "-DexKnifePlugin Enable = false\n"; } } } @@ -103,19 +95,4 @@ public class DexKnifePlugin implements Plugin { } } } - - 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 - } - } \ No newline at end of file diff --git a/DexKnifePlugin/dexknife-plus/src/main/resources/META-INF/gradle-plugins/dexknifePlus.properties b/DexKnifePlugin/dexknife-plus/src/main/resources/META-INF/gradle-plugins/dexknifePlus.properties index 24abf7f..c2ec48d 100644 --- a/DexKnifePlugin/dexknife-plus/src/main/resources/META-INF/gradle-plugins/dexknifePlus.properties +++ b/DexKnifePlugin/dexknife-plus/src/main/resources/META-INF/gradle-plugins/dexknifePlus.properties @@ -1 +1 @@ -implementation-class=com.ceabie.dexknife.DexKnifePlugin +implementation-class=com.tangxiaolv.dexknife.DexKnifePlusPlugin diff --git a/DexKnifePlugin/gradle/wrapper/gradle-wrapper.properties b/DexKnifePlugin/gradle/wrapper/gradle-wrapper.properties index e5ad1b4..e17444a 100644 --- a/DexKnifePlugin/gradle/wrapper/gradle-wrapper.properties +++ b/DexKnifePlugin/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Tue Sep 20 15:06:11 CST 2016 +#Mon Dec 26 17:59:14 CST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/README.md b/README.md index da9dcf6..28cdf90 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,25 @@ -#Android傻瓜式分包插件 +# Deprecated +Please use [multiDexKeepProguard](https://developer.android.com/studio/build/multidex?hl=zh-cn) to splite main dex classes. + +# Android傻瓜式分包插件 +[ ![Download](https://api.bintray.com/packages/tangxiaolv/maven/dexknife-plus/images/download.svg?version=1.0.4) ](https://bintray.com/tangxiaolv/maven/dexknife-plus/1.0.4/link) + 注1:不想看前半部分的话可以直接跳过到最下面配置部分。 -注2:本插件是基于[DexKnifePlugin 1.5.6](https://github.com/ceabie/DexKnifePlugin)优化改造而来,感谢ceabie的无私奉献。 +注2:本插件是基于[DexKnifePlugin 1.6.2](https://github.com/ceabie/DexKnifePlugin)优化改造而来,感谢ceabie的无私奉献。 -##填坑之路 -###坑1:65536 ,So easy! +## 填坑之路 +### 坑1:65536 ,So easy! **原因:**Dalvik 的 invoke-kind 指令集中,method reference index 只留了 16 bits,最多能引用 65535 个方法。 参考=>[由Android 65K方法数限制引发的思考](http://jayfeng.com/2016/03/10/%E7%94%B1Android-65K%E6%96%B9%E6%B3%95%E6%95%B0%E9%99%90%E5%88%B6%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83/). **解决:** ``` +android{ + defaultConfig { + multiDexEnabled true + } +} + dependencies { compile 'com.android.support:MultiDex:1.0.1' } @@ -22,7 +33,7 @@ protected void attachBaseContext(Context base) { } ``` -###坑2:Too many classes in –main-dex-list ,what? +### 坑2:Too many classes in –main-dex-list ,what? **原因:**通过上面的官方分包,已经把原Dex分为1主Dex加多从Dex。主Dex包含所有4大组件,Application,Annotation,multidex等及其必要的直接依赖。由于我们方法数已达到16W之巨,上百个Activity全部塞进主Dex,又成功的把主Dex撑爆了。 **解决:** @@ -41,17 +52,17 @@ afterEvaluate { ``` 参考=>[Android Dex分包之旅](http://yydcdut.com/2016/03/20/split-dex/index.html) -###坑3:gradle 1.5.0之后不支持这种写法 ,what the fuck? +### 坑3:gradle 1.5.0之后不支持这种写法 ,what the fuck? **原因:**官方解释Gralde`1.5.0`以上已经将(jacoco, progard, multi-dex)统一移到[Transform API](http://tools.android.com/tech-docs/new-build-system/transform-api)里,然而Transform API并没有想象的那么简单好用,翻遍Google终于找到一个兼容Gradle `1.5.0`以上的分包插件[DexKnifePlugin](https://github.com/ceabie/DexKnifePlugin)。 扩展=>这篇[Android 热修复使用Gradle Plugin1.5改造Nuwa插件](http://blog.csdn.net/sbsujjbcy/article/details/50839263)比较好的介绍了Transform API的使用。 -###坑4:NoClassDefFoundError ,are you kiding me? +### 坑4:NoClassDefFoundError ,are you kiding me? **原因:**通过插件手动指定main dex中要保留的类,虽然分包成功,但是main dex中的类及其直接引用类很难通过手动的方式指定。 **解决方式:** [美团Android DEX自动拆包及动态加载简介](http://tech.meituan.com/mt-android-auto-split-dex.html),他们是通过编写了一个能够自动分析Class依赖的脚本去算出主Dex需要包含的所有必要依赖。看来写脚本是跑不掉了。 -###坑5:自定义脚本 ,read the fuck source! +### 坑5:自定义脚本 ,read the fuck source! **问题一:**哪些类是需要放入主Dex中? 查看sdk\build-tools\platform-version\mainDexClasses.rules发现放入主Dex相关类有Instrumentation,Application,Activity,Service,ContentProvider,BroadcastReceiver,BackupAgent的所有子类。 @@ -95,31 +106,50 @@ afterEvaluate { } ``` -###坑6:主dex依然爆表,shit again! +### 坑6:主dex依然爆表,shit again! 其实上面那段脚本已经成功筛选出我们想要放入主Dex的`manifest_keep列表`和`maindexlist列表`,但是在打包的时候还是把所有类打进主Dex(已无语)。这个时候就需要跟[DexKnifePlugin](https://github.com/ceabie/DexKnifePlugin)插件配合使用,首先在gradle中加上上述脚本,然后使用插件时在配置文件中加上 `-split **.**`和`#-donot-use-suggest`。DexKnifePlugin插件运行原理很简单,在生成Dex任务之前首先读取自己的配置文件(包含前面我们通过Gradle脚本生成的`maindexlist`列表),然后扫描combined.jar(包含工程中所有.class文件)匹配出我们自定义的maindexlist.txt,再替换掉build/multi-dex/maindexlist.txt,和build实例。这样分包的时候就会基于我们的规则生成主Dex。 -###坑7:ANR,HAHAHA! +### 坑7:ANR,HAHAHA! 我们最低API=16,测试并未发现ANR问题,所以暂时没考虑景上添花,这个问题比较好解决。 参考=>[Android Dex分包之旅](http://yydcdut.com/2016/03/20/split-dex/index.html) -###Congratulation +### Congratulation 恭喜,填坑终于结束,不过还有点不爽的是需要同时维护Gradle脚本和插件的配置。 于是乎就将Gradle脚本整合进了插件,这样只需维护一个配置文件就行了。读者可以根据自己需求自行选择分开配置还是整合配置。通过这种方式我们把主Dex的方法数维持在15000左右,从此再也不用担心方法数问题了!!! -##配置部分 -**第一步:添加根目录Gradle** +## 配置部分 +**第一步:添加分包支持** +``` +android{ + defaultConfig { + multiDexEnabled true + } +} + +dependencies { + compile 'com.android.support:MultiDex:1.0.1' +} + +在继承的 Application中重写 attachBaseContext(Context) +@Override +protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + MultiDex.install(this); +} +``` +**第二步:添加根目录Gradle** ``` buildscript { dependencies { - classpath 'com.library.tangxiaolv:dexknife-plus:1.0.1' + classpath 'com.library.tangxiaolv:dexknife-plus:1.0.4' } } ``` -**第二步:在你的App模块的build.gradle添加插件** +**第三步:在你的App模块的build.gradle添加插件** ``` apply plugin: 'dexknifePlus' ``` -**第三步:配置参数** +**第四步:配置参数**   ``` dexKnife{ //必选参数 @@ -139,15 +169,15 @@ dexKnife{ * */ } ``` -**第四步:在你的App模块目录下新建dexknife.txt,并自定义配置** +**第五步:在你的App模块目录下新建dexknife.txt,并自定义配置** ``` #为注释符 -#-----------主Dex中必要依赖的脚本配置----------- -#默认保留四大组件中其他三大组件(并计算其依赖树),Activity组件选择性保留(使用-just activity 选项),若为空不保留任何Activity +#-----------主Dex中必要依赖的脚本配置-----------(支持依赖检测) +#默认保留四大组件中Service,ContentProvider,BroadcastReceiver三大组件,Activity组件选择性保留,若为空不保留任何Activity -just activity com.ceabie.demo.MainActivity -#-----------附加类----------- +#-----------附加类-----------(不支持依赖检测) # 如果你想要某个包路径在maindex中,则使用 -keep 选项,即使他已经在分包的路径中.若为空,不保留任意类 #-keep com.ceabie.demo.** @@ -167,16 +197,18 @@ dexKnife{ # 表明限制的方法数小于main dex的必要方法数,调大到合适数值即可 -dex-param --set-max-idx-number=4000 -# 不进行dex分包, 直到 dex 的id数量超过 65536.(设置自动执行分包策略) +# 注释掉将执行自定义dex分包,否则执行默认分包. #-auto-maindex # 显示miandex的日志. #-log-mainlist ``` -**第五步:在 defaultConfig 或者 buildTypes中打开 multiDexEnabled true,否则不起作用** +**第六步:在 defaultConfig 或者 buildTypes中打开 multiDexEnabled true,否则不起作用** + +**使用前先参阅 DexKnife 的[特性部分](https://github.com/ceabie/DexKnifePlugin#特性重要)** -##已知错误 +## 已知错误 注:分包的时候如果发现一些莫名的错误,可以关掉instant run,一般都能解决