DexKnifePlugin

Introduction: A simple android gradle plugin to smart split the specified classes to second dex.
More: Author   ReportBugs   
Tags:
MultiDex-MultiDex 指定类-

License Download

A simple android gradle plugin to use the patterns of package to smart split the specified classes to multi dex. Also supports android gradle plugin 2.2.0 multidex. Solve android studio enable the native multidex feature, but there will be too many classes in main dex. (See Features 7)

  • Notes: Because instant-run of 2.0.0 above is incompatible with multidex, DexKnife is auto disabled when instant-run mode. It will auto enable when disabled instant-run or in packaging release.(minsdk < 21)

中文

Update Log

1.6.1: Compatible with android gradle plugin 2.3.0, auto disable when build with ART-Runtime (See Features 8).
1.6.0: Modify: When only -keep is configured, only keep the specified classes.
1.5.9: Compatible with some ancient version of gradle and android gradle plugin.
1.5.8: Compatible with gradle 3.2, fixed use of only support-split and support-keep resulting in an extra large number of classes.
1.5.7: fixed support-split and support-keep are not work. (修复 support-split/support-keep 无效的 bug)
1.5.6: Experimentally compatible with java 1.7, fix nothing is selected when only -keep. (实验性的支持 java 1.7,修复但只有 keep 选项时没有类被选中)
1.5.5: support individual filter for suggest maindexlist. (单独的 maindexlist 过滤设置)
1.5.5.alpha: Experimentally compatible with android gradle plugin on 2.2.0. (实验性的支持 2.2.0 plugin)
1.5.4: auto disabled when instant run mode.(instant run 模式时自动禁用 DexKnife)
1.5.3: add some track logs and skip DexKnife when jarMerging is null.(增加跟踪日志,并在 jarMerging 为 null 跳过处理)
1.5.2: fixed the include and exclude path, and supports filtering single class.(修复 include 和 exclude, 并支持过滤单个类)
1.5.1.exp: Experimentally compatible with android gradle plugin on 2.1.0 (实验性的支持 2.1.0 plugin)
1.5.1: fixed the proguard mode

