转载请注明出处:https://blog.csdn.net/weixin_42072033/article/details/109622923

大家好,今天给大家带来Android系统ANR错误实战分析。在开始之前请允许我说几句不相干的话。

我叫麻锦荣,你们叫我锦荣就好了,在深圳从接触Android到现在已经有7个年头了,从最早的ADT(那会儿还没有AS)开发app,到后来定制手机ROM,再到智能家居,智能硬件,车载产品,电视机,商显等Android开发工作,一路走来深感Android的发展迅速,对于Android程序员的要求也从APP开发提高到系统Framework层,机器学习,机器视觉等更广的领域。一路上,我碰到过无数的难题,相信以后也一样会碰到,但是互联网的知识共享给了我力量,成为了我前进道路上的好帮手。但是我却以工作太忙或者其他的借口,其实就是自己的懒散为由从来没有把自己开发中的心得分享给大家,共同学习,共同进步。这是我第一篇博客,很多表达或许词不达意,但是我相信万事开头难,以后一定会渐入佳境。

好了,废话就说这么多,下面开始进入今天的主题吧,相信看完这篇文章的你对ANR问题的解决又会更加充满信心。

Android开发中经常碰到一个叫做ANR(Application Not Responding)的问题,如果这个apk是你们自己开发的,报ANR,有经验的程序员自己肯定会较为容易的检查出代码哪里出了问题。好,那么问题来了,如果是原厂(MTK,全志,RK,amlogic等)系统报出ANR,我们怎么解决?下面就用我公司实际碰到的问题为例,为大家实战讲解。

相信大家都知道有个东西叫小部件Widget,我们手机上的时钟小部件可以选择大小,开机的时候加载到我们的桌面上显示时间。前几天公司发现一个bug,直接上图。

如图,时钟Widget一直显示正在加载,几分钟后依然不显示时间,抓取log,发现ANR错误,如下:

使用的板子和源码均为amlogic平台,Android版本为9.0 。  由于是编译完直接烧录,可以100%确定是由源码引起ANR,从log信息看,似乎是com.android.phone 这个应用包下的VvmSimStateTracker这个类引起的,那是这样么?我们找到这个类

可以看出,VvmSimStateTracker就是一个广播接收器,就是用来监听一些sim卡热插拔的工具类,从刚才的log中看,是由于开机广播的处理出现了问题导致ANR,好,那么我们把这个android.intent.action.BOOT_COMPLETED给它注释掉,编译,看下结果。结果发现竟然正常了,时钟部件开机5秒内正常显示时间。好多人有疑问了,为什么只是简单接收了一个开机广播,就报ANR了呢?我们又不是第一天接收它了,留个疑问在这里。

Android中,主线程(UI线程)如果在规定时内没有处理完相应工作,就会出现ANR。具体来说,ANR会在以下几种情况中出现:

  1. 输入事件(按键和触摸事件)5s内没被处理: Input event dispatching timed out
  2. BroadcastReceiver的事件(onRecieve方法)在规定时间内没处理完(前台广播为10s,后台广播为60s):Timeout of broadcast BroadcastRecord
  3. service 前台20s后台200s未完成启动 Timeout executing service
  4. ContentProvider的publish在10s内没进行完:timeout publishing content providers

看完这个很多人就会恍然大悟,第二条这个报的错和上面日志的那个不是一样么

那这下原因找到了,就是因为UI线程阻塞导致了VvmSimStateTracker类没有在规定时间内处理完BroadcastReceiver事件。^_^ 那问题又来了,UI线程为什么会阻塞?谁把它搞阻塞了?这里我就要告诉大家怎么分析解决系统ANR。

首先,adb shell 进入 data/system 目录下会看到一个叫做dropbox 的文件夹(adb操作我就不多赘述了,可以查看其他博客)

只要你的Android系统出现ANR或者Crash等,系统就会保存日志到这个文件夹中,对你分析问题的产生有巨大帮助。话不多说,我们把这个dropbox拷出来,看看里面的内容

