zoukankan      html  css  js  c++  java
  • 微信Android ANR原理解析及解决方案

    最近微信安卓版的“两位数字+15个中文字符句号”BUG把ANR带回了大家的视野。

    前情介绍-微信bug事件

    在微信上给安卓手机用户发送:

    “11。。。。。。。。。。。。。。。”

    (两位数字+15个中文字符句号)接收到这样的信息以后,部分安卓手机在发送或收到这条消息时微信会无响应,如下图。

    图片

    本文将从如下几个方面给大家介绍一下Android ANR相关的知识:

    ANR的原理

    如何定位分析ANR(以微信为例)

    如何解决&避免ANR

    ANR的原理

    什么是ANR

    ANR全称Application Not Responding,意为程序未响应。Android基于消息处理机制系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应就会造成ANR。发生ANR时的表现一般是弹出一个提示框,告知程序无响应,让用户选择等待还是强制退出。时的表现一般是弹出一个提示框,告知程序无响应,让用户选择等待还是强制退出。

    什么场景会发生ANR

    一般情况下,如下场景会导致ANR:

    Service Timeout:比如前台服务在20s内未执行完成

    BroadcastReceiver Timeout:比如前台广播在10s内未执行完成

    ContentProvider Timeout:内容提供者,在publish过超时10s

    InputDispatching Timeout: 输入事件分发超时5s,包括按键和触摸事件

    系统如何发现ANR

    Android基于消息处理机制在系统层实现了一套发现ANR的机制,其其核心原理是消息调度和超时处理。

    下面以Service为例简单介绍下ANR的检测流程。

    Service的ANR机制

    Service的ANR检测流程可以简单归纳为首先埋一个超时消息,然后操作在超时内完成之后移除超时消息,否则消息被系统收到触发ANR。

    Service的ANR消息埋点

    Service的启动的大致过程如下图: 

    图片

    在Service进程启动之后并attach到system_server进程时会进行ANR埋点,代码如下:

    图片

    该方法的主要工作就是发送delay消息(SERVICE_TIMEOUT_MSG),所以,如果我们不能在操作完成时remove该消息,则消息会被发送到MainHandler从而触发ANR。

    图片

    Service的ANR消息移除

    根据ANR对监测原理,既然埋下了超时消息,那必然也有对应对移除逻辑,对于Service,在Service启动完成时会对消息进行移除,代码如下:

    图片

    可以看出该方法的主要工作就是在service启动完成时则移除服务超时消息。

    触发ANR

    当然,如果没能及时remove消息,则会触发ANR,通过上述代码,我们可以发现在system_server进程中有一个Handler线程, 名叫”ActivityManager”,而ANR对消息也是向该Handler线程发送。该Handler对ANR对处理逻辑如下:

    图片

    图片

    总结

    通过如上分析,可以大致总结初Android对ANR对监测机制主要基于消息机制,通过预发送超时消息并及时移除来实现超时检测对功能。

     ANR的信息收集

    通过如上的ANR触发逻辑可以看到ANR触发时最终都会调用AMS.AppNotResponding()方法,下面从这个方法说起。

    图片

    图片

    当发生ANR时, 会按顺序依次执行:

    • 第一次更新CPU的统计信息。这是发生ANR时,第一次CPU使用信息的采样

    • 输出ANR Reason信息到EventLog, 也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信息

    • 填充firstPids和lastPids数组

    • 收集并输出重要进程列表中的各个线程的traces信息

    • 第二次更新CPU统计信息,跟第一次一样,两次采样的数据分别对应ANR发生前后的CPU使用情况

    • 将traces文件和CPU使用情况信息保存到dropbox,即data/system/dropbox目录

    • 根据进程类型,来决定直接后台杀掉,还是弹框告知用户

    可以看出,除了前端提示之外,ANR发生的时候会写各种类型的日志来方便后续排查问题,主要的日志包括如下:

    • event log,主要包括ANR的发生时间,类型等基本信息

    • main log,主要包括ANR发生时的详细信息,如CPU的使用情况等

    • dropbox日志,通过dumpsys dropbox找到,主要为ANR发生时的详细信息,trace、函数调用、CPU信息等

    • trace日志,最常用的ANR分析文件,主要包括CPU信息和各进程的函数调用栈信息

    如何定位和分析ANR的问题

    通过对ANR的原理了解,我们可以从多个角度对ANR进行问题分析,如通过分析trace日志,dropbox日志等。

    通过logcat分析

    简单的情景,我们可以直接在logcat中检索ANR in关键词即可找到对应的ANR原因、堆栈信息和CPU使用情况,格式大致如下:

    图片

    通过日志能看到ANR的原因(Input dispatching timed out),发生时的CPU负载等信息。

    分析traces文件

    实际情况中,这些信息相对还是偏少的,所以我们就需要通过分析trace文件或者dropbox文件来获取详细信息来定位问题。下面以微信为例:

    首先,我们拿到trace文件,一般情况下,trace文件在如下目录:

    图片

    但是实际中,部分机型会对trace有rename的过程,所以trace文件的存储可能如下: 

    图片

    如图,trace文件的格式为traces_[package]_[time].txt

     

    将trace文件pull到本地,打开后可以看到如下的内容: 

    图片

    有源码场景下问题定位

    一般情况下,如果是自有的App,我们直接通过分析堆栈信息,即可定位到具体的代码行然后可以直接通过源码调试解决问题。

    无源码场景下问题定位

    如果App是黑盒,比如微信这样,那我们就需要借助一些反编译的手段来进行定位。

    以微信的这个ANR为例,通过分析多个ANR的trace文件后,发现每次的堆栈其实都有细微的不一样,上面的这个堆栈信息里面显示的错误可能是由于正则匹配,但是还有如下的情况: 

    图片

    通过这个堆栈信息可以看出是由于获取textWidth导致的问题,所以可以通过找到相同的父调用链来分析问题:

    图片

    所以我们基本可以定位到问题是在celltextview到这几个方法调用里面。

    然后,我们就可以开始进行反编译定位问题了,一般我们有两种方案:

    1. 通过动态调试来找到问题。首先,我们需要co.debuggable = 1的设备(root或者模拟器均可),然后先通过baksmali将微信apk反编译成smali文件,然后通过在android studio安装SmaliIDEA调试插件,导入反编译好的smali项目,即可开始调试(具体操作可以自行查阅)。

    2. 直接反编译代码,然后人肉定位问题。首先,我们需要先将apk解压,取出dex文件,借助dex2jar将dex文件转换成jar文件,然后借助jd-gui来查看源码,然后人肉定位问题。

    通过如上的两个方案,可以帮助我们在黑盒的情况下定位到具体问题。

     

    如何避免ANR

    ANR本质是一个性能问题,要求主线程在超时内完成操作,所以想要避免ANR,根本的就是需要杜绝主线程中的耗时操作。 


    所以我们可以使用一些异步的方式来进行耗时操作,常用的有AsynkTask、Thread等,然后通过Handler来处理结果,只在主程序进行耗时较少的更新操作。

    日行一善, 日写一撰
  • 相关阅读:
    SpringDataRedis 常用命令
    Java 连接 Redis
    Java 循环标记
    初学Docker
    线程池,进程和线程的理解
    Linux-定时器任务
    Linux 命令2
    Linux命令
    Java基础整理
    微服务简介
  • 原文地址:https://www.cnblogs.com/xiyuan2016/p/14296651.html
Copyright © 2011-2022 走看看