android应用的响应性设计
源地址:${android-sdk-windows}/docs/guide/practices/design/responsiveness.html
你写的代码也许能够通过世界上所有的性能测试,但是当用户使用你的这个应用的时候却可能会发狂。因为,你的应用程序的用户响应性太差了,比如用起来不够流畅,在关键的时刻挂起或者失去响应,或者花费了太多的时间用来处理用户的输入。
在android系统中,当应用程序在一段时间内没有响应的时候,系统会检测到此种情况,并且向用户显示一个对话框,这个对话框被称为“ANR”对话框(Application Not Responding)。这个时候用户可以选择继续等待程序运行,也可以选择强制关闭此应用。但是,如果每次使用这个应用都要看到这个对话框并且选择操作,显然用户不会很爽。所以,在你的设计中,添加对应用响应性的考虑,以避免系统弹出这个ANR对话框,就显得非常重要。
通常情况下,如果应用不能够响应用户的输入,系统就会显示这个ANR对话框。比如,如果一个应用因为IO操作而阻塞(通常是访问网络),那么应用的主线程就不能处理用户输入的事件。一段时间后,系统推测出该应用已经“僵死”,那么就会显示这个ANR对话框来给用户关闭这个应用的选择。
类似的,如果你的应用花费大量的时间在内存中构建一个复杂的数据结构,或者可能是计算游戏中的下一个移动点,系统此时就会认为你的程序已经挂起。这种情况下,首先要确认你的算法的性能足够好,但是即使的最高效率的代码,运行起来也是需要时间的。
针对上面提到的两种情况,强烈推荐的方法是建立一个子线程,在子线程中完成这些耗时的操作。这样的话,由于用户界面的事件循环是在主线程中运行的,故可以保持主线程一直在运行而避免系统认为你的程序已经"僵死"。由于这些线程一般情况下是通过类来完成的,因此,可以将应用的响应问题认为是类级别的问题,与此对应的是,性能问题一般是方法级别的。
本文描述了android系统是如何判断一个应用无响应的,并且给出了提高应用响应性的指南。
What Triggers ANR?(什么触发了ANR?)
在android中,系统是通过 Activity Manager 和 Window Manager这两个系统级别的服务来监控应用的响应性的。当系统检测的某个应用出现下面某种情况时,就会显示这个ANR对话框:
1. 对用户输入事件(比如按键,触摸屏幕)在5秒之内没有响应;
2. 一个BroadcastReceiver在10秒内没有完成处理。
How to Avoid ANR(如何避免ANR)
上面给出了ANR的定义,现在让我们看一下为什么android应用会出现ANR以及如何在你的程序中避免ANR。
android应用一般是整体的在一个单线程(主线程)中运行,这意味着你应用中任何在主线程中执行的需要耗费很长时间的东西都会导致ANR,因为你的应用没有机会处理用户输入事件或者广播消息。
因此,在你的程序主线程中运行的每一个方法都应该做尽可能少的工作。特别的,Activities在主要的生命周期方法比如OnCreate()或者OnResume()中也应该做尽量少的工作。可能会长时间运行的操作比如网络或者数据库操作,或者耗费大量计算的操作比如重新计算bitmap的大小等,这些尽量在子线程中完成(或者,对于数据库操作,可以考虑使用异步请求)。但这并不意味着在子线程完成运行之前你的主线程应该阻塞或者调用Thread.wait() or Thread.sleep()。主线程应该提供一个Handler来供子线程运行完毕时回传信息,而不是阻塞自己来等待子线程完成。采用上述方法来设计你的应用可以保证你的应用的响应性,从而避免因为5秒用户事件没响应超时而导致的ANR对话框。对于显示界面的其他线程也应该遵从上述实践,因为它们同样也受这个超时规则约束。
你可以使用android.os.StrictMode这个工具来帮助找到你程序中不小心引入的潜在耗时的操作比如数据库和网络操作。具体使用方法可以参见android.os.StrictMode这个类的使用说明。
IntentReceiver执行时间的特殊约束说明它们应该做的一些在后台运行的,小的,离散的工作,比如保存配置信息,注册一个Notification等。所以,和在主线程中调用的方法一样,应用应该避免在BroadcastReceivers中执行一些潜在的耗时的操作/计算。但是,当要响应一个耗时的广播消息(broadcast)时,你的应用应该启动一个Service,而不是在子线程中完成这些大量的任务,因为BroadcastReceiver的声明周期很短。
顺便说一句,你应该避免在一个Intent Receiver中启动一个Activity,因为这样的话会产生一个新的屏幕从而让用户当前运行的程序失去焦点。如果你的应用在响应一个Intent broadcast时需要显示一些信息给用户,你可以考虑使用消息管理器(Notification Manager)。
Reinforcing Responsiveness(加强响应性)
一般情况下,100-200毫秒是用户能够感觉到该应用有延迟的阈值。基于此,这里是一些能够使你的应用避免ANR并且看起来响应性还不错的小窍门:
-->如果你的应用在后台处理以响应用户输入,向用户显示当前运行的进度,可以考虑使用ProgressBar或者ProgressDialog。
-->特别的针对游戏,在子线程中计算位置移动;
-->如果你的应用有一个耗时的初始化步骤,那么考虑使用一个启动画面,或者先快速的显示出主视图,然后异步加载数据信息。无论哪种情况,你都应该暗示用户应用程序正在操作,避免使用户认为程序已经僵死。