Giter Site home page Giter Site logo

dexknifeplugin's Introduction

License Download Android Arsenal

DexKnife

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
    -split **
    -keep android.support.multidex.** # keep multidex lib
    -keep # wildcards of other keeping classes, and the count of idx don't overflow 65535

  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
    -split **
    -keep android.support.multidex.** # 保证 multidex 或者你自己开发的multidex
    -keep # 配置你的keep类的通配符,但数量不能超界

  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.

dexknifeplugin's People

Contributors

ceabie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dexknifeplugin's Issues

java.lang.NullPointerException(no error message)

:app:transformResourcesAndNative_libsWithJavaResourcesVerifierForDebug UP-TO-DATE
:app:transformClassesWithInstantRunForDebug UP-TO-DATE
:app:transformClasses_enhancedWithInstant+reloadDexForDebug UP-TO-DATE
:app:incrementalDebugTasks
:app:prePackageMarkerForDebug
:app:generateDebugInstantRunAppInfo UP-TO-DATE
:app:coldswapKickerDebug
:app:transformClassesWithInstantRunSlicerForDebug UP-TO-DATE
:app:transformClassesWithDexForDebug
DexKnife Processing ...
DexKnife Config: -donot-use-suggest
DexKnife Config: -auto-maindex
DexKnife Config: -log-mainlist
DexKnife Config: -keep android.support.v4.view.**
DexKnife Config: -keep tv.danmaku.ijk.media.**
DexKnife Config: android.support.v?.**
:app:genMainDexList
DexKnife: From Merged Jar
:app:transformClassesWithDexForDebug FAILED

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':app:transformClassesWithDexForDebug'.

    java.lang.NullPointerException (no error message)

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 1.83 secs

扩展性问题:代码改动新加类,插件能自动处理这种场景吗?

代码改动,类变动很快,然后在某些android版本上会can't find class, 某些版本能编译通过。 由于插件里面keep类,是写死的,这样每次维护起来就很困难,尤其合作开发,别人不清楚这个功能当有错误就很麻烦。    

这个能有什么功能能自动的支持这种场景吗,当新添加类的时候能识别,并作警示,或者能自动加入keep。

加代码混淆之后就不能工作了

minifyEnabled true

日志
Copying resources from program jar [/Users/bici/githome/androidOpenSource/DexKnifePlugin/app/build/intermediates/transforms/proguard/debug/jars/3/1f/main.jar]
:app:transformClassesWithDexForDebug
DexKnife Processing ...
DexKnife Config: -donot-use-suggest
DexKnife Config: -log-mainlist
DexKnife Config: -split rx.**
DexKnife Config: -keep android.support.v4.view.**
DexKnife Config: android.support.v?.**
DexKnife-MergedJar: null
DexKnife Finished: 1ms

提一个问题

如果在instant run模式下 自动禁用,那么main dex 的分包策略就不会受我们影响,这样当放法数足够大的时候(比如20W)main dex还是会爆炸.这个问题怎么办

插件有问题

无法分包,isAtMainDex这函数返回都是true,导致maindexlist.txt文件里的类太多

DexKnife: The Merged Jar is not exist! Can't be processed!

:app:coldswapKickerDebug
:app:transformClassesWithInstantRunSlicerForDebug
:app:transformClassesWithDexForDebug
DexKnife Processing ...
DexKnife Config: -donot-use-suggest
DexKnife Config: -log-mainlist
DexKnife Config: -split rx.**
DexKnife Config: -keep android.support.v4.view.**
DexKnife Config: -keep android.support.v7.app.appcompatdialogfragment.class
DexKnife Config: android.support.v?.**
:app:genMainDexList
DexKnife: From Merged Jar: null

DexKnife: The Merged Jar is not exist! Can't be processed!

:app:transformClassesWithDexForDebug FAILED

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':app:transformClassesWithDexForDebug'.

    DexKnife Warnning: Main dex is EMPTY ! Check your config and project!

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 3.191 secs

启用混淆后,出现class no def的crash

分包成功后,运行未混淆的代码,正常。

进行混淆后,出现class no def的crash,进行keep,dex load完后,用class loader进行load,仍然出现class no def的exception。

Error:Execution failed for task ':transformClassesWithDexForReleasePackageDebug'.

更新到android studio 2.2之后,报下面这个错误
Error:Execution failed for task ':transformClassesWithDexForReleasePackageDebug'.

No signature of method: com.ceabie.dexknife.MultiDexAndroidBuilder.setTargetInfo() is applicable for argument types:
(com.android.builder.sdk.SdkInfo, com.android.builder.sdk.TargetInfo, com.google.common.collect.RegularImmutableList)
values: [com.android.builder.sdk.SdkInfo@359e6321, com.android.builder.sdk.TargetInfo@4ef61642, ...]
Possible solutions: getTargetInfo(), setTargetInfo(com.android.builder.sdk.TargetInfo)

com.android.tools.build:gradle:3.0.0-alpha2支持

你好,请问什么时候会提供对com.android.tools.build:gradle:3.0.0提供支持?目前在alpha2版本上无法编译运行,出现如下错误:
Error:No such property: multiDex for class: com.android.build.gradle.internal.transforms.DexTransform

如果不使用DexKnifePlugin能编译成功并运行。

jarMerging is Null! Skip DexKnife. Please report All Gradle Log.

DexKnife Processing ...
DexKnife Adt Main: null
DexKnife Config: -keep android.support.v4.view.**
DexKnife Config: -keep com.hoge.android.cooperation.**
DexKnife Config: android.support.v?.**
DexKnife Config: -keep android.support.v7.app.AppCompatDialogFragment.class
DexKnife Config: -filter-suggest
DexKnife Config: -auto-maindex
DexKnife Config: -dex-param --set-max-idx-number=50000
DexKnife Config: -log-mainlist
DexKnife Config: -log-filter
DexKnife-From MergedJar: null
DexKnife Error: jarMerging is Null! Skip DexKnife. Please report All Gradle Log.
DexKnife Finished: 16ms

查看Log显示以上错误,我的AS是2.3
compile'com.android.support:multidex:1.0.1'
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.ceabie.dextools:gradle-dexknife-plugin:1.6.0'
distributionUrl=https://services.gradle.org/distributions/gradle-2.14.1-all.zip

拆包规则:
image

我的2个工程一样的配置,另外一个工程是可以拆包成功的,是不是因为我代码里的jar问题?

gradle 3.2,编译报错org.gradle.api.specs.Specs.not(Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/specs/NotSpec;

你好,我使用gradle 3.2版本,命令行编译报错,请问为什么?如下是编译的console错误信息:

DexKnife Processing ...
DexKnife Adt Main: /data/Code/maitao-android/maitao/build/intermediates/multi-dex/maitao/debug/maindexlist.txt
DexKnife Config: -keep com.umeng.message.**
DexKnife Config: -split .
DexKnife Config: -dex-param --set-max-idx-number=65000
DexKnife Config: -auto-maindex
DexKnife Config: -log-mainlist
DexKnife-From MergedJar: /data/Code/maitao-android/maitao/build/intermediates/transforms/jarMerging/maitao/debug/jars/1/1f/combined.jar
:maitao:genMainDexList
DexKnife: use suggest
DexKnife: From MergedJar: /data/Code/maitao-android/maitao/build/intermediates/transforms/jarMerging/maitao/debug/jars/1/1f/combined.jar
:maitao:transformClassesWithDexForMaitaoDebug FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':maitao:transformClassesWithDexForMaitaoDebug'.
> org.gradle.api.specs.Specs.not(Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/specs/NotSpec;

proguard issue

我使用了这个插件方案,发现Java的class name全部变为不混淆的了,请问怎么解决,是插件内做的吗?

使用了dexknife之后会报DexException错误,如何解决?

加入了dexknife发现会报com.android.dex.DexException: Too many classes in --main-dex-list,错误,去掉之后打包是正常的,可能是由于我的项目比较大,生成4个dex,项目的类比较多。
查看了生成的maindexlist.txt,发现使用了dexknife后比没使用多出了一万多个class,应该是dexknife有一套自己的生成规则,然后覆盖了系统生成的maindexlist.txt、

所以请问如下问题:
1.有什么方法可以解决这个com.android.dex.DexException的问题?使用哪个命令?
2.是否可以提供一种模式直接在系统生成的maindexlist.txt后面追加上自己要保留的类?

debug版本出现问题 Unable to instantiate application Caused by: java.lang.ClassNotFoundException: Didn't find class on path: DexPathList

01-17 11:13:38.160 15931-15931/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: tc.everphoto, PID: 15931
java.lang.RuntimeException: Unable to instantiate application everphoto.DebugApp: java.lang.ClassNotFoundException: Didn't find class "everphoto.DebugApp" on path: DexPathList[[zip file "/data/app/tc.everphoto-1.apk"],nativeLibraryDirectories=[/data/app-lib/tc.everphoto-1, /vendor/lib, /system/lib]]
at android.app.LoadedApk.makeApplication(LoadedApk.java:516)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4317)
at android.app.ActivityThread.access$1500(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassNotFoundException: Didn't find class "everphoto.DebugApp" on path: DexPathList[[zip file "/data/app/tc.everphoto-1.apk"],nativeLibraryDirectories=[/data/app-lib/tc.everphoto-1, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
at android.app.Instrumentation.newApplication(Instrumentation.java:975)
at android.app.LoadedApk.makeApplication(LoadedApk.java:511)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4317) 
at android.app.ActivityThread.access$1500(ActivityThread.java:135) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:136) 
at android.app.ActivityThread.main(ActivityThread.java:5017) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:515) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 
at dalvik.system.NativeStart.main(Native Method) 

5.0以上和以下具体区别是什么,我在5.0以下不能启动应用

java.lang.RuntimeException: Unable to get provider com.tencent.android.tpush.SettingsContentProvider: java.lang.ClassNotFoundException: Didn't find class "com.tencent.android.tpush.SettingsContentProvider" on path: DexPathList[[zip file "/data/app/com.gs.buluo.app-2.apk"],nativeLibraryDirectories=[/data/app-lib/com.gs.buluo.app-2, /vendor/lib, /system/lib]]

分包原理

默认的multidex分包是不是只把Instrumentation 和 Application及其相关类 放入multidex/maindexlist.txt中,因为我取消dexknifplugin我看到multidex/maindexlist.txt中和开启后内容一样,于是我想到
把分包逻辑去掉,只留下InjectAndroidBuilder发现效果一样,把以下代码注释

// 非混淆的,从合并后的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)) {

配置suggest list 异常问题

#全局过滤, 如果没设置 -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 com.google.android.gms.ads..
-suggest-keep android.support.multidex.**
-suggest-keep com.meetyou.frescopainter.**

以上是我的配置;确实配置完成之后,maindexlist该移除的都移除了,但是run起来的时候报com.meetyou.frescopainter这里边的一个类找不到,这是为什么呢?还有sugget list是要在哪里看,-suggest-split 是不能随意指定的对吗?

dexknife 的一些疑问

-suggest-split **.MainActivity2.class 这样的配置是指在maindexlist里面 把MainActivity2.class 移除掉么
我配置之后发现没有生效,该类还在生成maindexlist里面

5.0以上和5.0以下的不同效果

在5.0以上新增一个sugget-split是可以正常运行的;
但是在5.0以下同样新增一个suggest-split提示ClassNotFound;
是否有什么兼容性的问题,sugget-split的那个包是在application用到的,理论上不是只要执行完multidex.install之后应该都能找到才对吗

DexKnife Adt Main: null 貌似没起作用

DexKnife Processing ...
DexKnife Adt Main: null
DexKnife Config: android.support.v?.**
DexKnife Config: -keep android.support.v4.view.**
DexKnife Config: -keep android.support.v7.app.AppCompatDialogFragment.class
DexKnife Config: -split .
DexKnife Config: -filter-suggest
DexKnife Config: -dex-param --set-max-idx-number=50000
DexKnife Config: -log-mainlist
DexKnife Config: -log-filter-suggest
DexKnife-From MergedJar: null
DexKnife Error: jarMerging is Null! Skip DexKnife. Please report All Gradle Log.
DexKnife Finished: 12ms

Tinker 兼容问题

DexKnife 如何和微信TInker 热修复 ,两个插件同时存在如何保证tinker 发布补丁包正常

使用-suggest-keep了某个指定的类,还是报NoClassDefFoundError

如题,我配置了-suggest-keep com.xxx.util.DGUtil.class,并且在生成的maindexlist文件中能找到这个类,但是还是报DGUtil的NoClassDefFoundError错误。我反编译看了一下,DGUtil并没有被打包到classes.dex中去,而是在classes2.dex里面。求指点!
我的配置如下:

# 将 全局过滤配置应用到 建议的maindexlist中, 但 -donot-use-suggest 要关闭.
-filter-suggest

# 显示miandex的日志.
-log-mainlist

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

-suggest-keep com.xxx.ui.main.InstallDexActivity.class
-suggest-keep android.support.multidex.**
-suggest-keep com.xxx.util.DGUtil.class

修改过的DexSplitTools(目前能符合我的需求,但可能会有些问题)

/*

  • 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.NotSpec;
import org.gradle.api.specs.OrSpec;
import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs;
import org.gradle.api.tasks.util.PatternSet;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
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;
     }
    
     try {
         return genMainDexList(project, minifyEnabled, mappingFile, jarMergingOutputFile,
                 andMainDexList, dexKnifeConfig);
     } catch (Exception e) {
         e.printStackTrace();
     }
    
     return false;
    

    }

    /**

    • 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 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();

       System.out.println(line);
      
      
       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;
           System.out.println(DEX_KNIFE_CFG_AUTO_MAINDEX + " = " + minimalMainDex);
       } 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")) {
               System.out.println(param);
               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);
      }

      // 使用ADT建议的mainlist
      if (dexKnifeConfig.useSuggest) {
      System.err.println("dexKnifeConfig.useSuggest = " + dexKnifeConfig.useSuggest);
      // 将全局过滤应用到建议的mainlist
      if (dexKnifeConfig.filterSuggest) {
      splitSuggest.addAll(splitToSecond);
      keepSuggest.addAll(keepMain);
      }

       if (!splitSuggest.isEmpty() || !keepSuggest.isEmpty()) {
      
           dexKnifeConfig.splitSuggest = splitSuggest;
           dexKnifeConfig.keepSuggest = keepSuggest;
       }
      

      }

      System.err.println("splitToSecond size = " + splitToSecond.size() + " keepMain size = " + keepMain.size());

      if (!splitToSecond.isEmpty() || !keepMain.isEmpty()) {
      dexKnifeConfig.splitToSecond = splitToSecond;
      dexKnifeConfig.keepMain = keepMain;
      } else {
      dexKnifeConfig.useSuggest = true;
      System.err.println("DexKnife Warning: NO SET split Or keep path, it will use Suggest!");
      }

      dexKnifeConfig.additionalParameters = addParams;

      System.out.println("filterSuggest = " + dexKnifeConfig.filterSuggest + " logMainList = " + dexKnifeConfig.logMainList + " logFilterSuggest = " + dexKnifeConfig.logFilterSuggest);
      System.out.println("useSuggest = " + dexKnifeConfig.useSuggest);

      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<FileTreeElement> includeSpec = null;
         Spec<FileTreeElement> excludeSpec = null;
    
         if (!patternSet.getIncludes().isEmpty()) {
             includeSpec = patternSet.getAsIncludeSpec();
         }
    
         if (!patternSet.getExcludes().isEmpty()) {
             excludeSpec = patternSet.getAsExcludeSpec();
         }
    
         if (includeSpec != null) {
             if (excludeSpec != null) {
                 maindexSpec = new OrSpec<>(includeSpec, new NotSpec<>(excludeSpec));
             } else {
                 maindexSpec = new OrSpec<>(includeSpec);
             }
         } else {
             if (excludeSpec == null) {
                 maindexSpec = Specs.satisfyAll();
             } else {
                 maindexSpec = new NotSpec<>(excludeSpec);
             }
         }
     }
    
     if (maindexSpec == null) {
         maindexSpec = Specs.satisfyAll();
     }
    
     return maindexSpec;
    

    }

    private static boolean isPatternSetEmpty(PatternSet patternSet) {
    return patternSet.getExcludes().isEmpty() && patternSet.getIncludes().isEmpty()
    && patternSet.getExcludeSpecs().isEmpty() && patternSet.getIncludeSpecs().isEmpty();
    }

    /**

    • 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");

      // 1.get the adt's maindexlist
      Map<String, Boolean> adtMainClasses = null;
      if (dexKnifeConfig.useSuggest) {

       Set<String> splitSuggest = dexKnifeConfig.splitSuggest;
       Set<String> keepSuggest = dexKnifeConfig.keepSuggest;
       if (dexKnifeConfig.filterSuggest && keepSuggest == null) {
           splitSuggest = dexKnifeConfig.splitToSecond;
           keepSuggest = dexKnifeConfig.keepMain;
       }
      
       System.out.println("DexKnife: use suggest");
       adtMainClasses = getAdtMainDexClasses(andMainDexList, keepSuggest, splitSuggest, dexKnifeConfig.logFilterSuggest);
      

      }

// new File("$buildDir/intermediates/multi-dex/$variant.buildType.name/maindexlist.txt")

    File keepFile = project.file(MAINDEXLIST_TXT);
    System.out.println("keepFile AbsolutePath = " + keepFile.getAbsolutePath());

    keepFile.delete();

    // 2.process the global filter
    List<String> mainClasses = null;
    if ((dexKnifeConfig.splitToSecond == null || dexKnifeConfig.splitToSecond.isEmpty()) && (dexKnifeConfig.keepMain == null || dexKnifeConfig.keepMain.isEmpty())) {
        // only filter the suggest list
        if (adtMainClasses != null && adtMainClasses.size() > 0) {
            mainClasses = new ArrayList<>();
            Set<Map.Entry<String, Boolean>> entries = adtMainClasses.entrySet();
            for (Map.Entry<String, Boolean> entry : entries) {
                if (entry.getValue()) {
                    mainClasses.add(entry.getKey());
                }
            }
        }
    } else {
        if (minifyEnabled) {
            System.err.println("DexKnife: From Mapping");
            if (adtMainClasses != null) {
                System.out.println("adtMainClasses size = " + adtMainClasses.size());
            }

            // get classes from mapping
            mainClasses = getMainClassesFromMapping(mappingFile, dexKnifeConfig.keepMain, dexKnifeConfig.splitToSecond, adtMainClasses);

            System.out.println("mainClasses size = " + mainClasses.size());
        } else {
            System.out.println("DexKnife: From MergedJar: " + jarMergingOutputFile);
            if (jarMergingOutputFile != null) {
                // get classes from merged jar
                mainClasses = getMainClassesFromJar(jarMergingOutputFile, dexKnifeConfig.keepMain, dexKnifeConfig.splitToSecond, adtMainClasses);
            } else {
                System.err.println("DexKnife: The Merged Jar is not exist! Can't be processed!");
            }
        }
    }

    // 3.create the miandexlist
    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!");
}

/**
 * Gets main classes from jar.
 *
 * @param jarMergingOutputFile the jar merging output file
 * @param adtMainCls           the filter mapping of suggest classes
 * @return the main classes from jar
 * @throws Exception the exception
 * @author ceabie
 */
private static ArrayList<String> getMainClassesFromJar(
        File jarMergingOutputFile, Set<String> keepSuggest, Set<String> splitSuggest, Map<String, Boolean> adtMainCls)
        throws Exception {
    ZipFile clsFile = new ZipFile(jarMergingOutputFile);

// ClassFileTreeElement treeElement = new ClassFileTreeElement();

    // lists classes from jar.
    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);

            boolean isAtMainDex = false;
            Boolean object = null;

            if (adtMainCls != null) {
                object = adtMainCls.get(entryName);
            }

            if (object == null) {
                if (keepSuggest != null) {
                    if (startWith(keepSuggest, entryName)) {
                        isAtMainDex = true;
                    }
                }
            } else {
                isAtMainDex = object;

                if (isAtMainDex) {
                    if (splitSuggest != null) {
                        if (startWith(splitSuggest, entryName)) {
                            isAtMainDex = false;
                        }
                    }
                }
            }



            if (isAtMainDex) {
                mainDexList.add(entryName);
            }
        }
    }

    clsFile.close();

    return mainDexList;
}

/**
 * Gets main classes from mapping.
 *
 * @param mapping    the mapping file
 * @param adtMainCls the filter mapping of suggest classes
 * @return the main classes from mapping
 * @throws Exception the exception
 * @author ceabie
 */
private static List<String> getMainClassesFromMapping(
        File mapping,
        Set<String> keepSuggest, Set<String> splitSuggest,
        Map<String, Boolean> adtMainCls) throws Exception {

    String line;
    List<String> mainDexList = new ArrayList<>();
    BufferedReader reader = new BufferedReader(new FileReader(mapping)); // all classes

    ClassFileTreeElement filterElement = new ClassFileTreeElement();

    while ((line = reader.readLine()) != null) {
        line = line.trim();

        if (line.endsWith(":")) {
            int flagPos = line.indexOf(MAPPING_FLAG);
            if (flagPos != -1) {
                String sOrgCls = line.substring(0, flagPos).replace('.', '/') + CLASS_SUFFIX;
                String sMapCls = line.substring(flagPos + MAPPING_FLAG_LEN, line.length() - 1)
                        .replace('.', '/') + CLASS_SUFFIX;

// filterElement.setClassPath(sOrgCls);

                boolean isAtMainDex = false;
                Boolean object = null;

                if (adtMainCls != null) {
                    object = adtMainCls.get(sMapCls);
                }

                if (object == null) {
                    if (keepSuggest != null) {
                        if (startWith(keepSuggest, sOrgCls)) {
                            isAtMainDex = true;
                        }
                    }
                } else {
                    isAtMainDex = object;

                    if (isAtMainDex) {
                        if (splitSuggest != null) {
                            if (startWith(splitSuggest, sOrgCls)) {
                                isAtMainDex = false;
                            }
                        }
                    }
                }

                System.out.println("Filter: " + sOrgCls + " [" + isAtMainDex + "]");
                if (isAtMainDex) {
                    mainDexList.add(sMapCls);
                }
            }
        }
    }

    reader.close();

    return mainDexList;
}

private static boolean isAtMainDex(
        Map<String, Boolean> mainCls, String sMapCls,
        ClassFileTreeElement treeElement, Spec<FileTreeElement> keepMain, Spec<FileTreeElement> splitSuggest) {

    if (mainCls != null) {
        Boolean inAdtList = mainCls.get(sMapCls);
        if (inAdtList != null) {
            System.out.println("inAdtList: " + inAdtList);
            return inAdtList;
        }
    }

    boolean flag = false;

    if (splitSuggest != null && splitSuggest.isSatisfiedBy(treeElement)) {

        return false;
    }

    if (keepMain != null) {
        flag = keepMain.isSatisfiedBy(treeElement);
    }

    System.out.println("flag: " + flag);

    return flag;
}

private static boolean isAtMainDex(
        Map<String, Boolean> mainCls, String sMapCls,
        ClassFileTreeElement treeElement, Spec<FileTreeElement> asSpec) {

    if (mainCls != null) {
        Boolean inAdtList = mainCls.get(sMapCls);
        if (inAdtList != null) {
            System.out.println("inAdtList: " + inAdtList);
            return inAdtList;
        }
    }

    boolean flag = asSpec.isSatisfiedBy(treeElement);
    System.out.println("flag: " + flag);

    return flag;
}

/**
 * get the maindexlist of android gradle plugin.
 * if enable ProGuard, return the mapped class.
 */
private static Map<String, Boolean> getAdtMainDexClasses(
        File outputDir,
        Set<String> keepSuggest, Set<String> splitSuggest,
        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;
    }

    HashMap<String, Boolean> mainCls = new HashMap<>();
    BufferedReader reader = new BufferedReader(new FileReader(outputDir));

    ClassFileTreeElement treeElement = new ClassFileTreeElement();

    String line, clsPath;
    while ((line = reader.readLine()) != null) {
        line = line.trim();
        int clsPos = line.lastIndexOf(CLASS_SUFFIX);
        if (clsPos != -1) {
            boolean satisfiedBy = true;

            if (keepSuggest != null || splitSuggest != null) {
                clsPath = line.substring(0, clsPos).replace('.', '/') + CLASS_SUFFIX;
                treeElement.setClassPath(clsPath);

                if (keepSuggest != null) {
                    if (!startWith(keepSuggest, clsPath) && (splitSuggest != null && startWith(splitSuggest, clsPath))) {
                        satisfiedBy = false;
                    }
                } else {
                    if (splitSuggest != null) {
                        if (startWith(splitSuggest, clsPath)) {
                            satisfiedBy = false;
                        }
                    }
                }

                if (logFilter) {
                    System.out.println("DexKnife-Suggest: [" +
                            (satisfiedBy ? "Keep" : "Split") + "]  " + clsPath);
                }
            }

            mainCls.put(line, satisfiedBy);
        }
    }

    reader.close();

    if (mainCls.size() == 0) {
        mainCls = null;
    }

    return mainCls;
}

static boolean startWith(Set<String> splitSuggest, String clsPath) {
    if (splitSuggest == null || clsPath == null || clsPath.length() <= 0) {
        return false;
    }
    Iterator<String> it = splitSuggest.iterator();
    while (it.hasNext()) {
        String str = it.next();
        if (str != null && str.startsWith(clsPath)) {
            return true;
        }
    }

    return false;
}

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;
}

}

设置-auto-maindex遇到的问题

om.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.dex.DexException: Too many classes in --main-dex-list, main dex capacity exceeded

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.