ANR(Application Not Response)是安卓开发团队经常遇到的无响应问题,但却很难定位和根除。尤其是线上问题,由于难以复现,导致开发者难以有效地快速解决。为此,本⽂将为大家分享作者是如何在⼀个⽉内降低 50% 的 ANR 线上问题发⽣率的探索与实践,希望能对开发者有所帮助或启发。
Google 的一项内部研究表明,过高的崩溃与 ANR 发生率会直接影响应用的评分情况,并且很难在商店中累积起用户量,严重影响应用在商店的排名情况。这一系列的连锁反应将会给应用带来很大的损失,且有可能失去在应用商店获得谷歌推荐的资格。因此,ANR 问题对于⼤多数安卓团队来说十分棘手,尤其是线上问题令人头疼。因为本地问题可以复现,线上 ANR 却很难。因而探究线上 ANR 问题的治理⽅案更具意义。

这些问题经过简单分析后,得出了如下结论:
nativePollOnce 问题,暂时不能依赖现有的⽇志信息得出结论。不过其占⽐较⼤,要放在⾼优先级处理。processPendingWork 问题已经有可靠的处理⽅案。占⽐不低,应该放在较⾼的优先级处理。
剩余问题数量相近,所以处理顺序并不固定。
线上上报的堆栈如上图所示。棘手之处在于:如果只看上报的堆栈和错误⽇志,很难排查出问题的根本原因。上⾯提到过,处理这类问题要“⼤胆假设”,其可能的原因有:
默认的异常处理机制会在线程发⽣ Crash 时同步给 ActivityThread、ActivityManagerService 之后再“kill”掉⾃身。
那么如果当主线程发⽣异常的情况下,不使⽤系统的处理链路或异常处理过程中耗时过久就会发⽣ ANR。当主线程发⽣了崩溃后其实已处于终⽌状态。此时主线程 Looper 的 MessageQueue 组件⽆法继续添加新的消息,⽽ Android 应⽤的运⾏恰恰依赖的就是主线程的消息轮询 -- 线上这个错误堆栈也是指向了 MessageQueue 组件在等待新消息的到来。因此,当主线程发⽣异常并⽆法及时 kill 掉进程时,系统就 会触发 ANR 超时机制。按照以上的逻辑推断,我们通过埋点找出了线上异常处理链的各个⽅法耗时数据:
通过对⽐各个异常处理⽅法的耗时可以发现,Firebase 异常分析 SDK 在异常处理链路中耗时最⻓。预计移除此组件可降低该问题的发⽣数量。
最终在去除 Firebase 异常分析 SDK 的版本上线之后,线上数据显示整体的 ANR 率都有所下降。
接下来分析另⼀种情况:
监控逻辑的关键代码:
在后期,我们通过这种⽅式获得了以下慢函数数据:
整理后得到的分布数据:
不难得出:【QueuedWork.processPendingWork】、【⾸⻚创建】类型的问题占⽐最⼤,必须优先处理。下⾯将详细分析这两个问题。
回到上⾯错误处理的思路中:
此问题的处理中,使⽤了通⽤的⽆源问题处理⽅案。通过 【假设 -> 验证 -> 上线 ->数据变化 】这⼀流程,最终减少了此问题的发⽣或转换成了有源问题。当然,这个问题的诱因不只以上两个猜想,还有更多的可能需要进⼀步探索。
对 AndroidFrameWork 源码接触较少的同学们,可能并不了解这个类的作⽤。这时候可以借助 Android 源码搜索引擎来⼀看究竟:
通过对源码的分析,可以得出⼀个⼤致流程:
SharedPreferencesImpl.apply() ⽅法中调⽤ QueuedWork.add() 将 SharedPreferences 的写 ⼊任务添加进 QueuedWork 的任务队列中,之后 ActivityThread 在⼀些组件⽣命周期⽅法中执⾏了 QueuedWork.waitToFinish → QueuedWork.processPendingWork 这⼀流程。这⼏个⽣命周期方法有:
要完成上⾯的功能,使⽤ MtAjx 只需要编写⼀些简单的规则即可实现:
接下来可采⽤⾃动化测试来模拟线上⽤户的真实操作,并通过 SharedPreferencesWrapper 的⽇志对SharedPreferences 写⼊频率进⾏分析。
最终输出的数据如下:
⽬前收集的线索可以推断出: GMS 组件的某个操作,⼤量地调⽤了 SharedPreferences 的 apply ⽅法。这个操作可能会使 SharedPreferences 的异步写⼊任务创建过多从⽽导致 ANR。后期处理此问题时,我们对这个组件进⾏了改造:使⽤ MtAjx 在 GMS 中拦截 SharedPreferences 的创建、获取操作, 并返回⼀个安全的 SharedPreferences 实现。⽽然上线之后,得到的数据跟预估的有些差距:
⾃身问题减少的⽐例很⼩
线上整体 ANR 波动不⼤
猜测这个问题发⽣时系统的 IO 负载已⼗分严重,处理部分场景可能收益并不⼤。于是之后上线了全量的 SharedPreferences 替换来避免此问题发⽣。
请注意,这⾥的“全量”并⾮真正的全量替换,⽽是排除了⼀些可能会受到影响的调⽤。⼤部分都只涉及 业务,这⾥不作为核⼼展开讨论;使⽤“安全的” SharedPreferences 实现只是规避问题,其根本还 是需要降低系统负载。
处理全量 SharedPreferences 替换的处理的⽅法与⽇志输出的流程类似。也采⽤了 MtAjx ⽅案来拦 截 SharedPreferences 的创建,并返回⼀个安全的实现。
此处为了容灾,线上做了在线开关作为全量替换的整体控制策略来规避未知⻛险。
最终,这个策略上线之后,上述问题整体降低了 60%-70%,详见下图:

