(自己翻译的,转载请注明,谢谢。缺图、排版问题以及无效链接,我会慢慢修正,之前请和AndroidSDK文档对照来看)
就算你写的代码能够通过世界上所有的性能测试,你的应用仍然有可能使用户陷入狂暴状态。缺乏响应性 — 反应慢、某些情况会卡、处理输入的时间非常长 — 的应用就能做到这一点。
在Android中,系统通过弹出一个“应用无响应(ANR)”对话框给用户,来对抗一段时间没有相应的应用。用户可以在这个对话框中,选择继续你的应用,但是用户不会喜欢在用你的应用的时候总是看到这个对话框。所以,在你的应用中设计响应性是很重要的,系统就不会弹出ANR给用户。
一般来说,当应用对用户输入没有相应的时候,系统弹出ANR。例如,如果一个应用阻塞在某些输入输出操作(例如频繁地网络请求),应用的主线程就不会继续响应用户的输入事件。过了一段时间后,系统会认为这个应用已经废了,于是就弹出一个ANR来让用户选择是否强制关闭应用。
同样地,如果你的应用花了太多的时间在构建精巧的数据结构或者计算游戏的下一步上面,系统就会以为你的应用废了。利用下面要说的技术使你的计算更高效是非常重要的,但是就算是最高效的代码,仍然会消耗时间。
在这两种情况下,建立一个子线程来完成工作是常用的修复手段。这样,主线程(响应UI事件的循环)就会一直运行,系统就不会认为你的代码死了。一般来说,线程是属于类级别,所以,你可以认为响应性是一个类级别的问题。(跟基本行为比较,基本行为,如下描述,是方法级别的内容。)
本文档讨论了Android系统是如何判断一个应用是否无响应,以及提供保证代码响应性的指导。
本文档包含了一下内容:
- ANR是什么触发的?
- 如何避免ANR?
- 增强响应性。
ANR是什么触发的?
在Android中,应用的响应性是被ActivityManager和WindowManager两个系统服务监视的。对于一个应用,在以下两种情况,Android会弹出ANR:
- 输入事件(按键、点击屏幕之类)之后5秒没有相应。
- 一个
BroadcastReceiver
执行超过了10秒。
如何避免ANR
通过上面给出的ANR的定义,让我们看看为什么Android应用会无响应,以及如何使你的应用避免这个。
一般来说,Android应用会整个运行在一个线程(主线程)里。这意味着,在主线程,任何需要很长时间完成的动作,由于导致了你的应用没机会处理输入事件或者广播的Intent,都会触发ANR对话框。
因此,任何在主线程工作的方法,都应该只做最少的事情。 Activity的关键生命周期方法,例如onCreate()
和onResume()
里,更要做尽可能少的事。潜在的耗时运算,例如网络或数据库操作,或者进行类似缩放位图这样的大量的数学运算,都应该在子线程做。(对于数据库操作,可以通过一个异步方法,而不必放进另一个线程)。这并不意味着你的主线程应该阻塞住等着子线程,无论是通过Thread.wait()
还是Thread.sleep()
。你的主线程应该提供一个Handler
来给子线程结束后返回结果。如此设计你的应用,可以让你的主线程对输入保持小于5秒的响应速度,从而避免ANR对话框。如果其它的线程涉及展示UI,应该遵循同样的实践。
对IntentReceiver的执行时间显示,暗示了它应该做的事情,是后台小规模的工作,类似保存设置或者注册Notification一类。所以,跟在主线程的方法一样,应用应该避免在BroadcastReceiver中进行潜在的耗时操作或运算。除了在子线程中处理大量密集任务(因为BroadcastReceiver生命周期是很短的)。当一个潜在的耗时操作需要返回一个广播Intent时,你的应用应该启动一个 Service
。额外提醒,你应该避免从一个IntentReceiver里启动Activity,这将会跳出一个新的界面,并把用户正在做的工作打断。如果你的应用收到广播Intent之后需要展示给用户什么的话,它应该使用 Notification Manager
。
增强响应性
一般来说,100到200毫秒是用户感到“卡”的门槛。下面是避免ANR以及加快你的应用响应额外的建议。
- 如果你的应用需要等着后台工作的结果,展示出它的进度。 (可以使用
ProgressBar
或ProgressDialog
) - 对于游戏,大量的计算应该放到子线程。
- 如果你的应用初始化耗时很长,考虑使用一个SplashScreen或者尽快进入主界面然后再异步地慢慢填充。 在这两种情况,你应该提供给用户一个进度条之类的东西,表明你的应用还没死。