NoClassDefFoundError 排查过程分析

2,089 阅读2分钟

记一次 NoClassDefFoundError 排查过程

背景

在一次将应用程序打入系统包后,出现了该报错,随之伴随着应用频繁崩溃。

堆栈打印如下:

java stacktrace:
java.lang.NoClassDefFoundError: com.manager.HiBoardMgr$1
	at com.manager.HiBoardMgr.<init>(HiBoardMgr.java:322)
	at com.manager.HiBoardMgr.getInstances(HiBoardMgr.java:313)

报错原因

developer.android.com/reference/j…

查看 Android 对于该 Error 类型进行的定义是如下:

1、JVM 或 ClassLoader 实例想要加载一个类的定义(一个正常方法调用,或者调用其的 new 关键字来创建实例),但是在这个类中找不到这个类定义。

2、这个类在编译这个将要执行的类时是存在的,但是在执行时无法找到这个类定义。

如何理解上面的定义呢?

对于上面的堆栈打印,我们找不到 HiBoardMgr1的构造函数的定义,但是我们在编译XBoardMgr1 的构造函数的定义,但是我们在编译 XBoardMgr1 时,这个类定义是有的。也就是说明我们最终的 apk 中是缺少 XBoardMgr$1 这个类的。

排查思路

反编译 apk

反编译 apk 的目的是为了去看一下是否我们的包中是否真的缺少这个类的定义。

简易方法,把 apk 拖入到 AS中,查看它的 classes.dex 文件。

1.png

根据一层层报名去找到我们需要的类。

2.png

的确是存在的。

ClassNotFound 与 NoClassDefFound 的区别

这里就涉及到了第一个误区与弯路,由于不明确两者的区别,我认为这个类是存在的,即认为我们的 apk 是没有问题的,并不是打包过程中的问题。而是 odex 加载过程、MultiDex 相关的问题,往这个错误方向排查了很久。

这里先解释一下两者的区别:

1、ClassNotFoundException 是当我们在运行时想要去加载(load)一个类时,通过 loadClass() 、 Class.forName() 方法,然后这个类在我们的 classPath 中无法被找到时会报错。

显然我们不属于上面的情况,该报错也不会发生

2、NoClassDefFoundError 是指我们在编译期这个类是存在的,但是在运行时它找不到了,才会报这个错。

我们属于这种情况,应该去做的是为什么会在运行时缺少这个类。

查看 HiBoardMgr$1 的类定义

带有 $1 这种的类名,一般是匿名内部类之类,那么我们就看看我们代码里是怎么使用的这个匿名内部类。

3.png

可以看到有三个定义,一个是 HiBoardMgr 的本身对象,另外一个是这个匿名内部类实现的函数,

还有一个全局变量,也是 HiBoardMgr 本身。

那么我们就确认下这个找不到的类定义在代码中是如何实现的:

4.png

用到的是 ExceptionCallBack 接口类,那么就是这个类找到不!

5.png

并且查一下 dex 文件中存不存在,果然也没有,那么就是这个问题!

反查代码:

由于之前正常版本是正常的,反查一下之前版本,这个类有没有呢?

是有的,并且可以通过 AS 来反查到对应的 jar 包

6.png

而目前的代码上面是没有这个库的引用的,那么就反查一下为什么会删除该依赖原因就行。

解决方案

通知其他组通知删除 jar 包中对于第三方 class 的引用,

之后重新打包后问题修复。

参考资料

1、NoClassDefFoundError DOCUMENT

2、ClassNotFoundException vs. NoClassDefFoundError