当dex分包遇上NoClassDefFoundError&ClassNotFoundException
Posted 赤耳A狼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了当dex分包遇上NoClassDefFoundError&ClassNotFoundException相关的知识,希望对你有一定的参考价值。
简介 本文记录的是:国庆节前夕,解决Crash率高达9.08%问题成功避免加班拿3倍工资的故事 PS: 除了在时间上两者相遇外,本文中提到的两个(top1&top2)crash问题与dex分包并没有关系初见问题(2015-09-25) 2015-09-25:产品灰度第一天灰度结果:Crash率9.08%,主要是如下两个Crash所导致
TOP1: java.lang.NoClassDefFoundError
TOP2: java.lang.RuntimeException/java.lang.ClassNotFoundException堆栈还原: java.lang.NoClassDefFoundError: com/example/assistant/activity/BaseActivity java.lang.Class.newInstanceImpl(Native Method) java.lang.Class.newInstance(Class.java:1319) android.app.Instrumentation.newActivity(Instrumentation.java:1056) android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1984) android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2094) android.app.ActivityThread.access$600(ActivityThread.java:134) android.app.ActivityThread$H.handleMessage(ActivityThread.java:1202) android.os.Handler.dispatchMessage(Handler.java:99) android.os.Looper.loop(Looper.java:137) android.app.ActivityThread.main(ActivityThread.java:4791) java.lang.reflect.Method.invokeNative(Native Method) java.lang.reflect.Method.invoke(Method.java:511) com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:766) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:533) dalvik.system.NativeStart.main(Native Method)
堆栈还原: java.lang.RuntimeException: Unable to instantiate activity ComponentInfocom.example.android.downloader/com.example.assistant.activity.SpaceCleanActivity: java.lang.ClassNotFoundException: Didn’t find class “com.example.assistant.activity.SpaceCleanActivity” on path: DexPathList[[zip file “/data/app/com.example.android.downloader-1.apk”, zip file “/data/data/com.example.android.downloader/app dex/extlibs.1.jar”],nativeLibraryDirectories=[/data/app-lib/com.example.android.downloader-1, /vendor/lib, /system/lib]] android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2274) android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417) android.app.ActivityThread.access$800(ActivityThread.java:151) android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342) android.os.Handler.dispatchMessage(Handler.java:110) android.os.Looper.loop(Looper.java:193) android.app.ActivityThread.main(ActivityThread.java:5322) java.lang.reflect.Method.invokeNative(Native Method) java.lang.reflect.Method.invoke(Method.java:515) com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:829) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645) dalvik.system.NativeStart.main(Native Method) cause by: java.lang.ClassNotFoundException: Didn’t find class “com.example.assistant.activity.SpaceCleanActivity” on path: DexPathList[[zip file “/data/app/com.example.android.downloader-1.apk”, zip file “/data/data/com.example.android.downloader/appdex/extlibs.1.jar”],nativeLibraryDirectories=[/data/app-lib/com.example.android.downloader-1, /vendor/lib, /system/lib]] dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) java.lang.ClassLoader.loadClass(ClassLoader.java:497) java.lang.ClassLoader.loadClass(ClassLoader.java:457) android.app.Instrumentation.newActivity(Instrumentation.java:1061) android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2265) android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417) android.app.ActivityThread.access$800(ActivityThread.java:151) android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342) android.os.Handler.dispatchMessage(Handler.java:110) android.os.Looper.loop(Looper.java:193) android.app.ActivityThread.main(ActivityThread.java:5322) java.lang.reflect.Method.invokeNative(Native Method) java.lang.reflect.Method.invoke(Method.java:515) com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:829) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645) dalvik.system.NativeStart.main(Native Method)
线索
- java.lang.NoClassDefFoundError: com/example/assistant/activity/BaseActivity
- java.lang.ClassNotFoundException: Didn’t find class “com.example.assistant.activity.SpaceCleanActivity”
联想 这两个Crash问题在灰度版本中首次发现,恰好该灰度版本有新加入的dex分包功能,于是dex分包合情合理的成了头号怀疑对象,但是Check分包方案后有如下疑问:
- Crash栈中的BaseActivity和SpaceCleanActivity在主dex中
- 这两个Crash在测试中不可复现
意外发现Crash与dex分包无关(2015-09-28) 2015-09-28:在经过几次分包方案调整后依然不见效果,项目时间紧迫,为了尽快暴露其它Crash,我们尝试先针对android 3.0以上用户灰度一个不带分包方案的版本 2015-09-29: 发现不带dex分包方案的灰度版本中依然出现之前的 TOP1&TOP2 Crash
重新审视问题,搜集更多线索 经过新版本的灰度结果,发现TOP1&TOP2的Crash与分包毫无关系,之前一直研究的方向是错误的,所以我们把再次关注问题本身,深入研究问题,查找到了更多的线索 Bugly抓取的自定义日志内容: Bugly抓取的系统日志内容: 如上图,我们得到以下线索
- 程序是运行了一段较长的时间才Crash的
appliationCreateTime:2015-09-29 21:23:33 crashTime:2015-09-29 22:49:58
- Crash发生时,没有进入过任何activity
contact:991327—,NotAdd—
- Crash发生时,都伴随有如下错误信息
09-29 22:49:57.948 I/dalvikvm( 4203): Rejecting re-init on previously-failed class Lcom/example/assistant/activity/BaseActivity; v=0x0 09-29 22:49:57.948 W/dalvikvm( 4203): Class init failed in newInstance call (Lcom/example/pangu/activity/AppDetailActivityV5;) 09-29 22:49:57.948 D/AndroidRuntime( 4203): Shutting down VM 09-29 22:49:57.948 W/dalvikvm( 4203): threadid=1: thread exiting with uncaught exception (group=0x40f852a0) 09-29 22:49:57.948 W/System.err( 4203): java.lang.NoClassDefFoundError: com/example/assistant/activity/BaseActivity
分析 推论:
- 线索1+线索3 -> Crash发生的前提条件:程序在后台运行了一段时间,且BaseActivity类之前初始化失败过
-
线索2+线索3 -> Crash的触发操作:第一次创建Activity对象(BaseActivity是所有Activity的基类)
偶然发现重要线索 BaseActivity.java类近期无修改,且常规测试也不能重现BaseActivity类初始化失败的场景,问题再次陷入僵局。幸好,在RDM异常上报平台逐个查看logcat信息的过程中,我们在第4页找到了一个关键的日志信息 log.txt 我们分别来看看对应关键地方的代码
- com.example.nucleus.manager.spaceclean.SpaceScanManager.y(ProGuard:709)
- com.example.assistant.activity.BaseActivity.(ProGuard:750)
-
再来看看SpaceCleanActivity.isActivityInfront
制造场景重现Crash 知道了Crash发生的原理,很简单的就能制造场景重现Crash,代码如下: 结果: 第一处被catch住的java.lang.ExceptionInInitializerError异常 第二处没有catch的java.lang.NoClassDefFoundError异常
解决 知道了根因,解决就简单了,直接上解决方法
- 将静态变量isActivityInfront从SpaceCleanActivity移至SpaceScanManager(非BaseActivity子类,不会导致在非主线程中加载BaseActivity类)
-
修复BaseActivity中存在的潜在风险,将静态成员
灰度验证 吸取之前的教训,这次我们同时灰度了两个包,对比dex分包所带来的问题: 验证结果:9112和9113版本Crash率均在预期的范围内(0.5%左右),且无TOP1&TOP2 Crash
总结
- 惯性思维思考问题容易陷入盲区
- 相信证据,当证据和常识相违背时,给证据多一点信任,深入分析其根因
- 当解决问题遇到瓶颈时,不要忘记重新审视问题本身,挖掘每一条细微的线索,并认真对待
-
关于NoClassDefFoundError Crash的技术总结: 使用java类的静态变量时,会触发类的加载操作,类加载过程会初始化类的所有静态变量,如果静态变量初始化失败,将导致类加载失败,并抛出java.lang.ExceptionInInitializerError异常 若类加载java.lang.ExceptionInInitializerError异常被try/catch住,下次new类对象时将产生java.lang.NoClassDefFoundError异常 谨慎使用try/catch,try/catch很容易掩盖事故的第一现场。try/catch处,至少要将catch到的异常进行输出throwable.printStackTrace();,否则第一现场不光会被掩盖,而且找不到任何蛛丝马迹
留给读者的问题 非主线程(没有Looper)中是否可以创建Activity对象? 读者可以试验如下代码 [Bash shell] 纯文本查看 复制代码 ?
1 2 3 4 5 6 |
new Thread()
@Override
public void run()
Activity activity = new Activity();
.start();
|
以上是关于当dex分包遇上NoClassDefFoundError&ClassNotFoundException的主要内容,如果未能解决你的问题,请参考以下文章