Keeping Your App Responsive中文翻译
来源:互联网 发布:macbook必备软件 知乎 编辑:程序博客网 时间:2024/04/28 08:55
平方X翻译说明:
仅供参考,对正确性概不负责,翻译不对的欢迎指正,勿喷。
译于20160802,原文《Keeping Your App Responsive》
Develop>Training>Best Practices for Performance
关键字:
安卓ANR
保持应用响应
或许可以写出胜任所有性能测试的代码,但有的时候仍可能会感觉迟钝、挂起或冻结,有关应用的响应,可能发生最差情况就是“应用未响应”(ANR)对话框。
在安卓中,如果你的应用在一段时间内没有响应,系统守护就会显示一个写着应用停止响应的对话框,如图1.这时,因为你的应用长时间没有响应,所以系统给用户提供一个退出应用的选择。对应用进行严格的响应设计,这样系统就永远不会给用户显示ANR对话框。
本文介绍了安卓系统如何确定应用没有响应,并且为确保应用保持响应提供指南。
什么原因引发ANR?
一般情况下,如果一个应用无法响应用户的输入,系统就会显示ANR。比如,如果一个应用在UI线程阻塞了一些IO操作(通常是网络访问)使得系统不能处理传入的用户事件。或者是应用在UI线程中花费了太多的时间来创建一个复杂的内存结构,或是在游戏花费太多时间计算下一步的行动。确保计算总是高效的一直很重要,但即使是高效的代码,也需要时间来运行。
如果你的应用可能要进行潜在的漫长的操作,就不应该在UI线程中执行,而应该创建一个工作线程,在工作线程中执行大部分的工作。这样就能保持UI线程(它驱动了用户交互事件的循环)的运行并且防止因为代码卡住而被系统结束。因为这样的线程通常是在类级别完成的,你可以把响应当做一个类级别的问题。(和基本代码的性能比较,它是方法级别考虑的)
在安卓中,应用的响应由系统服务Activity Manager和Window Manager监控。如果一个应用被检测到下列情况之一,就会显示ANR对话框。
- 一个输入事件(比如按键或触屏事件)在5秒内没有被响应(译注1)
- 一个广播接收器在10秒内没有结束运行(译注1)
译注1:
对于这一个5s和10s进行了实测,发现是不正确的。
写一个死循环。
long i=0L; while (true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Log.d("test","i="+ (++i)); }
1,首先要一个输入事件。
如果只是死循环,没有用户输入是不会诱发ANR的。
2,输入事件不一定能传进去
可能因为死循环在卡死,也可能因为我的Thread.sleep,反正有时候点击屏幕不一定能传入事件,要多点几下。
3,5秒钟有记录,几秒延时后显示ANR对话框
实测的时候,当一个事件输入后,5秒后Log显示如下
I/art: Thread[2,tid=9093,WaitingInMainSignalCatcherLoop,Thread*=0x7f87d0d000,peer=0x12ced0a0,"Signal Catcher"]: reacting to signal 3I/art: Wrote stack traces to '/data/anr/traces.txt'
模拟器延时1-3秒左右出现ANR,可能是我电脑卡了。
MIUI延时8-10秒,可能是故意设置的。
在测试广播接收器的时候,发现要比Activity的延时短1、2秒。
Service也一样
包括start和bind
广播接收器也一样
必须要有用户输入,且要等5秒再加延时,而不是上面说的10秒。
如何避免ANR
通常地,安卓应用都在一个单线程中运行,默认的“UI线程”或者“主线程”。这就意味着,应用做的任何需要很长时间才能完成的操作,都会诱发ANR,因为你的应用没有给它机会处理用户输入事件和intent广播。
因此,在UI线程中运行的任何方法都应该在线程中做尽可能少的工作。特别地,在Activity的关键生命周期的方法中(比如onCreate和onResume),要尽可能少地做设置工作。潜在的长时间的操作(比如网络或数据库操作)或大量的计算(比如调整bitmap的大小)都应该在一个工作线作中完成(或者在数据库操作时通过异步请求)。
创建一个工作线程来完成耗时任务,最有效的方法是使用AsyncTask类。简单的继承AsyncTask并且实现doInBackground()来执行工作。要发布进度给用户,可以调用publishProgress()方法,它将执行onProgressUpdate()回调方法。你可以从实现的onProgressUpdate()方法(它运行在UI线程上)中通知用户。比如:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { // Do the long-running work in here protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } // This is called each time you call publishProgress() protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } // This is called when doInBackground() is finished protected void onPostExecute(Long result) { showNotification("Downloaded " + result + " bytes"); }}
要执行这个工作线程,只需要创建一个实例,并调用execute():
new DownloadFilesTask().execute(url1, url2, url3);
如果不使用AsyncTask可能会很复杂,但你可能要自已创建线程或HandlerThread。如果你要这样做,你应该通过调用Process.setThreadPriority() 方法并且传递THREAD_PRIORITY_BACKGROUND来把线程的优先级设为“后台”。如果你不通过这程方式来给线程设置较低的优先级,线程仍然会拖慢你的应用,因为它默认和UI线程有相同的优先级。
如果你使用了Thread或者HandlerThread,确保你的UI线程不会阻塞在等工作线程完成——不要调用Thread.wait()或者Thread.sleep()。取而代之的,你的主线程应该为其他线程提供一个Handler,来认它们在完成时发回信息。这样设计的应用允许UI线程保持对输入的响应,从而避免5秒输入事件超时的ANR。
关于广播接收器的执行时间,要特别强调的是,广播接收器是为了在后台做一些小的、离散量的工作,比如保存设置或者注册通知。因此,作为在UI线程被调用的方法,应用应该避免在广播接收器中做潜在的耗时操作或计算。如果你的应用在响应一个intent广播时要进行潜在的长时间的操作,取代通过工作线程做密集型任务的做法是,让应用启动一个IntentService。
提示:你可以通过使用StrictMode来找出可能偶然在主线程做的长时间操作,比如网络或数据库操作。
加强响应
一般情况下,用户在应用中可以觉查出缓慢的上限是100至200毫秒。因此,下面是一些关于避免ANR和让应用似乎对用户响应的额外的提示:
- 如果你的应用在后台工作以响应用户输入,显示出当前的进展(比如UI中的一个进度条)。
- 特别对于游戏来说,在工作线程中进行移动的计算。
- 如果你的应用在启动时有耗时初始化设置过程,考虑显示一个启动画面,或者先尽可能快地渲染出主视图,显示正在加载并且异步地填满信息。在这两种情况下,都应该以某种方式指明当前的进展,以使用户知道应用是否卡住了。
- 使用性能工具(比如Systrace 和Traceview)来确定应用响应能力的瓶颈。
- Keeping Your App Responsive中文翻译
- Keeping Your App Responsive
- Android - 《Keeping Your App Responsive》
- Android如何避免ANR 增加相应速度 Keeping Your App Responsive
- <等待翻译>Android Wear 进阶 2.3 Keeping Your App Visible 保持你的应用可见
- Android Develop Training中文翻译02 《Building Your First App》
- Android Develop Training中文翻译04《Running Your App》
- Keeping Your Informix rootdbs Lean and Clean
- Webpack your bags(中文翻译)
- asterisk app命令中文翻译
- asterisk app命令中文翻译
- keeping background service alive after user exit app
- App Store 审核指南中文翻译
- Your First iOS App
- Building your First App
- Debug Your App
- Internationalize Your App
- Running Your App (android)
- PhpStorm中快捷键总结,附详细使用说明
- Latex--论文编辑软件使用心得
- Linux——Centos下搭建MySQL环境
- LightOJ1094Farthest Nodes in a Tree(BFS+树的直径)
- 关于VC操作Office屏蔽掉“服务器正在运行中”要选择“切换到...”或"重试"的对话框
- Keeping Your App Responsive中文翻译
- 讨论SQLite数据库损坏与修复
- Android LruCache 实现原理解析
- ruby
- iOS js oc相互调用(JavaScriptCore)
- Swift中一切皆为对象
- tableView实现添加多个图片
- 递归算法获取截取的所有需要内容
- 从框架看PHP的五种境界及各自的薪资待遇