ANR 是 Android 应用程序中的 "Application Not Responding" 的缩写,中文意思是 "应用程序无响应"。这是当应用程序在 Android 系统上运行时,由于某种原因不能及时响应用户输入事件或执行一个操作,导致界面无法更新,系统弹出对话框通知用户应用程序未响应,给用户提供了等待它响应或是直接关闭的选项。
当应用程序发生ANR 时,Android 系统会在设备的日志文件中记录相关信息,这个文件包含了在 ANR 发生时的各个线程的调用堆栈(路径:/data/anr/traces.txt)
但是 /data/anr/ 路径只有 root 权限才能够进行访问,在非 root 权限的情况下需要 adb 命令导出相关的日志文件后筛选查看 ANR 的日志信息
举个例子,故意写个 ANR:
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)findViewById<Button>(R.id.btn).setOnClickListener {Thread.sleep(100000)}}
}
连续点击按钮,等待系统 ANR 的弹框,点击确认。
将设备的诊断信息日志导出到电脑上(路径默认是adb的路径):
adb bugreport
想到电脑上的指定路径也可以配置:
adb bugreport D:\log.zip
把导出的 zip 包解压缩后打开:
日志文件比较多,ANR 所在的文件目录在:\FS\data\anr
这里面就包含了每一次发生 ANR 时的日志信息,日期时间都能看到
找到对应时间点的 ANR 日志用 txt 打开
里面的信息非常多,使用 Ctrl + F 搜索主线程相关的信息: "main" prio=5 tid=1
这样子就能够快速定位到主线程相关的信息了
首先跟在主线程后面的是状态,常见的有这几种状态:
Native:正在执行 JNI 本地函数(主线程空闲时执行的 nativePollOnce 也属于此)
Runnable:线程正在 Java 虚拟机中执行(可能正在进行耗时操作)
Sleeping:线程正在睡眠(通常是调用了 Thread.sleep() )
Blocked:线程被阻塞(等待监视器锁的释放)
在我举的例子中,主线程就是处于 Sleeping 状态,我们就可以往下看日志信息找到调用的堆栈,从而定位到代码进行具体的优化
应用在线上的数据中里,ANR 并不完全由应用产生,也有可能由系统原因导致(比如内存不足、其他应用CPU抢占等),即便如此,我们还是需要尽量避免在应用层产生 ANR