关于ANR的官方建议

本文讨论了Android应用中Application Not Responding (ANR)的问题,解释了导致ANR的原因及如何通过优化主线程处理和合理使用子线程来避免ANR的发生。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有可能写出的代码赢得了每一次性能测试,但是却仍然使得当用户尝试使用应用时,使他们处于怒火之中。这是因为应用的响应不够迅速 - 用户可能会觉的应用程序运行缓慢,应用程序被挂起,或因冻结很长时间而无反应,或者处理输入时间过长。

在Android中,系统会监视那些反应不够迅速的应用。如果系统发现某个应用程序反应太慢,它会显示一个叫做Application Not Responding (ANR)的对话框给用户,如Figure 1所示。用户可以选择等待该应用程序让它继续,也可以选择强行结束该应用程序。虽然用户可以选择等待该应用程序让它继续,但是用户其实是非常不喜欢出现该对话框(ANR提示框)

因此设计具有较好响应性的应用程序是非常重要的,这样系统才不会给用户显示一个ANR对话框。

一般来说,如果一个应用不能快速响应用户输入,则系统显示一个ANR。例如:如果一个应用程序的主线程因为在进行一些I/O操作而被阻塞了(经常是一个网络访问),那么该应用程序的主线程此时就没有能力处理用户的输入事件。一段时间后,系统推断出这个应用冻结了,然后显示一个ANR给用户一个选择杀死此应用或等待该应用程序操作完成后继续。

相似的,如果你的应用程序的主线程消耗太多时间来建立一个复杂的内存中的结构,或者计算游戏中的下一步,系统会断定你的应用已经挂起。总是保证使用上面的技巧这些计算是有效率的是很重要的,但是即使是最有效的代码也需要时间来运行。

这两种情况中,推荐的方法是建立一个子线程来做你在那里的大部分工作。这样保持主线程运行并且阻止系统认定你的代码已经冻结。因为这样的线程经常在类级别中完成,你可以把响应性作为一个类的问题考虑。


What Triggers ANR?


在Android中,应用响应度是由Activity Manager和Window Manager系统服务监视的。Android当发现到下面其中一个条件发生的时候,会特定的应用显示ANR对话框。

  • No response to an input event (e.g. key press, screen touch) within 5 seconds.一个输入事件5秒内没有反馈。 但是并不意味着花4秒时间来处理输入事件是安全的。相反对于这种主线程的回调函数,应该让其执行时间尽量的短,以避免主线程,以便主线程能处理一些更重要更紧急的事务。
  • BroadcastReceiver hasn't finished executing within 10 seconds In Android, application responsiveness is monitored by the Activity .一个BroadcastReceiver在10秒内没有执行完毕。但是并不意味着在BroadcastReceiver的OnReceiver中花9秒时间来处理事务是安全的。相反对于这种主线程的回调函数,应该让其执行时间尽量的短,以避免主线程,以便主线程能处理一些更重要更紧急的事务。

How to Avoid ANR


通过上面的叙述,我们已经了解了什么是ANR的定义,现在让我们再次认识为什么Android应用中会ANR中发生,并且如何才能设计更好的架构来避免ANR的发生。


Android应用通常整个都运行在一个线程中(主线程)。这意味着,你应用中做的任何事情都在主线程,如果耗时太多就可能触发ANR对话框,因为主线程来不及处理用户输出或者Intent广播及其他回调函数。

因此任何在主线程中调用的函数都应该做尽量少的工作。尤其是四大组件的生命周期函数,比如Activity的生命周期函数onCreate() 和onResume().对于需要长时间运行的操作,如网络或者数据库操作,或者高代价的计算,或者调整bitmap大小,应该放在一个子线程中来执行。然而,这不意味着你的主线程应该在等待子线程完成时而阻塞,你的主线程应该为子线程提供一个 Handler ,当完成时把结果返回给主线程 - 而不该调用 Thread.wait() 或 hread.sleep()。按照这种思路来设计你的Android应用程序,你的应用程序就能对输入事件进行很好的反应,以避免因为无法在5秒内对输入事件进行响应而产生的ANR对话框。其实对于任何用于显示UI的线程都应该遵循以上原则,因为它们都面临着对输入事件不能及时响应的问题。关于Handler的更多内容请阅读《Android线程模型》和《关于Handler技术》和《Looper和Handler

You can use StrictMode to help find potentially long running operations such as network or database operations that you might accidentally be doing your main thread.

你可以使用 StrictMode 来帮助你在主线程中查找潜在的耗时操作,比如对网络或数据库操作。

线程对IntentReceiver执行时间上的明确的约束,主要是为了强调它是用于执行非常少量的工作 - 只是用于分离一些在后台工作,比如保存一个设置或者注册一个Notification。因此和其他在主线程中调用的其他方法一样,在BroadcastReceivers内应该避免的长时间操作或计算。对于耗时的工作,因为BroadcastReceivers的生命周期是非常短暂的,因此你也不应该在其中开启一个子线程来做该工作,而是应该启动一个 Service,再在这个Service中创建一个子线程来做该工作。另外,你也应该避免从一个Intent过滤器启动一个activity,因为它将产生一个新屏幕,从用户当前运行的应用中偷走焦点。如果你的应用中有一些内容作为Intent广播的反馈需要显示给用户,它应该使用Notification Manager来完成。

Reinforcing Responsiveness


一般来说,在应用中,100200毫秒是用户能不能察觉延迟的临界值。(每个人反应速度不一样,所以会有100ms-200ms这个范围)这有一些额外的窍门是你为了避免ANR而应该做的,会帮助你的应用对用户来说看起来是很灵敏的。

  • If your application is doing work in the background in response to user input, show that progress is being made (ProgressBar and ProgressDialog are useful for this).如果你的应用正在后台工作来响应用户输入,那么给用户显示你的执行进度是个不错的选择(ProgressBar和ProgressDialog在这点上很有用)。
  • .对于游戏来说, 移动的运算应该在子线程中来进行。
  • .如果你的应用在初始化阶段较耗时,考虑显示一个splash屏或者尽快让主视图快速显示处理,然后才显示其他的视图。不管是哪一种情况,你应该设法表明程序正在往前执行,以免用户觉得应用冻结了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值