所有的卡顿点都落在 Runtime.loadLibrary0() 这个调⽤上。
线上 ANR 或者慢函数的数据表现也近乎⼀致:都是⾼通机型、低端机较多。
经过分析得知,⾼通机型存在⼀个 BoostFramework 组件⽤于加快应⽤的响应速度:类似的机制 可以在 特定情况下提升调度优先级、CPU 频率,从⽽加快应⽤的响应速度。
但在某些情况下,这种机制会导致应⽤发⽣卡顿:BoostFramework 初始化依赖⼀个 so 的加载。⽽Runtime.loadlibrary0 是⼀个 synchronize 修饰的函数,多线程调⽤必然会存在锁竞争情况。
美图秀秀在启动过程中,存在着⼤量“异步”加载 so 的操作。如果⼦线程先于主线程进⼊ Runtime.loadLibrary0 ⽅法,拿不到锁的主线程就会等待⼦线程释放锁之后再继续执⾏。也就是说,如果某个⼦线程中存在着耗时较久的 so 加载⾏为,就会阻塞主线程的 so 加载。
⽬前 so 的加载时间还是盲区,⽆法针对性地去处理这个问题。与之前 SharedPreferences 问题处理的思路⼀样:使⽤ MtAjx 拦截 System.loadLibrary() ⽅法并输出其耗时,最后得到如下数据(部分):
从数据上看,某些 so 加载确实消耗了不少时间。要解决此问题,⽬前初步的⽅案有:
尽量降低异步 so 加载对主线程 so 加载产⽣的影响
尝试 Hack ⾼通平台的 BoostFramework,让其延迟加载或提前加载
列举出可以执⾏的⽅案如下:
最终上线的⽅案:
经过分析、查阅资料后,初步得出结论。当出现此类问题时,系统或应⽤基本都处于以下这些状态:
仍然采⽤⾃动化测试来模拟线上⽤户的使⽤情况并输出⼀份⽇志,分析后得到如下数据(部分):
数据反映出了⼀系列问题:
以上这些问题经过处理,整体 ANR 问题的发⽣都有⼀定程度地降低。所以,⼤部分 ANR 问题并⾮单体问题,通常降低整个应⽤的负载亦可降低整体 ANR 问题的发⽣率。
发现、预警,到 Bug 跟进,可以将整个开发和交付流程串联起来。这将是未来性能优化⼯作的⼀⼤利器。


我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie
当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?
我正在使用Postgres.app在OSX(10.8.3)上。我已经修改了我的PATH,以便应用程序的bin文件夹位于所有其他文件夹之前。Rammy:~phrogz$whichpg_config/Applications/Postgres.app/Contents/MacOS/bin/pg_config我已经安装了rvm并且可以毫无错误地安装pggem,但是当我需要它时我得到一个错误:Rammy:~phrogz$gem-v1.8.25Rammy:~phrogz$geminstallpgFetching:pg-0.15.1.gem(100%)Buildingnativeextension
我的测试尝试访问网页并验证页面上是否存在某些元素。例如,它访问http://foo.com/homepage.html并检查Logo图像,然后访问http://bar.com/store/blah.html并检查页面上是否出现了某些文本。我的目标是访问经过Kerberos身份验证的网页。我发现Kerberos代码如下:主文件uri=URI.parse(Capybara.app_host)kerberos=Kerberos.new(uri.host)@kerberos_token=kerberos.encoded_tokenkerberos.rb文件classKerberosdefini
我想在Rails中使用插件系统创建一个应用程序。潜在用户应该能够上传(或更好地从存储库安装)一个插件并安装它,使我的应用程序能够做更多的事情。这应该在没有FTP/SSH/对服务器的任何低级别访问的情况下完成。关于如何在Rails3中完成它,是否有任何好的gems或教程? 最佳答案 你看过http://edgeguides.rubyonrails.org/plugins.html了吗??它似乎不是100%兼容Rails3,但它可以帮助您入门。我看过的大多数插件文章都涉及Rails2。 关于
我有一个成功运行多个进程的Procfile设置:#/Procfileredis:bundleexecredis-serversidekiq:bundleexecsidekiq-v-C./config.ymlforward:forward4567mock-api我需要再添加一个进程-一个位于我机器上不同目录中的Sinatra应用程序。如果我cd到该目录,我可以从终端启动它:$rackup-p4567我可以使用终端从不同的目录启动它:$sh-c'cd/Path/to/project/&&execrackup-p4567'但是我应该如何使用工头来做到这一点。我尝试添加以下内容,但它无声地失败
一、离线方式1.1.下载ip2region.xdbGitHub项目地址:https://github.com/lionsoul2014/ip2region我们首先需要下载一个ip2region.xdb的文件下载地址:https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb打开后点击如图的Download图标即可下载。下载完成后,需要将该文件放到我们的项目中。ps:我是直接放到服务器的,因为放在项目的资源文件夹下,当我们调试的时候使用JavaSpring自带的工具去获取该文件的绝对路径时,没有任何问题,能够正
我正在使用命令行程序,它的工作原理如下:$ROUTE_TO_FOLDER/app如果“longtext”是使用“app”需要的参数编写的,那么它将用结果填充一个文本文件。如果没有,它将连续用点填充文本文件(为了避免这种情况,我无法处理或修改“app”的代码)。在ruby脚本中有这样一行:text="longtextthatwillbeusedbyapp"output=system("ROUTE_TO_FOLDER/app现在,如果文本写得好,就不会有问题,我会得到一个输出文件,如前所述。当文本写得不好时,问题就来了。接下来发生的是我的ruby脚本挂起,我不确定如何终止它。我找到
我有这段代码:puts"Start"loopdoThread.startdoputs"Hellofromthread"exitendtext=getsputs"#{text}"endputs"Done"我希望看到“Start”后跟“Hellofromthread”,然后我可以输入会得到回显的输入。相反,我得到“Start”和“Hellofromthread”,然后程序退出。来自关于exit的文档:Terminatesthrandschedulesanotherthreadtoberun.Ifthisthreadisalreadymarkedtobekilled,exitreturnst
RubyCompass不工作,代码如下,我已经在网上尝试了10-20种方法,有什么建议吗?在屏幕截图中,您会找到一种更简单的方法来读取我的gem的终端转储和错误,如果您想从那里获取一些东西,您会在屏幕截图下方找到文本谢谢,干杯,罗伯特RubyGemsisasophisticatedpackagemanagerforRuby.Thisisabasichelpmessagecontainingpointerstomoreinformation.Usage:gem-h/--helpgem-v/--versiongemcommand[arguments...][options...]Examp