-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathindex.html
359 lines (223 loc) · 19.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Android Training - 避免程序无响应ANR - 胡凯</title>
<meta name="author" content="HuKai">
<meta name="description" content="可能你写的代码在性能测试上表现良好,但是你的应用仍然有时候会反应迟缓(sluggish),停顿(hang)或者长时间卡死(frezze),或者应用处理输入的数据花费时间过长。对于你的应用来说最槽糕的事情是出现”程序无响应(Application Not Responding)&# …">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://hukai.me/android-training-performance-anr/">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<!--
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="/javascripts/ender.js"></script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
-->
<link href="/atom.xml" rel="alternate" title="胡凯" type="application/atom+xml">
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<!--Mark by @Kesen-->
<!-- <link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css"> -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-37679268-1', 'auto');
ga('send', 'pageview');
</script>
</head>
<body class="no-sidebar" >
<header role="banner"><hgroup>
<h1><a href="/">胡凯</a></h1>
</hgroup>
</header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
<form action="http://google.com/search" method="get">
<fieldset role="search">
<input type="hidden" name="q" value="site:hukai.me" />
<input class="search" type="text" name="q" results="0" placeholder="Search"/>
</fieldset>
</form>
<ul class="main-navigation">
<li><a href="/">首页</a></li>
<li><a href="/blog/archives">存档</a></li>
<li><a href="/android-training-course-in-chinese/index.html">Android官方培训课程</a></li>
<li><a href="/about/me.html">关于胡凯</a></li>
<li><a href="/about/friends.html">友情链接</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div>
<article class="hentry" role="article">
<header>
<h1 class="entry-title">Android Training - 避免程序无响应ANR</h1>
<p class="meta">
<time datetime="2014-07-06T12:09:00+08:00" pubdate data-updated="true">Jul 6<span>th</span>, 2014</time>
| <a href="#disqus_thread">Comments</a>
</p>
</header>
<div class="entry-content"><p>可能你写的代码在性能测试上表现良好,但是你的应用仍然有时候会反应迟缓(sluggish),停顿(hang)或者长时间卡死(frezze),或者应用处理输入的数据花费时间过长。对于你的应用来说最槽糕的事情是出现”程序无响应(Application Not Responding)” (ANR)的警示框。</p>
<p>在Android中,系统通过显示ANR警示框来保护程序的长时间无响应。对话框如下:</p>
<p><img src="/images/anr.png" alt="anr" /></p>
<p>此时,你的应用已经经历过一段时间的无法响应了,因此系统提供用户可以退出应用的选择。为你的程序提供良好的响应性是至关重要的,这样才能够避免系统为用户显示ANR的警示框。</p>
<p>这节课描述了Android系统是如何判断一个应用不可响应的。这节课还会提供程序编写的指导原则,确保你的程序保持响应性。</p>
<h2>是什么导致了ANR?(What Triggers ANR?)</h2>
<p>通常来说,系统会在程序无法响应用户的输入事件时显示ANR。例如,如果一个程序在UI线程执行I/O操作(通常是网络请求或者是文件读写),这样系统就无法处理用户的输入事件。或者是应用在UI线程花费了太多的时间用来建立一个复杂的在内存中的数据结构,又或者是在一个游戏程序的UI线程中执行了一个复杂耗时的计算移动的操作。确保那些计算操作高效是很重要的,不过即使是最高效的代码也是需要花时间执行的。</p>
<!-- More -->
<p><strong>对于你的应用中任何可能执行时间长的操作,你都不应该执行在UI线程</strong>。你可以创建一个工作线程,把那些操作都执行在工作线程中。这确保了UI线程(这个线程会负责处理UI事件) 能够顺利执行,也预防了系统因代码僵死而崩溃。因为UI线程是和类级别相关联的,你可以把相应性作为一个类级别(class-level)的问题(相比来说,代码性能则属于方法级别(method-level)的问题)</p>
<p>在Android中,程序的响应性是由Activity Manager与Window Manager系统服务来负责监控的。当系统监测到下面的条件之一时会显示ANR的对话框:</p>
<ul>
<li>对输入事件(例如硬件点击或者屏幕触摸事件),5秒内都无响应。</li>
<li>BroadReceiver不能够在10秒内结束接收到任务。</li>
</ul>
<h2>如何避免ANRs(How to Avoid ANRs)</h2>
<p>Android程序通常是执行在默认的UI线程(也可以成为main线程)中的。这意味着在UI线程中执行的任何长时间的操作都可能触发ANR,因为程序没有给自己处理输入事件或者broadcast事件的机会。</p>
<p>因此,任何执行在UI线程的方法都应该尽可能的简短快速。特别是,在activity的生命周期的关键方法<code>onCreate()</code>与<code>onResume()</code>方法中应该尽可能的做比较少的事情。类似网络或者DB操作等可能长时间执行的操作,或者是类似调整bitmap大小等需要长时间计算的操作,都应该执行在工作线程中。(在DB操作中,可以通过异步的网络请求)。</p>
<p>为了执行一个长时间的耗时操作而创建一个工作线程最方便高效的方式是使用<code>AsyncTask</code>。只需要继承AsyncTask并实现<code>doInBackground()</code>方法来执行任务即可。为了把任务执行的进度呈现给用户,你可以执行<code>publishProgress()</code>方法,这个方法会触发<code>onProgressUpdate()</code>的回调方法。在<code>onProgressUpdate()</code>的回调方法中(它执行在UI线程),你可以执行通知用户进度的操作,例如:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">private</span> <span class="kd">class</span> <span class="nc">DownloadFilesTask</span> <span class="kd">extends</span> <span class="n">AsyncTask</span><span class="o"><</span><span class="n">URL</span><span class="o">,</span> <span class="n">Integer</span><span class="o">,</span> <span class="n">Long</span><span class="o">></span> <span class="o">{</span>
</span><span class='line'> <span class="c1">// Do the long-running work in here</span>
</span><span class='line'> <span class="kd">protected</span> <span class="n">Long</span> <span class="nf">doInBackground</span><span class="o">(</span><span class="n">URL</span><span class="o">...</span> <span class="n">urls</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">urls</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
</span><span class='line'> <span class="kt">long</span> <span class="n">totalSize</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
</span><span class='line'> <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">count</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span><span class='line'> <span class="n">totalSize</span> <span class="o">+=</span> <span class="n">Downloader</span><span class="o">.</span><span class="na">downloadFile</span><span class="o">(</span><span class="n">urls</span><span class="o">[</span><span class="n">i</span><span class="o">]);</span>
</span><span class='line'> <span class="n">publishProgress</span><span class="o">((</span><span class="kt">int</span><span class="o">)</span> <span class="o">((</span><span class="n">i</span> <span class="o">/</span> <span class="o">(</span><span class="kt">float</span><span class="o">)</span> <span class="n">count</span><span class="o">)</span> <span class="o">*</span> <span class="mi">100</span><span class="o">));</span>
</span><span class='line'> <span class="c1">// Escape early if cancel() is called</span>
</span><span class='line'> <span class="k">if</span> <span class="o">(</span><span class="n">isCancelled</span><span class="o">())</span> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'> <span class="k">return</span> <span class="n">totalSize</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// This is called each time you call publishProgress()</span>
</span><span class='line'> <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onProgressUpdate</span><span class="o">(</span><span class="n">Integer</span><span class="o">...</span> <span class="n">progress</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="n">setProgressPercent</span><span class="o">(</span><span class="n">progress</span><span class="o">[</span><span class="mi">0</span><span class="o">]);</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// This is called when doInBackground() is finished</span>
</span><span class='line'> <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onPostExecute</span><span class="o">(</span><span class="n">Long</span> <span class="n">result</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="n">showNotification</span><span class="o">(</span><span class="s">"Downloaded "</span> <span class="o">+</span> <span class="n">result</span> <span class="o">+</span> <span class="s">" bytes"</span><span class="o">);</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>为了能够执行这个工作线程,只需要创建一个实例并执行<code>execute()</code>:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="k">new</span> <span class="nf">DownloadFilesTask</span><span class="o">().</span><span class="na">execute</span><span class="o">(</span><span class="n">url1</span><span class="o">,</span> <span class="n">url2</span><span class="o">,</span> <span class="n">url3</span><span class="o">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>相比起AsycnTask来说,创建自己的线程或者HandlerThread稍微复杂一点。如果你想这样做,<strong>你应该通过<code>Process.setThreadPriority()</code>并传递<code>THREAD_PRIORITY_BACKGROUND</code>来设置线程的优先级为”background”。</strong>如果你不通过这个方式来给线程设置一个低的优先级,那么这个线程仍然会使得你的应用显得卡顿,因为这个线程默认与UI线程有着同样的优先级。</p>
<p>如果你实现了Thread或者HandlerThread,请确保你的UI线程不会因为等待工作线程的某个任务而去执行Thread.wait()或者Thread.sleep()。UI线程不应该去等待工作线程完成某个任务,你的UI现场应该提供一个Handler给其他工作线程,这样工作线程能够通过这个Handler在任务结束的时候通知UI线程。使用这样的方式来设计你的应用程序可以使得你的程序UI线程保持响应性,以此来避免ANR。</p>
<p>BroadcastReceiver有特定执行时间的限制说明了broadcast receivers应该做的是:简短快速的任务,避免执行费时的操作,例如保存数据或者注册一个Notification。正如在UI线程中执行的方法一样,程序应该避免在broadcast receiver中执行费时的长任务。但不是采用通过工作线程来执行复杂的任务的方式,你的程序应该启动一个IntentService来响应intent broadcast的长时间任务。</p>
<blockquote><p><strong>Tip:</strong> 你可以使用StrictMode来帮助寻找因为不小心加入到UI线程的潜在的长时间执行的操作,例如网络或者DB相关的任务。</p></blockquote>
<h2>增加响应性(Reinforce Responsiveness)</h2>
<p>通常来说,100ms - 200ms是用户能够察觉到卡顿的上限。这样的话,下面有一些避免ANR的技巧:</p>
<ul>
<li>如果你的程序需要响应正在后台加载的任务,在你的UI中可以显示ProgressBar来显示进度。</li>
<li>对游戏程序,在工作线程执行计算的任务。</li>
<li>如果你的程序在启动阶段有一个耗时的初始化操作,可以考虑显示一个闪屏,要么尽快的显示主界面,然后马上显示一个加载的对话框,异步加载数据。无论哪种情况,你都应该显示一个进度信息,以免用户感觉程序有卡顿的情况。</li>
<li>使用性能测试工具,例如Systrace与Traceview来判断程序中影响响应性的瓶颈。</li>
</ul>
<hr />
<p><strong>学习自<a href="http://developer.android.com/training/perf-anr.html">http://developer.android.com/training/perf-anr.html</a>,欢迎交流讨论</strong></p>
</div>
<footer>
<p><img src="http://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" alt="" title="" type="image/png"><br>
<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享许可协议</a>:本站作品由<a href="http://hukai.me" target="_blank">HuKai</a>创作,采用<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议</a>进行许可。
<!-- <br>如果你觉得这篇文章对你有帮助,请点击下面的分享链接,你还可以选择扫描二维码进行打赏!</br> -->
<!-- <img src="/images/pay2me.jpg" alt=""/> -->
<p class="meta">
<span class="byline author vcard">Posted by <span class="fn">HuKai</span></span>
<time datetime="2014-07-06T12:09:00+08:00" pubdate data-updated="true">Jul 6<span>th</span>, 2014</time>
<span class="categories">
<a class='category' href='/blog/categories/android/'>Android</a>, <a class='category' href='/blog/categories/android-performance/'>Android:Performance</a>, <a class='category' href='/blog/categories/android-training/'>Android:Training</a>
</span>
</p>
<div class="sharing">
<!-- JiaThis Button BEGIN -->
<div class="jiathis_style_32x32">
<a class="jiathis_button_qzone"></a>
<a class="jiathis_button_tsina"></a>
<a class="jiathis_button_tqq"></a>
<a class="jiathis_button_weixin"></a>
<a class="jiathis_button_googleplus"></a>
<a class="jiathis_button_renren"></a>
<a class="jiathis_button_linkedin"></a>
<a class="jiathis_button_douban"></a>
<a href="http://www.jiathis.com/share?uid=1723296" class="jiathis jiathis_txt jtico jtico_jiathis" target="_blank"></a>
<a class="jiathis_counter_style"></a>
</div>
<script type="text/javascript">
var jiathis_config = {data_track_clickback:'true'};
</script>
<script type="text/javascript" src="http://v3.jiathis.com/code/jia.js?uid=1342501457879302" charset="utf-8"></script>
<!-- JiaThis Button END -->
</div>
<br></br>
<p class="meta">
<a class="basic-alignment left" href="/android-training-volley-custom-request/" title="Previous Post: Android Training - Volley(Lesson 4 - 实现自定义的请求)">« Android Training - Volley(Lesson 4 - 实现自定义的请求)</a>
<a class="basic-alignment right" href="/android-notes-add-copy-text-option-to-share-list/" title="Next Post: Android Notes - 添加Copy to Clipboard的选项到分享列表中">Android Notes - 添加Copy to Clipboard的选项到分享列表中 »</a>
</p>
</footer>
</article>
<section>
<h1>Comments</h1>
<div id="disqus_thread" aria-live="polite"><noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>
</section>
</div>
</div>
</div>
<footer role="contentinfo"><p>
Copyright © 2018 - HuKai -
<span class="credit">Powered by <a href="http://octopress.org">Octopress</a> - 本站作品采用 <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议</a>进行许可.</span>
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="/javascripts/ender.js"></script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
</p>
</footer>
<script type="text/javascript">
var disqus_shortname = 'kesenhoo';
// var disqus_developer = 1;
var disqus_identifier = 'http://hukai.me/android-training-performance-anr/';
var disqus_url = 'http://hukai.me/android-training-performance-anr/';
var disqus_script = 'embed.js';
(function () {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://' + disqus_shortname + '.disqus.com/' + disqus_script;
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
}());
</script>
</body>
</html>