好家伙,东西还挺多,可以看到里面有很多的日志,那么我们所需要分析ANR的日志正是 system_app_anr@*******.txt.gz,不同平台的命名规则可能大同小异,总之大家看anr这几个关键字就可以了。解压,打开看看。

前面几行我们刚才见过了,下面红色标注的这里就是系统在发生ANR时CPU的实时使用情况

可以看到,我们的CPU在发生ANR 的时候总共才使用了12%左右的性能。这里要注意了,

1.如果发生ANR的进程CPU占用较高,如到了80%或90%以上,则可以怀疑是应用内一些代码不合理消耗掉了CPU资源,比如死循环或者一些算法库进行大量高精度复杂运算导致CPU长期占用较高,这就要结合trace和ANR前后的log进一步分析了。

2.如果某些进程的CPU占用百分比较高,几乎占用了所有CPU资源,而发生ANR的进程CPU占用为0%或非常低,则认为CPU资源被占用,进程没有被分配足够的资源,从而发生了ANR。这种情况多数可以认为是系统状态的问题,并不是由本应用造成的。

3.如果CPU总用量不高,那么很有可能是一些耗时操作或者锁的问题使主线程被阻塞 ,导致ANR。

4.如果iowait 占用率过高,很可能是系统等待I/O耗时操作,导致ANR。

这里我们明显看到CPU使用情况在各项指标中都表现正常,那么我们怀疑是第三条,也就是一些耗时操作或者锁的问题使主线程被阻塞 ,既然怀疑是主线程阻塞,那么我们往下看,找到主线程的相关日志。

"main" prio=5 tid=1 Native| group="main" sCount=1 dsCount=0 flags=1 obj=0x75429ee0 self=0xaea49000| sysTid=4078 nice=0 cgrp=default sched=0/0 handle=0xb2d34494| state=S schedstat=( 168928298 466851782 531 ) utm=6 stm=10 core=0 HZ=100| stack=0xbb365000-0xbb367000 stackSize=8MB| held mutexes=native: #00 pc 00019d58  /system/lib/libc.so (syscall+32)native: #01 pc 0001d215  /system/lib/libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+88)native: #02 pc 000633b1  /system/lib/libc.so (pthread_cond_timedwait+84)native: #03 pc 0004a005  /system/lib/libc++.so (_ZNSt3__118condition_variable15__do_timed_waitERNS_11unique_lockINS_5mutexEEENS_6chrono10time_pointINS5_12system_clockENS5_8durationIxNS_5ratioILx1ELx1000000000EEEEEEE+124)native: #04 pc 0001ebf1  /system/lib/libhidltransport.so (android::hardware::details::Waiter::wait()+224)native: #05 pc 0001f31f  /system/lib/libhidltransport.so (android::hardware::details::getRawServiceInternal(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, bool, bool)+826)native: #06 pc 000b07c5  /system/lib/libandroid_runtime.so (JHwBinder_native_getService(_JNIEnv*, _jclass*, _jstring*, _jstring*, unsigned char)+168)at android.os.HwBinder.getService(Native method)at android.hardware.radio.V1_0.IRadio.getService(IRadio.java:40)at com.android.internal.telephony.RIL.getRadioProxy(RIL.java:369)at com.android.internal.telephony.RIL.getHardwareConfig(RIL.java:3367)at com.android.internal.telephony.TelephonyDevController.registerRIL(TelephonyDevController.java:112)at com.android.internal.telephony.RIL.<init>(RIL.java:484)at com.android.internal.telephony.PhoneFactory.makeDefaultPhone(PhoneFactory.java:172)- locked <0x051877b9> (a java.lang.Object)at com.android.internal.telephony.PhoneFactory.makeDefaultPhones(PhoneFactory.java:99)at com.android.phone.PhoneGlobals.onCreate(PhoneGlobals.java:286)at com.android.phone.PhoneApp.onCreate(PhoneApp.java:41)at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1154)at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5871)at android.app.ActivityThread.access$1100(ActivityThread.java:199)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)at android.os.Handler.dispatchMessage(Handler.java:106)at android.os.Looper.loop(Looper.java:193)at android.app.ActivityThread.main(ActivityThread.java:6669)at java.lang.reflect.Method.invoke(Native method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

