0. 前言
本文是官方对于ANR的一些解释和避免手段,本文是译文,有删减。官方的原文链接在这里。
1. ANR起因
In Android, the system guards againstapplications that are insufficiently responsive for a period of time bydisplaying a dialog to the user, called the Application Not Responding (ANR)dialog, shown at right in Figure 1. The user can choose to let the applicationcontinue, but the user won't appreciate having to act on this dialog every timehe or she uses your application. It's critical to design responsiveness intoyour application, so that the system never has cause to display an ANR dialogto the user.
在Android中,系统会监视那些反应不够迅速的应用。如果系统发现某个应用程序反应太慢,它会显示一个叫做Application Not Responding (ANR)的对话框给用户,如上图所示。用户可以选择等待该应用程序让它继续,也可以选择强行结束该应用程序。虽然用户可以选择等待该应用程序让它继续,但是用户其实是非常不喜欢这种情况的发生。因此设计具有较好响应性的应用程序是非常重要的。
Generally, the system displays an ANR if anapplication cannot respond to user input. For example, if an application blockson some I/O operation (frequently a network access), then the main applicationthread won't be able to process incoming user input events. After a time, thesystem concludes that the application is frozen, and displays the ANR to givethe user the option to kill it.
一般来说,如果一个应用不能快速响应用户输入,则系统显示一个ANR。例如:如果一个应用程序的主线程因为在进行一些I/O操作而被阻塞了(经常是一个网络访问),那么该应用程序的主线程此时就没有能力处理用户的输入事件。一段时间后,系统推断出这个应用冻结了,然后显示一个ANR给用户一个选择杀死此应用或等待该应用程序操作完成后继续。
Similarly, if your application spends too muchtime building an elaborate in-memory structure, or perhaps computing the nextmove in a game, the system will conclude that your application has hung. It'salways important to make sure these computations are efficient using thetechniques above, but even the most efficient code still takes time to run.
相似的,如果你的应用程序的主线程消耗太多时间来建立一个复杂的内存中的结构,或者计算游戏中的下一步,系统会断定你的应用已经挂起,因此保证这些计算的高效率是很重要的,但是即使是最有效的代码也是需要时间来运行的。
In Android, application responsiveness ismonitored by the Activity Manager and Window Manager system services. Androidwill display the ANR dialog for a particular application when it detects one ofthe following conditions:
No response to an input event (e.g. key press,screen touch) within 5 seconds. A BroadcastReceiver hasn't finished executingwithin 10 seconds In Android.
在Android中,应用响应度是由Activity Manager和Window Manager系统服务监视的。Android当发现到下面其中一个条件发生的时候,会特定的应用显示ANR对话框。一个输入事件5秒内没有反馈;一个BroadcastReceiver在10秒内没有执行完毕。
2. 避免ANR的一些建议
Android applications normally run entirely on asingle (i.e. main) thread. This means that anything your application is doingin the main thread that takes a long time to complete can trigger the ANRdialog because your application is not giving itself a chance to handle theinput event or Intent broadcast.
Android应用通常整个都运行在一个主线程中。这意味着,你的应用中做的任何事情都在主线程,如果耗时太多就可能触发ANR对话框,因为主线程来不及处理用户输出或者Intent广播及其他回调函数。
Therefore any method that runs in the mainthread should do as little work as possible. In particular, Activities shoulddo as little as possible to set up in key life-cycle methods such asonCreate()and onResume(). Potentially long running operations such as network or databaseoperations, or computationally expensive calculations such as resizing bitmapsshould be done in a child thread (or in the case of databases operations, viaan asynchronous request). However, this does not mean that your main threadshould block while waiting for the child thread to complete — nor should youcall Thread.wait() or Thread.sleep().
因此任何在主线程中调用的函数都应该做尽量少的工作。尤其是四大组件的生命周期函数,比如Activity的生命周期函数 onCreate() 和onResume()。对于需要长时间运行的操作,如网络或者数据库操作,或者高代价的计算,或者调整bitmap大小,应该放在一个子线程中来执行。然而,这不意味着你的主线程应该在等待子线程完成时而阻塞,你的主线程应该为子线程提供一个 Handler,当完成时把结果返回给主线程,而不该调用 Thread.wait() 或 Thread.sleep()。
You can use StrictMode to help find potentiallylong running operations such as network or database operations that you mightaccidentally be doing your main thread.
你可以使用StrictMode 来帮助你在主线程中查找潜在的耗时操作,比如对网络或数据库操作。
Applications should avoid potentiallylong-running operations or calculations in BroadcastReceivers. But instead ofdoing intensive tasks via child threads (as the life of a BroadcastReceiver isshort), your application should start a Service if a potentially long runningaction needs to be taken in response to an Intent broadcast. As a side note,you should also avoid starting an Activity from an Intent Receiver, as it willspawn a new screen that will steal focus from whatever application the user iscurrently has running. If your application has something to show the user inresponse to an Intent broadcast, it should do so using the NotificationManager.
在BroadcastReceivers内应该避免的长时间操作或计算。对于耗时的工作,由于BroadcastReceivers的生命周期是非常短暂的,因此你也不应该在其中开启一个子线程来做该工作,而是应该启动一个 Service,再在这个Service中创建一个子线程来做该工作。另外,你也应该避免从一个Intent过滤器启动一个activity,因为它将产生一个新屏幕,从用户当前运行的应用中偷走焦点。如果你的应用中有一些内容作为Intent广播的反馈需要显示给用户,它应该使用Notification Manager来完成。
If your application is doing work in thebackground in response to user input, show that progress is being made(ProgressBar and ProgressDialog are useful for this).
如果你的应用正在后台工作来响应用户输入,那么给用户显示你的执行进度是个不错的选择(ProgressBar和ProgressDialog在这点上很有用)。
If your application has a time-consuminginitial setup phase, consider showing a splash screen or rendering the mainview as quickly as possible and filling in the information asynchronously. Ineither case, you should indicate somehow that progress is being made, lest theuser perceive that the application is frozen.
如果你的应用在初始化阶段较耗时,考虑显示一个splash屏或者尽快让主视图快速显示处理,然后才显示其他的视图。不管是哪一种情况,你应该设法表明程序正在往前执行,以免用户觉得应用冻结了,造成不好的用户体验。