Features

  1. DexKnife just converts the wildcards of class path to maindexlist.txt, does not participate in other compilation process. It is not automatic tools, you need to have a understanding of the maindexlist features.
  2. If the class can not be found (ie class no def / found) at runtime, enable DexKnife's log function, debug the config of dexKnife and check the config ProGuard. Verify the generated maindexlist.txt match your config. Do not split the classes in the Application class into second dex. (Even if you manually configure the maindexlist will be such a problem.)
  3. DexKnife can only explicitly specify the classes of main dex, can not specify the classes of after the second dex (limitation of dex's param maindexlist). If you need to completely configure the main dex manually, use:

     -donot-use-suggest<br />
     -split ** <br />
     -keep android.support.multidex.** # keep multidex lib <br />
     -keep # wildcards of other keeping classes, and the count of idx don't overflow 65535 <br />
    
  4. DexKnife does not have dependency detection and requires you to configure it manually because DexKnife does not know your project requirements.

  5. DexKnife uses the original classpath as the configuration, not the obfuscated classpath.
  6. the count of ID that generated by -keep can not overflow 65535, otherwise there will be error of too many class.
  7. If you use the android gradle plugin's native multidex, but the declaration in the manifest is too much, resulting in the number of methods and variables are still overflow, or can not be packaged. You can simply use -suggest-split to move some of the classes in the suggest list out of the main dex.
  8. minsdk < 21. If minsdk >= 21, the android gradle plugin will build with ART-Runtime, MainDexList isn't necessary, DexKnife is auto disable. In debug mode with Android Gradle plugin >= 2.3.0, minsdk is associated with min(Target running device, TargetSdk). Make sure your MinSdkVersion < 21, DexKnife will auto enable in release mode if conditions are compatible.

Usage

1.In your project's build.gradle, buildscript.

    buildscript {
            ....
        dependencies {
            ....
            classpath 'com.android.tools.build:gradle:2.3.0'  // or other
            classpath 'com.ceabie.dextools:gradle-dexknife-plugin:1.6.1'
        }
    }

please make sure gradle version is compatible with the android gradle plugin, otherwise it can causes some sync error, such as:
Gradle sync failed: Unable to load class 'com.android.builder.core.EvaluationErrorReporter'.

2.Create a 'dexknife.txt' in your App's module, and config the patterns of classes path that wants to put into sencond dex.
(The rest of any classes that is not marked split will be in miandexlist)

    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.

Also see: https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html

Other config key:

'#' is the comment, config is disabled when '#' adds on line start.

# Global filter, don't apply with suggest maindexlist if -filter-suggest is DISABLE.
# this path will to be split to second dex.
android.support.v?.**

# if you want to keep some classes in main dex, use '-keep'.
-keep android.support.v4.view.**

# you can keep single class in main dex, end with '.class', use '-keep'.
-keep android.support.v7.app.AppCompatDialogFragment.class

# do not use suggest of the maindexlist that android gradle plugin generate.
-donot-use-suggest

# the global filter apply with maindexlist, if -donot-use-suggest is DISABLE.
-filter-suggest

# Notes: Split dex until the dex's id > 65536. --minimal-main-dex is default.
-auto-maindex  # default is not used.

# dex additional parameters, such as --set-max-idx-number=50000
# if number is too small, it will cause DexException: Too many classes in --main-dex-list, main dex capacity exceeded
-dex-param --set-max-idx-number=50000

# log the main dex classes.
-log-mainlist

# log the filter classes of suggest maindexlist, if -filter-suggest is enabled..
-log-filter-suggest

#the filter log。Recommend;Global;true;false
-log-filter

# if you only filter the suggest maindexlist, use -suggest-split and -suggest-keep.
# Global filter will merge into them if -filter-suggest is ENABLE at same time.
-suggest-split **.MainActivity2.class
-suggest-keep android.support.multidex.**

Note: if you want to filter the inner classes, use $*, such as: SomeClass$*.class

3.add to your app's build.gradle, add this line:

apply plugin: 'com.ceabie.dexnkife'

and then, set your app

multiDexEnabled true
  • Notes: You want to set 'multiDexEnabled true' in 'defaultConfig' or 'buildTypes', otherwise ineffective.

4.run your app

DexKnife 中文

一个简单的将指定使用通配符包名分包到第二个 dex 中 gradle 插件。(使用疑问查看 特性) 同时支持 android gradle plugin 2.2.0 multidex. 可以解决 android studio 使用 multidex,但还会出现 dex 类太多的问题。(参见 特性 7)

  • 注意:由于高于 2.0.0 的 instant-run 和 ART-Runtime 特性与 multidex 不兼容,DexKnife 会暂时禁用。当 instant-run 被禁用或者 release 打包时会自动启用。(minsdk < 21)

更新日志

1.6.1: 兼容 Android gradle plugin 2.3.0,在 ART-Runtime 编译模式下自动禁用,增加相关提示。(参见 特性 8)
1.6.0: 修改:当只有 keep 时,只保留 keep 指定的类
1.5.9: 兼容一些古老的 gradle 和 android gradle plugin 版本
1.5.8: 兼容 gradle 3.2,修复当只使用 support-split/support-keep 时出现大量的额外类
1.5.7: 修复 support-split/support-keep 无效的 bug
1.5.6: 实验性的支持 java 1.7,修复但只有 keep 选项时没有类被选中
1.5.5: 增加单独的 maindexlist 过滤设置
1.5.5.alpha: 实验性的支持 2.2.0 plugin
1.5.4: instant run 模式时自动禁用 DexKnife
1.5.3: 增加跟踪日志,并在 jarMerging 为 null 时跳过处理
1.5.2: 修复 include 和 exclude, 并支持过滤单个类
1.5.1.exp: 实验性的支持 2.1.0 plugin
1.5.1: 修复 proguard mode

特性(重要)

  1. DexKnife 只负责由配置的 类路径通配符 -> maindexlist 文件的转换,生成 maindexlist.txt,不参与其他的处理和编译过程。非全自动工具,需要对 maindexlist 特性有较深的了解。
  2. 如果出现运行时类找不到(i.e. class no def/found),请打开 DexKnife 的 log 功能,比对调试下 DexKnife 或 ProGuard 配置,并检查生成的 maindexlist 是否匹配你的配置。不要将在 Application 中使用到的类,分到第二个 dex 中。(即使不使用 DexKnife,手动配置 maindexlist 也会出现这样的问题)
  3. DexKnife 只能明确指定第一个 dex 中的类,不能明确指定第二个 dex 以后的类(dex 的 maindexlist 限制)。如果需要完全手动配置第一个 dex,使用

     -donot-use-suggest<br />
     -split ** <br />
     -keep android.support.multidex.** # 保证 multidex 或者你自己开发的 multidex <br />
     -keep # 配置你的 keep 类的通配符,但数量不能超界 <br />
    
  4. DexKnife 不带有依赖检测,需要你手动配置,因为 DexKnife 并不知道你的项目需求。

  5. DexKnife 使用原始类路径作为配置,不要使用混淆后的类路径。
  6. -keep 的类所产生的 ID 数量不能超过 65535,否则会出现 Too many classes 的错误。
  7. 如果使用 android gradle plugin 原生的 multidex,但由于 manifest 中声明的类过多,导致所包含的方法和变量的数量还是超出上限,出现 dex 的类还是太多,还是无法打包。可以仅使用-suggest-split 将推荐列表中的一些类移出第一个 dex,剩下打包工具会自行分配。
  8. minsdk < 21。如果 minsdk >= 21,官方将换用 ART 编译方式,已经不需要 MainDexList,DexKnife 会自动禁用。Android Gradle plugin >= 2.3.0 时,在 debug 状态下,minsdk 会暂时设为 min(目标运行设备, TargetSdk),只要 minsdk < 21,Release 模式时会自动启用。

使用方法

1.在你的工程的 build.gradle 中 buildscript:

    buildscript {
            ....
        dependencies {
            ....
            classpath 'com.android.tools.build:gradle:2.3.0'  // or other
            classpath 'com.ceabie.dextools:gradle-dexknife-plugin:1.6.1'
        }
    }

注意,请确保使用的 gradle 版本和 android gradle plugin 兼容,否则会出现同步错误,例如:
Gradle sync failed: Unable to load class 'com.android.builder.core.EvaluationErrorReporter'.

2.在 App 模块下创建 dexknife.txt,并填写要放到第二个 dex 中的包名路径的通配符.(注意,其余任何未被注明 split 的类都会在 miandexlist)

    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.

更多参见: https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/util/PatternFilterable.html

其他配置:

使用 # 进行注释, 当行起始加上 #, 这行配置被禁用.

# 全局过滤, 如果没设置 -filter-suggest 并不会应用到 建议的 maindexlist.
# 如果你想要某个已被排除的包路径在 maindex 中,则使用 -keep 选项,即使他已经在分包的路径中.
# 注意,没有 split 只用 keep 时,miandexlist 将仅包含 keep 指定的类。
-keep android.support.v4.view.**

# 这条配置可以指定这个包下类在第二 dex 中.(注意,未指定的类会在被认为在 maindexlist 中)
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

#过滤日志。Recommend:在 maindexlist 中(由推荐列表确定);Global:在 maindexlist 中,由全局过滤确定;true,前两者都成立的;false,不在 maindexlist 中
-log-filter

# 如果你只想过滤 建议的 maindexlist, 使用 -suggest-split 和 -suggest-keep.
# 如果同时启用 -filter-suggest, 全局过滤会合并到它们中.
-suggest-split **.MainActivity2.class
-suggest-keep android.support.multidex.**

注意:

  1. 过滤的类路径使用非混淆的。
  2. 使用全局 split(或不加,也当做排除的),仅仅只有指定了 split 的类才会被移出 maindex,未标注的剩余类都会保留在 maindex 中。如果只使用 keep,那只有被 keep 的类会在 maindexlist。配置不当会,会出现未指定过的类。
  3. suggest-split 与 suggest-keep 规则如同 第 2 条。
  4. 如果使用了全局过滤,又使用了 suggest-xxx,那么只要其中一个结果成立,那么这个类都会 maindexlist 中。建议仅使用 suggest-split 对 ADT 推荐的列表进行再过滤。
  5. 如果你要过滤内部类, 使用$*,例如: SomeClass$*.class。

3.在你的 App 模块的 build.gradle 增加:

apply plugin: 'com.ceabie.dexnkife'

最后,在 app 工程中设置:

multiDexEnabled true
  • 注意:要在 defaultConfig 或者 buildTypes 中打开 multiDexEnabled true,否则不起作用。

4.编译你的应用

调试

在 Terminal 中运行 gradleDebug。具体参见:http://blog.csdn.net/ceabie/article/details/55271161

License

Copyright (C) 2017 ceabie (http://blog.csdn.net/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.
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools