Android开发专栏Android八阿哥lalala

Android 5.0以下出现 java.lang.NoClas

2017-08-29  本文已影响1704人  bb84e92acf1d

原文地址:http://www.jianshu.com/p/48523f05c569

 

前言

最近的项目接近尾声,同事无意之中想用他的老古董手机(Android4.2版本)对APP进行测试,APP却毫不留情面的一开即蹦,然后同事一脸懵逼的对着我抛出

吓得我赶紧试试我的手机(Android6.0),却发现一切正常,于是拿他的手机进行调试,发现抛出了java.lang.NoClassDefFoundError异常,

通过查阅相关资料,最终发现是分包的原因导致,并找到一个比较大众化的解决办法

常规办法

在此简单说一下此办法的步骤,不再赘言多加描述,详情可点击链接查看具体。

一、build.gradle(app)下配置

android{
          defaultConfig{
                multiDexEnabled true
        }
}
dependencies{

compile'com.android.support:multidex:1.0.1'

}

二、使用自定义的Application继承MultiDexApplication这个类,或者重写Application的方法attachBaseContext(),并调用MultiDex.install();

@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);

MultiDex.install(base);
}

三、到项目的根目录下执行  gradlew clean build(本人不知这步是否有必要,因为我的问题试用了这步依旧没法解决)

完成以上三步后,一般都可以解决该问题,如果未解决依然报类似的错误请接着往下看


深入解决

到这如果还没解决的话,对于初级程序员,一般都是比较懵逼的了,因为网上搜到的解决办法大都是上面三步。这里将直接讲解决办法以及遇到的问题,详情可看解决问题的干货 (大佬对分包出现的坑进行了总结和讲解,强烈推荐)。

:com.android.tools.build:gradle:3.0.0,个人使用Demo用最新版的插件测试可行,如果你这边不可以的话,可以联系我一起探讨。

第一步、添加分包支持

将上文中的三步完成。

ps:使用了JessYanCoding的项目框架的话,先尝试新建一个自己的application并继承于MultiDexApplication,并把源码的BaseApplication的所有内容复制到你自己的application,再测试一下是否可行,如果不行再往下走。

第二步:添加根目录Gradle

buildscript {
          dependencies {
//gradle3.0.0以上使用(3.1.0以上无效,因此依旧建议新建一个baseApplication)
              classpath 'com.library.tangxiaolv:dexknife-plus:1.0.5.alpha'
//gradle3.0.0以下使用
classpath 'com.library.tangxiaolv:dexknife-plus:1.0.4'
}
}

第三步:在你的App模块的build.gradle添加插件并配置参数

apply plugin: 'dexknifePlus'
dexKnife{
//必选参数
enabled true //if false,禁用分包插件
}

第四步:在你的App模块目录下新建dexknife.txt文件,并自定义配置

#通用
-split io.**
-split android.support.v4.**
-split android.support.v7.**
-keep android.support.v4.app.**
-keep android.support.v7.app.**

#下面为命令解释 可看可不看

#为注释符
#-----------主Dex中必要依赖的脚本配置-----------(支持依赖检测)
#默认保留四大组件中Service,ContentProvider,BroadcastReceiver三大组件,
#Activity组件选择性保留,若为空不保留任何Activity
#-just activity com.ceabie.demo.MainActivity

#-----------附加类-----------(不支持依赖检测)
# 如果你想要某个包路径在maindex中,则使用 -keep 选项,即使他已经在分包的路径中.
若为空,不保留任意类
#-keep com.ceabie.demo.**
# 保留单个类.
#-keep android.support.v7.app.AppCompatDialogFragment.class

# 这条配置可以指定这个包下类在第二及其他dex中.
#-split android.support.v?.**
#将全部类移出主Dex
#-split **.**

# 不包含Android gradle 插件自动生成的miandex列表.(不使用建议的依赖树,注释掉表示使用,
否则-just activity无效)
#-donot-use-suggest
# (分割每个dex包的方法数上限) 扩展参数:例如 --set-max-idx-number=50000
# 如果出现 DexException: Too many classes in --main-dex-list, main dex capacity exceeded:
# 表明限制的方法数小于main dex的必要方法数,调大到合适数值即可
#-dex-param --set-max-idx-number=4000

# 注释掉将执行自定义dex分包,否则执行默认分包.
#-auto-maindex
#-log-mainlist

这里讲解下遇到的坑

出于惯性思维的思考,既然是该方法或类不在主Dex中,那用-keep 方法将相应的方法或类保留在主Dex中,但本人发现并非所有的类都生效,依旧有部分类的初始化出现该异常,打开APK查看出错的具体类时,出现这样的现象。

主Dex

在主Dex中,报错方法的具体类Defined Methods方法数为0,而副Dex中Defined Methods的方法数却是满的,说明-keep并没有将相应的方法真正留在主Dex中。

副Dex

既然惯性思维行不通,便反其道而行之,既然不能保留相应的类,那就将初始化中不需要的第三方Jar包以及依赖的库使用-split移除主Dex并用-keep(此时发现-keep有效,个人感觉-keep必须配合-split使用)保留应用初始化需要的类,最终得以解决!

注:如果报Too many classes in --main-dex-list, main dex capacity exceeded:是因为主Dex方法数依旧超过64K,继续使用-split移除初始化不需要的类就好,给一张我的dexknife.txt文件的具体内容。

ps:1.如果使用了rxJava 把-split io.**加上,能少近1万个方法数,配合-split 其它包使用(例如-split android.support.v4.**),单独使用无效 。

        2.-split **.**,有朋友使用说效果不好,在此不推荐使用。

        3.使用-keep保留某个类时,需要加上.class,建议直接把那个类所在的上一级包直接全部-keep 。

        4.每个人使用的效果都有少许差异,例如使用-split android.support.v7.**时,可能会导致v7包下的部分类找不到,只要按照报错的地方稍微进行调整一般都能解决,建议优先-split第三方SDK,例如地图SDK,图片选择器等等。

为了解决此BUG,本人查阅了许多分包的资料,花费了大量的时间,最终得以解决,在此写下此文章,帮助遇到相似问题的朋友。

番外话:Android5.0以下由于分包的原因,用户第一次安装APK时,容易造成ANR的问题,在此给出一个解决办法 供大家参考。

如果这篇文章对您有所帮助,可以点一下喜欢,这对我来说有着很大的帮助,谢谢。

上一篇下一篇

猜你喜欢

热点阅读