`
hilary3113
  • 浏览: 262668 次
  • 性别: Icon_minigender_1
  • 来自: 邯郸
社区版块
存档分类
最新评论
阅读更多

1)是什么引发了ANR?
在Android系统上,应用的响应灵敏性由Activity Manager和Window Manager system services所监控,当它监测到如下的其中一个条件时,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的生命周期是短暂的)。对Intent broadcast作出响应,你的应用应该启动一个Service来执行长耗时的动作。同样,你也应该避免从Intent Receiver中启动Activity,因为它会产生一个新的屏,偷走任何用户正在运行的应用的焦点。对Intent broadcast作出的响应,假如你的应用需要向用户显示什么东西,应该用Notification Manager来完成。
增强响应灵敏性
通常,在一个应用中,100到200微秒是一个让用户感觉到阻滞的阈值,因此这里有些小技巧让你用来使你的应用看起来响应更灵敏。
如果你的应用正在后台对用户输入作出响应,显示正在进行的进度(ProgressBar和ProgressDialog对此很有用)。
特别是对于游戏,在子线程中做移动的计算。
如果你的应用有一个耗时的初始化过程,考虑用闪屏或尽可能快地渲染主界面并异步地填充信息。在这两种情况下你都应该表明进度正在进行,以免用户觉得你的应用被冻住了。
为无缝设计:
即使你的应用是快速且响应灵敏的,一些设计仍然能句对用户造成问题——因为与其它应用未计划的交互或者对话,意外的数据丢失,无意识的阻塞等等。为了避免这些问题,有助于理解你的应用运行的环境和可以影响你的应用的系统交互。总之,你应该倔尽全力地开发一个与系统和其它应用无缝交互的应用。
一个常见的无缝问题就是一个应用的后台进程(如service或broadcast receiver)对某事件作出响应而弹出对话框,这看起来仿佛并无大碍,特别是当你在模拟器上单独地构建和测试你的应用时。然而,当你的应用在真正的设备上运行,后台线程显示对话框时,你的应用当时可能没有获得用户焦点。这就会出现你的应用会在活动的应用后面显示对话框,或者从当前应用中获得焦点并显示对话框的情况,而管论当时用户正在做什么(如正在打电话等)。那样的行为可能对你的应用或用户不起作用。
为了避免这些问题,你的应用应该利用适当的系统资源——Notification类,来通知用户。利用通知,你的应用可以通过在状态条上显示一个图标来通知用户事件已经发生,而非获得焦点和打断用户。
另外一个无缝问题的例子就是,Activity由于未能正确实现onPause()及其它生命周期方法而无意中丢失了状态或用户数据。又或者,你的应用要暴露供其它应用使用的数据,你应该通过ContentProvider来实现,而非通过一个全世界都可读的原始文件或数据库。
这些例子的共同特点就是,它们都是关于如何跟系统和其它应用协作得更好,Android系统的设计就是将所有的应用看作是一个松散耦合的组件联邦,不是一堆墨盒代码。这就使你作为一个开发者可以将整个系统视为只是一个更大一点的组件联邦。这样有得于你将应用与其它应用清晰和无缝的集成,所以作为回报,你应该更好的设计你的代码。
这个文档讨论了常见的无缝集成问题和怎样去避免它们。它包含了如下主题:
别丢弃数据
一定要记住Android是一个移动平台。看起来很显然,但是记住这个事实很重要,就是任何Activity(如"正在打进来的电话"应用)在任何时候都有可能弹出来覆盖你的Activity,这会激活onSaveInstanceState()和onPause()方法,并导致你的应用被杀死。如果当其它Activity出现时,用户正好在你的应用上编辑数据,你的应用被杀死的同时那些数据也很可能会丢失。当然了,除非你先保存了进行中的工作。“Android方式”是这么做的:能接收和编辑用户输入的应用需要重写onSaveInstanceState()方法并以恰当的方式保存它们的状态,当用户重新访问应用时,就能重新获得数据。
一个运用这个行为经典的例子就是邮件应用,当用户正在撰写邮件时另一人Activity开始了,应用应该将正在编辑的邮件保存为草稿。
不要暴露原始数据
如果你不想穿着内衣在大街上散步,同样你的数据也不应如此。尽管可能暴露某些应用可以方便其它应用读取,但这通常不是最好的主意。暴露原始数据要求其它的应用能够理解你的数据格式;如果你改变了格式,你将会破坏其它没有同时更新的应用。
“Android方式”就是创建一个ContentProvider通过一个清晰的、深思熟虑的、可维护的API来暴露你的数据给其它应用。使用ContentProvider就像一个Java接口来分离和组件化两段紧密耦合的代码,这就意味着你能够修改你数据的内部格式而不用修改由ContentProvider暴露的接口,这样就不会影响其它应用。
别打断用户
如果能确定一个用户是带有目的性的运行一个应用才是安全的。那就是为什么你除非是直接响应当前活动的用户输入,不然就要避免产生Activity的原因。
那就是说,不要从后台运行的BroadcastReceiver和Service中调用startActivity()。如果这样做将会打断任何正在运行的应用,并使用户恼怒。甚至你的Activity可能成为一个“击键强盗”接收一些用户正在为上一个Activity提供的输入,视乎你的应用所做的,这是这可能是个坏消息。
取代直接从后台直接产生Activity UIs,你应该用NotificationManager来设置通知,这将会出现在状态条上,当用户空闲时可以点击它们来看你的应用向他们显示了什么。
(注意,当你的Activity已经在前台时所有这些都没适用:这时,对于输入的响应,用户期望看到你的下一个Activity。)
有太多事要做?在一个线程里做
如果你的应用需要做一个代价高昂或长耗时的计算,你可能要将它移到一个线程里。这个将会防止显示“Application Not Responding”对话框给用户,最终导致你的应用完全终止。
默认地,在一个Activity中的代码和其所有的View运行在同一个线程上。这与处理UI事件的线程是同一个。例如,当一个键被按下时,一个key-down事件被添加到Activity主线程的队列。事件处理系统需要很快地让这个事件出列并处理这个事件。不然,系统数秒后将会认为应用已经挂起并替用户杀死这个应用。
如果你有长耗时的代码,让它在你的Activity上内联运行将会在使它运行在事件处理线程上,这很大程度上阻塞了了事件处理句柄。这会延缓输入处理并导致ANR对话框。为了避免之,将你的计算移到一个线程中。在为响应灵敏性设计中已经讨论了如何做。
5)不要过载一个单一的Activity屏
任何值得使用的应用都可能会有几个不同的屏幕。当设计你的UI屏幕时,请一定要运用多个Activity对象实例。依赖于你的开发背景,你可能像解释某些类似Java Applet的东西一样来解释一个Activity,Activity是你应用的入口点。然而,那并不是准确:一个Applet的子类是一个Java Applet的单一入口点,而一个Activity应该被看作一个潜在的进入你的应用的多个入口点。在你的”main”Activity和任何其它你可能有的Activity之间的唯一不同就是,那“miain”Activity碰巧是那个唯一在你的AndroidManifest.xml文件中对“android.intent.action.MAIN”动作有兴趣的一个而已。
所以,当设计你的应用时,把你的应用看成一个Activity对象的联邦。从长远来看,这会使得你的代码更具可维护性。
6)扩展系统主题
当提到用户接口的观感时,协调是很重要的。用户为那些与他们所期望的用户接口相反的应用所震动。当设计你的UI时,你应当尽量避免出现太多你自己主题,相反地,用同一个主题。你可以重写或扩展那部分你必须的主题,但是至少你是基于与其他应用相同的UI基础上的。详细可以参阅“应用风格和主题”部分。
7)设计你的UI可以与多屏分辨率一起工作
不同的的基于Android的设备可能会支持不同的分辨率。甚至一些可能支持随时更改分辨率。保证你的布局和图片足够灵活对于在不同设备屏幕上正常显示是非常重要的。
幸运的是,这是很容易办到的。简单讲,你需要做的就是为你的关键分辨率提供不同版本的作品,然后设计你的布局适应各种不同的维度。(例如,避免作用硬编码位置而用相对布局。)如果那样做的话,剩下的系统会处理,你的应用在任何设备上看起来都很棒。
8)假定网络是很慢的
Android设备会有多种网络链接选项。所有的都会提供数据访问,虽然有一些会比另一些更快。其中速度最慢的就是GPRS(GSM网络的非3G数据服务)。即便具备3G能力的设备在非3G网络上也会花很多的时间,所以网速低将会是一个长期存在的事实。
那就是为什么你应该针对最小化的网络访问和带宽编写你的应用。你不能假设网络是快速的,所以你应该一直计划它是慢的。如果你的用户碰巧是在一个快速的网络上,那很好 ——他们的体验只会提升。你要避免相反的情况:应用有时可用,但有时慢得令人沮丧,得看用户是在哪在什么时间,这样的应用可能不会受欢迎。
别假定触摸屏和键盘
Android可能支持多种外观形状。那就是说一些Android设备将会有完整的“QWERTY"键盘,而其它的可能会有40键、12键或其它键盘设置。同样地,一些设备会有触摸屏,但很多会没有。
当构建你的应用时,一定要记住,不要假定特定的键盘布局——当然了,除非你真的喜欢限制你的应用以到它只能在某些设备上运行。
一定要节省设备电池
如果移动设备经常局限于屋内,那就不是很“移动”。移动设备是电池供电的,而我们如果能让电池每充一次的电量使用得更持久一些,每个人都会更开心——特别是用户。其中的两个用电大户就是处理器和音频,那就是为什么你写的应用应尽量做少的工作的同时尽可能频繁地使用网络的原因。
最小化你的应用使用的处理器时间就归结为书写高效的代码。从音频上最小化功耗,要确保优雅地处理错误条件,并仅获取你需要的东西。例如,如果连接网络失败不要一直重试连接网络,如果失败了一次,很可能是用户没有接收信号。如果你立即重试,那么你所做的一切只是在浪费电池能量。
用户是相当聪明的:如果你的程序是高耗电的,你可以相信他们会发觉的。在那一点上,唯一可以确定的是你的程序将不会保持安装非常久。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics