Android最佳实践 为响应灵敏性设计

代码可能通过各种性能测试,但是当用户使用时还是会需要漫长的等待,这些就是那种响应不够灵敏的应用——它们反应迟钝,挂起或冻住周期很长,或者要花很长时间来处理输入。

在Android上,系统通过向用户显示一个称为应用无响应(ANR:ApplicationNotResponding)的对话框来防止在一段时间内响应不够快。用户可以选择让应用继续,但是用户并不会想要每次都来处理这个对话框。因此应把你的应用设计得响应灵敏,使得系统不必显示ANR给用户。

通常地,当不能响应用户输入时系统显示一个ANR。例如,如果一个应用在IO操作(经常是网络访问)上阻塞了,那么主应用线程就会无法处理正在进行的用户输入事件。经过一段时间,系统认为应用已经挂起,向用户显示一个ANR,让用户可以选择关闭。

相同地,如果你的应用花太多的时间在构建详细的内存结构上,又或者在计算游戏的下一个动作上,系统会认为你的应用已经挂起。用上面的技术来保证这些计算是高效的一直都是很重要的,但是即使是最高效的代码运行也是需要花费时间的。

在这两种情况下,解决的办法通常就是创建一个子线程,在这个子线程上做你的大部分工作。这样让你的主线程(驱动用户接口事件循环)保持运行,并让你的代码免于被系统认为已经冻住。因为这样的线程化通常都是在类级别上来完成的,所以你可以认为响应性能问题是一个类问题(与上面描述的方法级别的性能问题)。

这个文档讨论了Android系统是如何决定一个应用没有响应的,并提供了指引来保障你的应用是响应灵敏的。

1)是什么引发了ANR?

在Android系统上,应用的响应灵敏性由ActivityManager和WindowManagersystemservices所监控,当它监测到如下的其中一个条件时,Android就会为特定的应用显示一个ANR:

5秒内对输入事件无响应。

一个BroadCastReceiver在10秒内没有执行完毕。

怎样避免ANR?

考虑到上面对ANR的定义,让我们来研究一下这是为什么会发生以及怎样最好的组织你的应用以避免ANR。

Android应用正常是运行在一个单独的(如main)线程中的,这就意味着在你应用主线程中正在做的需要花很长时间来完成的事情都能够激活ANR对话框。因为你的应用并没有给自己一个机会来处理输入事件或Intent广播。

因此任何运行在主线程中的方法应该做尽可能少的事情。特别地Activitiy在关键生命周期方法中如onCreate()和onResume()应当做尽可能少的设置。潜在地的耗时长的操作(如网络或数据库操作,或高耗费数学计算如改变位图大小)应该在子线程里面完成(或以数据库操作为例,可以通过异步请求)。尽管如此,这并不是说当等待子线程完成的过程中你的主线程必须被阻塞——你不必调用Thread.wait()或Thread.sleep(),恰恰相反,你的主线程应该为子线程提供一个Handler,以便子线程完成时可以提交回给主线程。以这种方式来设计你的应用,将会允许你的主线程一直可以响应输入,以避免由5秒钟的输入事件超时导致的ANR对话。这些做法同样应该被其它任何显示UI的线程所效仿,因为它们属于同样类型的超时。

IntentReciever执行时间的特定限制限制了它们应该做什么:在后台执行的一些琐碎的工作如保存设置或注册通知。至于其它在主线程里被调用的方法,在BroadcastReceiver中,应用应该避免潜在的长耗时操作或计算,而是应该用子线程来完成密集任务(因为BroadcastReceiver的生命周期是短暂的)。对Intentbroadcast作出响应,你的应用应该启动一个Service来执行长耗时的动作。同样,你也应该避免从IntentReceiver中启动Activity,因为它会产生一个新的屏,偷走任何用户正在运行的应用的焦点。对Intentbroadcast作出的响应,假如你的应用需要向用户显示什么东西,应该用Notification Manager来完成。

增强响应灵敏性

通常,在一个应用中,100到200微秒是一个让用户感觉到阻滞的阈值,因此这里有些小技巧让你用来使你的应用看起来响应更灵敏。

如果你的应用正在后台对用户输入作出响应,显示正在进行的进度(ProgressBar和ProgressDialog对此很有用)。

特别是对于游戏,在子线程中做移动的计算。

如果你的应用有一个耗时的初始化过程,考虑用闪屏或尽可能快地渲染主界面并异步地填充信息。在这两种情况下你都应该表明进度正在进行,以免用户觉得你的应用被冻住了。

相关推荐