"main" 这个标志就代表主线程的相关日志,我们看到在 PhoneFactory 类中 makeDefaultPhone方法是有Object对象锁的。不急,我们找到日志中显示的几个类看看,

PhoneApp类 onCreate 时会调用 PhoneGlobals的 onCreate

public class PhoneApp extends Application {PhoneGlobals mPhoneGlobals;TelephonyGlobals mTelephonyGlobals;public PhoneApp() {}@Overridepublic void onCreate() {if (UserHandle.myUserId() == 0) {// We are running as the primary user, so should bring up the// global phone state.mPhoneGlobals = new PhoneGlobals(this);mPhoneGlobals.onCreate();mTelephonyGlobals = new TelephonyGlobals(this);mTelephonyGlobals.onCreate();}}
}

PhoneGlobals类 onCreate时会调用 PhoneFactory类的 makeDefaultPhones

public void onCreate() {if (VDBG) Log.v(LOG_TAG, "onCreate()...");ContentResolver resolver = getContentResolver();// Cache the "voice capable" flag.// This flag currently comes from a resource (which is// overrideable on a per-product basis):sVoiceCapable =getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);// ...but this might eventually become a PackageManager "system// feature" instead, in which case we'd do something like:// sVoiceCapable =//   getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);if (mCM == null) {// Initialize the telephony frameworkPhoneFactory.makeDefaultPhones(this);// Start TelephonyDebugService After the default phone is created.Intent intent = new Intent(this, TelephonyDebugService.class);startService(intent);mCM = CallManager.getInstance();for (Phone phone : PhoneFactory.getPhones()) {mCM.registerPhone(phone);}

而PhoneFactory类的 makeDefaultPhones方法是有对象锁的

那么问题迎刃而解,就是由于开机启动的时候,PhoneApp类调用PhoneFactory类的makeDefaultPhones 方法,而makeDefaultPhones还是被对象锁住的状态,导致主线程阻塞,进而导致VvmSimStateTracker类没有在规定时间内处理完开机广播,发生ANR。那么我们试试解决这个问题,把PhoneAPP初始化操作放在一个子线程中去进行。

@Overridepublic void onCreate() {if (UserHandle.myUserId() == 0) {// We are running as the primary user, so should bring up the// global phone state./* mPhoneGlobals = new PhoneGlobals(this);mPhoneGlobals.onCreate();mTelephonyGlobals = new TelephonyGlobals(this);mTelephonyGlobals.onCreate(); */new Thread(new Runnable() {@Overridepublic void run() {Looper.prepare();mPhoneGlobals = new PhoneGlobals(PhoneApp.this);mPhoneGlobals.onCreate();mTelephonyGlobals = new TelephonyGlobals(PhoneApp.this);mTelephonyGlobals.onCreate(); Looper.loop();}}).start();}}

编译下,试试效果

开机5秒内时钟部件正常显示了时间,没有ANR错误报出,问题解决。(这里篇幅过大就不做动图了,后面的博客我尽量把动态图加进去,使大家阅读体验更加好)

至此,我的第一篇博客就结束了,后续会给大家分享一些APK开发或者系统Framework层的一些问题,而且也会把代码或者项目开源,放在GitHub上与大家一起分享,共同进步,希望我的文章对你们有所帮助。

Android系统ANR错误实战分析相关推荐

  1. 基于Android系统的IPv6网络接入分析

                                                                      基于Android系统的IPv6网络接入分析 摘 要:本文深入分析了 ...

  2. Android系统的JNI原理分析(二)- 数据类型转换和方法签名

    声明 前阶段在项目中使用了Android的JNI技术,在此文中做些技术知识总结. 本文参考了一些书籍的若干章节,比如<Android进阶解密-第9章-JNI原理>.<深入理解Andr ...

  3. 基于android系统DVR稳定性问题分析及对策

    基于android系统DVR稳定性问题分析及对策 DVR,全名Digital Video Recorder,数字视频录像机,在车载行业大家通俗的叫行车记录仪,因为这个数字视频的内容是行车的形式动态.如 ...

  4. 由Debuggerd导致的Android系统死机问题分析

    1. 问题现象 问题发生的Android系统版本是7.0(Nougat): 屏幕没有任何刷新,输入事件无任何响应,即我们平时说的死机(冻屏): watchdog没有重启system_server: 问 ...

  5. Android系统问题及日志分析

    这篇文章全是干货,我们一起聊聊安卓系统稳定性问题.部分性能问题.本篇列举了作者在某厂工作中遇到实际问题,大部分只有日志概率性问题,通过日志分析问题. 自己对这半年工作做个笔记,也希望对大家有用.方便你 ...

  6. Android系统root破解原理分析

    上一篇文章 Android adb 源码分析 理论基础 root破解过程的终极目标是替换掉系统中的su程序.但是要想替换掉系统中su程序本身就是需要root权限的,怎样在root破解过程中获得root ...

  7. Android系统的HAL层分析 -- Sensors

    Android系统HAL层分析 -- Sensors 0 前言 1 HAL层Sensors代码分析/注释 0 前言 1 HAL层Sensors代码分析/注释

  8. Android系统原理性问题分析 - RefBase、sp、wp 分析

    声明 在Android系统中经常会遇到一些系统原理性的问题,在此专栏中集中来讨论下. 接触Android系统,遇到很多sp.wp相关问题,此篇分析Android系统内的智能指针问题. 此篇参考一些博客 ...

  9. grub2下常见系统初始化错误的分析和解决

    原文连接http://tieba.baidu.com/p/2910461207 grub rescue> grub> (initramfs) 是开机错误时,常见到的状况. 我将分以下几个部 ...

  10. Android 系统应用开发实战

最新文章

  1. salesforce开发入门1
  2. java编程button_以编程方式在Java Swing中单击GUIbutton
  3. 【C语言位运算的应用】如何按bit位翻转一个无符号整型
  4. 前缀和优化+计蒜客 泡咖啡
  5. mysql 存储引擎_MySQL存储引擎
  6. 组装一台微型计算机需要哪些部件6,微型计算机的组成与配置(6页)-原创力文档...
  7. 程序员过关斩将——搞定秒杀,只需要这几步!!
  8. mysql archive分区_MYSQL-分区表
  9. smarty3.1.30 模板引擎的使用
  10. 职场中,什么样的人最容易升职?
  11. Android项目接入魔窗SDK自定义使用
  12. CSDN数据库泄露!那些网站够安全吗?
  13. OP27运放在同相比例放大器中的应用
  14. js将base64图片处理成背景透明png
  15. 《自然语言处理实战入门》第三章 :中文分词原理及相关组件简介---- 汉语分词领域主要分词算法、组件、服务(下)
  16. YOLOv5报错:OSError: cannot open resource
  17. 第07章 图形操作 · 7.1 GDI原理(3)
  18. 烟草生产,条码在过程中到底起了什么作用?
  19. 甲骨文华育兴业-青柠成长计划
  20. Linux——如何安装WPS

热门文章

  1. 使用Typora添加数学公式
  2. android graphics,Android graphics值Bitmap
  3. php 中 normdist,说明 Excel 中的 NORMDIST 函数
  4. 计算机word公式平均数,平均值word公式怎么用?
  5. 网络攻防“三剑客”正式加盟墨者安全 担任首席安全顾问
  6. 项目质量管理的三个重要流程
  7. Ubuntu 命令行 安装 Operator Mono 字体
  8. 华为AR路由器配置导出
  9. WebScoket 实例 简单的网页聊天室
  10. C语言执行时进行窗口隐藏