-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathatom.xml
More file actions
1482 lines (1095 loc) · 144 KB
/
atom.xml
File metadata and controls
1482 lines (1095 loc) · 144 KB
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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Stay foolish, stay hungry.]]></title>
<link href="http://jeffreylyg.github.io/atom.xml" rel="self"/>
<link href="http://jeffreylyg.github.io/"/>
<updated>2014-12-28T00:45:18+08:00</updated>
<id>http://jeffreylyg.github.io/</id>
<author>
<name><![CDATA[Jeffrey Lee]]></name>
<email><![CDATA[jeffreylyg@gmail.com]]></email>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[IntelliJ IDEA导入Android源码工程(AOSP)]]></title>
<link href="http://jeffreylyg.github.io/blog/2014/12/27/intellij-ideadao-ru-androidyuan-ma-gong-cheng-aosp/"/>
<updated>2014-12-27T21:16:15+08:00</updated>
<id>http://jeffreylyg.github.io/blog/2014/12/27/intellij-ideadao-ru-androidyuan-ma-gong-cheng-aosp</id>
<content type="html"><![CDATA[<p>这篇Blog主要介绍一下在IntelliJ IDEA中如何导入整个Android源码工程。</p>
<p>首先,确保整个Android源码被编译成功了(至于不知道如何下载Android源码和编译源码的同学请自行Google),在out目录中所对应的机型目录下成功生成了<strong>system.img</strong>这个文件。接下来就是如何导入的详细步骤了:</p>
<blockquote><p>请注意:接下来的命令都是在你所下载的Android源码的根目录中执行。</p></blockquote>
<!--more-->
<h3>1. 执行如下命令生成idegen.jar文件</h3>
<figure class='code'><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=''><span class='line'>mmm developent/tools/idegen/ -B</span></code></pre></td></tr></table></div></figure>
<h3>2. 按如下命令执行idegen.sh脚本</h3>
<figure class='code'><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=''><span class='line'>development/tools/idegen/idegen.sh</span></code></pre></td></tr></table></div></figure>
<p>这时如果是导入Android 5.0的源码的话会碰到如下图的问题:</p>
<p><img src="http://jeffreylyg.github.io/images/2014-12-27/1.png"></p>
<p>解决办法很简单,先将那个目录随便改个后缀名,等导入完成后改回来就好了。这条命令执行完成后就在Android源码的根目录中生成了android.iml, android.ipr和android.iws三个文件。</p>
<h3>3. 用IntelliJ IDEA直接打开android.ipr这个文件</h3>
<p>这时IntelliJ IDEA就会扫描整个目录并去建立索引了,这个过程是相当耗时间的,大概得一个小时左右,根据电脑的配置决定,所以为了能最大程度地缩短这个时间,在这之前我们有一些准备工作要做。</p>
<h4>增加IntelliJ IDEA所占的内存</h4>
<p>如果是Linux,就在”IDEA_HOME/bin/idea.vmoptions64”(如果是mac系统就在”IntelliJ IDEA.app/Contents/Info.plist”)中改变”“-Xms -Xmx”的值,如果是32位系统就修改vmoptions。我的电脑是16G的内存,所以我给Xmx开了1G。</p>
<h3>4.一些后续步骤</h3>
<p>当建立完索引后打开project structure就会在modules中看到如下图所示的很多jar文件的依赖:</p>
<p><img src="http://jeffreylyg.github.io/images/2014-12-27/2.png"></p>
<p>这时将除了Module Source 和 1.6 (No libraries)的jar文件都移除了,然后选Module SDK为一个空的java library(官方的说法),其实选对应版本的Android SDK也是没有任何问题的。</p>
<p>IntelliJ IDEA导入Android源码的步骤就这么多,接下来就可以在IntelliJ IDEA中自由自在地阅读源码或进行源码的开发了。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[onInterceptTouchEvent和onTouchEvent的调用时序]]></title>
<link href="http://jeffreylyg.github.io/blog/2014/11/10/onintercepttoucheventhe-ontoucheventde-diao-yong-shi-xu/"/>
<updated>2014-11-10T23:40:42+08:00</updated>
<id>http://jeffreylyg.github.io/blog/2014/11/10/onintercepttoucheventhe-ontoucheventde-diao-yong-shi-xu</id>
<content type="html"><![CDATA[<p>大多数刚接触Android开发的同学可能会对<code>onInterceptTouchEvent</code>和<code>onTouchEvent</code>这两个方法的调用时序搞不明白,我刚开始也是,只是知其然,但不知其所以然。其实很多事情最重要的是亲手去做,一旦自己亲手去做了,很多事情就会豁然开朗。下面我们通过代码和Log将这两个方法的调用时序给大家展示明白。</p>
<p><code>onInterceptTouchEvent</code>是ViewGroup中的方法,从字面上理解很简单,就是拦截Touch事件,返回<strong>true</strong>表示拦截,返回<strong>false</strong>表示不拦截。</p>
<p><code>onTouchEvent</code>是View中的方法,它也返回一个布尔值,<strong>true</strong>表示对这个Touch事件进行处理,消耗(consume)了这个事件,<strong>false</strong>表示对这个Touch事件不进行处理,没有消耗这个事件,然后将这个事件返回给它的父亲处理。</p>
<p>首先,我们一定要对Android的触控系统(Touch System)充分理解,当我们手指按在手机屏幕上的时候,首先是当前的Activity接收到这个事件,然后通过<code>dispatchTouchEvent</code>方法将事件分发给当前的布局(layout),最先接收到事件的是布局最外层的ViewGroup,也就是身为父亲的ViewGroup,然后它再通过自己的<code>dispatchTouchEvent</code>方法将这个事件传递给它的孩子(View或ViewGroup),如果此时这个父亲在它的<code>onInterceptTouchEvent</code>方法中返回<strong>true</strong>表示它将这个事件拦截了,以后的所有事件都不传递给自己的孩子了,在自己的<code>onTouchEvent</code>中返回<strong>true</strong>来处理这个事件,返回<strong>false</strong>交给它的父亲来处理这个事件。反之,以后的事件它都传递给自己的孩子,让自己的孩子来处理这个事件,同时它也监听着。一旦拦截了就将以后所有的事件都不会传递给自己的孩子了,这个过程是不可逆的,直到下一个周期到来(接收到下一个<strong>Down</strong>事件)。Android的触控系统是以接收到一个<strong>Down</strong>事件到接收到一个<strong>Up</strong>或<strong>Cancel</strong>事件为一个周期的。Android的触控系统就是这样将事件层层传递和层层返回的。</p>
<!--more-->
<p>好了,下面我们来看具体的例子,Demo的布局很简单,就是一个自定义的LinearLayout嵌套了一个自定义的View,对应的XML代码如下:</p>
<figure class='code'><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>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt"><com.jeffreylee.androidtest.CustomLinearLayout</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
</span><span class='line'> <span class="na">android:id=</span><span class="s">"@+id/parent"</span>
</span><span class='line'> <span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
</span><span class='line'> <span class="na">android:layout_height=</span><span class="s">"match_parent"</span><span class="nt">></span>
</span><span class='line'>
</span><span class='line'> <span class="nt"><com.jeffreylee.androidtest.CustomView</span>
</span><span class='line'> <span class="na">android:id=</span><span class="s">"@+id/child"</span>
</span><span class='line'> <span class="na">android:layout_width=</span><span class="s">"300dp"</span>
</span><span class='line'> <span class="na">android:layout_height=</span><span class="s">"300dp"</span>
</span><span class='line'> <span class="na">android:layout_gravity=</span><span class="s">"center"</span>
</span><span class='line'> <span class="na">android:background=</span><span class="s">"@android:color/holo_red_dark"</span> <span class="nt">/></span>
</span><span class='line'><span class="nt"></com.jeffreylee.androidtest.CustomLinearLayout></span>
</span></code></pre></td></tr></table></div></figure>
<h3>父亲CustomLinearLayout<code>onInterceptTouchEvent</code>中返回<strong>true</strong>拦截事件</h3>
<p>CustomLinearLayout.java</p>
<figure class='code'><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>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onInterceptTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">ev</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">ev</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_DOWN in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_MOVE in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_UP in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">true</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'>
</span><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">event</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_DOWN in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_MOVE in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_UP in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">true</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>CustomView.java</p>
<figure class='code'><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>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">event</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_DOWN in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_MOVE in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_UP in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">true</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>Log如下:</p>
<blockquote><p>I/AndroidTouchSystem(24162): ParentView has received ACTION_DOWN in onInterceptTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_DOWN in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(24162): ParentView has received ACTION_UP in onTouchEvent</p></blockquote>
<h3>父亲CustomLinearLayout<code>onInterceptTouchEvent</code>中返回<strong>false</strong>不拦截事件,孩子CustomView<code>onTouchEvent</code>中返回false不处理事件</h3>
<p>CustomLinearLayout.java</p>
<figure class='code'><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>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onInterceptTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">ev</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">ev</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_DOWN in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_MOVE in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_UP in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">false</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'>
</span><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">event</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_DOWN in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_MOVE in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_UP in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">true</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>CustomView.java</p>
<figure class='code'><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>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">event</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_DOWN in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_MOVE in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_UP in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">false</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>Log如下:</p>
<blockquote><p>I/AndroidTouchSystem(25705): ParentView has received ACTION_DOWN in onInterceptTouchEvent
I/AndroidTouchSystem(25705): ChildView has received ACTION_DOWN in onTouchEvent
I/AndroidTouchSystem(25705): ParentView has received ACTION_DOWN in onTouchEvent
I/AndroidTouchSystem(25705): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(25705): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(25705): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(25705): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(25705): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(25705): ParentView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(25705): ParentView has received ACTION_UP in onTouchEvent</p></blockquote>
<h3>父亲CustomLinearLayout<code>onInterceptTouchEvent</code>中返回<strong>false</strong>不拦截事件,孩子CustomView<code>onTouchEvent</code>中返回true消耗事件</h3>
<p>CustomLinearLayout.java</p>
<figure class='code'><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>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onInterceptTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">ev</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">ev</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_DOWN in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_MOVE in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_UP in onInterceptTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">false</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'>
</span><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">event</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_DOWN in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_MOVE in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ParentView has received ACTION_UP in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">true</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>CustomView.java</p>
<figure class='code'><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>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="nd">@Override</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">onTouchEvent</span><span class="o">(</span><span class="n">MotionEvent</span> <span class="n">event</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="o">(</span><span class="n">event</span><span class="o">.</span><span class="na">getAction</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_DOWN</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_DOWN in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_MOVE</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_MOVE in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_UP</span><span class="o">:</span>
</span><span class='line'> <span class="k">case</span> <span class="n">MotionEvent</span><span class="o">.</span><span class="na">ACTION_CANCEL</span><span class="o">:</span>
</span><span class='line'> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">"ChildView has received ACTION_UP in onTouchEvent"</span><span class="o">);</span>
</span><span class='line'> <span class="k">break</span><span class="o">;</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <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="kc">true</span><span class="o">;</span>
</span><span class='line'> <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>Log如下:</p>
<blockquote><p>I/AndroidTouchSystem(26236): ParentView has received ACTION_DOWN in onInterceptTouchEvent
I/AndroidTouchSystem(26236): ChildView has received ACTION_DOWN in onTouchEvent
I/AndroidTouchSystem(26236): ParentView has received ACTION_MOVE in onInterceptTouchEvent
I/AndroidTouchSystem(26236): ChildView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(26236): ParentView has received ACTION_MOVE in onInterceptTouchEvent
I/AndroidTouchSystem(26236): ChildView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(26236): ParentView has received ACTION_MOVE in onInterceptTouchEvent
I/AndroidTouchSystem(26236): ChildView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(26236): ParentView has received ACTION_MOVE in onInterceptTouchEvent
I/AndroidTouchSystem(26236): ChildView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(26236): ParentView has received ACTION_MOVE in onInterceptTouchEvent
I/AndroidTouchSystem(26236): ParentView has received ACTION_MOVE in onInterceptTouchEvent
I/AndroidTouchSystem(26236): ChildView has received ACTION_MOVE in onTouchEvent
I/AndroidTouchSystem(26236): ParentView has received ACTION_UP in onInterceptTouchEvent
I/AndroidTouchSystem(26236): ChildView has received ACTION_UP in onTouchEvent</p></blockquote>
<p>同学们看完代码和Log后对<code>onInterceptTouchEvent</code>和<code>onTouchEvent</code>的调用时序应该就会很明白了吧。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Processes and Threads]]></title>
<link href="http://jeffreylyg.github.io/blog/2014/05/22/processes-and-threads/"/>
<updated>2014-05-22T14:18:44+08:00</updated>
<id>http://jeffreylyg.github.io/blog/2014/05/22/processes-and-threads</id>
<content type="html"><![CDATA[<blockquote><p>注:此为毕业设计中学院要求的翻译与自己所做毕设相关且不少于2万字符英文原始资料的任务,由于自己毕设做的是Android方面的开发,所以决定翻译一下Android官方文档中Training和API Guides中的部分内容。由于水平有限,如有错误,望理解。</p></blockquote>
<h2><a href="http://developer.android.com/guide/components/processes-and-threads.html">原文链接</a></h2>
<br/>
<p>当一个应用程序组件启动并且这个应用程序没有任何其它组件运行时,Android系统会为这个应用程序启动一个新的执行单线程的Linux进程。默认情况下,同一个应用程序的所有组件在同一个进程和线程(称为“主”线程)中运行。如果一个应用程序组件启动并且已经为那个应用程序存在一个进程(因为那个应用程序的另一个组件存在),则这个组件在那个进程内启动并且使用同一个执行线程。然而,你可以将应用程序的不同组件安排运行在不同的进程里,并且你可以为任何进程创建额外的线程。</p>
<p>这份文档讨论了进程和线程在一个Android应用程序里是如何工作的。</p>
<!--more-->
<h2>进程</h2>
<p>默认情况下,同一个应用程序的所有组件运行在同一个进程上并且大多数应用程序不应该改变这一点。然而,如果你发现你需要控制某个组件属于哪个进程,你可以在manifest文件里这样做。</p>
<p>组件元素每种类型的清单项——<a href="http://developer.android.com/guide/topics/manifest/activity-element.html"><activity></a>,<a href="http://developer.android.com/guide/topics/manifest/service-element.html"><service></a>,<a href="http://developer.android.com/guide/topics/manifest/receiver-element.html"><receiver></a>和<a href="http://developer.android.com/guide/topics/manifest/provider-element.html"><provider></a>——支持一个 android:process 属性可以指定一个那个组件运行的进程。你可以设置这个属性以便每个组件运行在它自己的进程上或者一些组件共享一个进程而其它的组件没有。你还可以设置 android:process 以便不同应用程序的组件运行在同一个进程里——只要这些应用程序共享相同的Linux用户ID和相同的证书签名。</p>
<p><a href="http://developer.android.com/guide/topics/manifest/application-element.html"><application></a>元素也支持 android:process 属性,设置一个默认值应用于所有的组件。</p>
<p>当内存不足和被其它进程要求更直接服务用户时,Android也许在某些时候决定关闭进程。在被杀进程中运行的应用程序组件因此被破坏。一个进程再次启动这些组件当再次有工作给它们做时。</p>
<p>当决定杀死哪些进程时,Android系统衡量它们对用户的相对重要性。例如,更容易关闭一个持有在屏幕上看不见的活动的进程,相对于持有可见活动的进程来说。因此,决定是否要终止一个进程,取决于在那个进程里运行的组件的状态。用于决定哪些进程终止的规则将在下面讨论。</p>
<h3>进程生命周期</h3>
<p>Android系统试图尽可能地保持一个应用程序的进程,但是最终为了新的或更重要的进程还是需要移除旧的进程来回收内存。要决定保持哪些进程和杀死哪些进程,系统基于运行在进程里的组件和这些组件的状态将每个进程放进了“重要性层级”。最低重要性的进程最先被消除,然后是这些次重要性的进程,然后依次类推,如果有必要回收系统资源。</p>
<p>有五个级别的重要性层级。下面的列表按重要性的顺序展示了不同类型的进程(第一个进程是最重要的且最后一个被杀死的)。</p>
<p>1.前台进程</p>
<p>一个用户当前正在做什么需要的进程。一个进程被认为是在前台如果下列任何情况为真:</p>
<ul>
<li><p>它持有一个用户正在进行交互的<a href="http://developer.android.com/reference/android/app/Activity.html">Activity</a>(<a href="http://developer.android.com/reference/android/app/Activity.html">Activity</a>的<a href="http://developer.android.com/reference/android/app/Activity.html#onResume()">onResume()</a>方法被调用)。</p></li>
<li><p>它持有一个绑定到用户正在进行交互的活动的<a href="http://developer.android.com/reference/android/app/Service.html">Service</a>。</p></li>
<li><p>它持有一个正在运行在前台的<a href="http://developer.android.com/reference/android/app/Service.html">Service</a>——这个服务调用了<a href="http://developer.android.com/reference/android/app/Service.html#startForeground(int,%20android.app.Notification)">startForeground()</a>方法。</p></li>
<li><p>它持有一个正在执行Service生命周期中的一个回调函数的<a href="http://developer.android.com/reference/android/app/Service.html">Service</a>(<a href="http://developer.android.com/reference/android/app/Service.html#onCreate()">onCreate()</a>,<a href="http://developer.android.com/reference/android/app/Service.html#onStart(android.content.Intent,%20int)">onStart()</a>,或者<a href="http://developer.android.com/reference/android/app/Service.html#onDestroy()">onDestroy()</a>)。</p></li>
<li><p>它持有一个正在执行<a href="http://developer.android.com/reference/android/content/BroadcastReceiver.html#onReceive(android.content.Context,%20android.content.Intent)">onReceive()</a>方法的<a href="http://developer.android.com/reference/android/content/BroadcastReceiver.html">BroadcastReceiver</a>。</p></li>
</ul>
<p>通常,只有少数前台进程在任何给定的时间存在。它们被杀死仅仅是作为最后的手段——如果内存是如此之低以至于它们都不能继续运行。一般来说,在这一点上,该设备已经达到了内存分页状态,所以需要杀死某些前台进程来保持用户界面能够响应。</p>
<p>2.可见进程</p>
<p>一个没有任何前台组件,但是仍然能够影响用户在屏幕上看到什么的进程。一个进程被认为是可见的如果下列任何一个情况为真:</p>
<ul>
<li><p>它持有一个不在前台的<a href="http://developer.android.com/reference/android/app/Activity.html">Activity</a>,但是仍然对用户是可见的(它的<a href="http://developer.android.com/reference/android/app/Activity.html#onPause()">onPause()</a>方法被调用)。这可能会发生,例如,如果前台活动启动一个对话框,它允许前一个活动在它后面能看到。</p></li>
<li><p>它持有一个绑定到可见的(或前台的)活动的<a href="http://developer.android.com/reference/android/app/Service.html">Service</a>。</p></li>
</ul>
<p>一个可见进程被认为是及其重要的并且不会被杀死除非这样做是需要保持所有前台进程的运行。</p>
<p>3.Service 进程</p>
<p>一个正在运行着被<a href="http://developer.android.com/reference/android/content/Context.html#startService(android.content.Intent)">startService()</a>方法启动的服务的进程并且不属于任何上两个更高的类别中的一个。尽管服务进程不会直接连接到用户看到的任何东西,它们一般都做用户关心的事情(例如在后台播放音乐或者在网络上下载数据),因此系统保持它们运行除非没有足够的内存保留它们和前台进程以及可见进程一起运行。</p>
<p>4.后台进程</p>
<p>一个持有对用户当前不可见活动的进程(活动的<a href="http://developer.android.com/reference/android/app/Activity.html#onStop()">onStop()</a>方法被调用)。这些进程对用户体验没有直接影响,并且系统可以在任何时候杀死它们来为前台、可见或是服务进程回收内存。通常情况下有很多正在运行的后台进程,因此它们保持着一个LRU(最近最少使用)清单来确保最经常被用户看见的活动的进程最后一个被杀死。如果活动正确地实现了它的生命周期方法,并且保存其当前状态,杀死它的进程不会对用户体验有明显的效果,因为当用户导航返回这个活动时,活动恢复了其所有的可见状态。请参阅有关保存和恢复状态信息的<a href="http://developer.android.com/guide/components/activities.html#SavingActivityState">Activities</a>文档。</p>
<p>5.空进程</p>
<p>一个不再持有任何活动的应用程序组件的进程。保持这种进程活着的唯一原因是高速缓存的目的,以提高一个需要运行在它里面的组件的下次的启动时间。为了平衡进程缓存和底层内核缓存之间的整个系统资源系统会经常杀死这些进程。</p>
<p>Android 基于进程中当前活动的组件的重要性把一个进程排在它能排到的最高层级上。例如,如果一个进程持有一个服务和可见的活动,这个进程会被排为可见进程而不是服务进程。</p>
<p>另外,一个进程的排名可能会上升因为其它进程都依赖于它——一个正在服务其它进程的进程永远不可能比它正在服务的进程的排名低。例如,如果进程A中一个的content provider正在服务进程B的一个客户,或者如果进程A中的一个服务绑定到了进程B中的一个组件中,进程A总是被认为至少跟进程B一样重要。</p>
<p>因为运行一个服务的进程比运行后台活动的进程的排名高,所以一个初始化了长期操作的活动为那个操作启动一个服务可能会做得很好,而不是简单地创建一个工作线程——特别是如果那个操作很可能拖垮这个活动。例如,一个向网站正在上传图片的活动应该启动一个服务去执行上传以便上传可以在后台继续即使用户离开了这个活动。使用服务保证了操作至少具有“服务进程”的优先级,不管活动发生了什么。广播接收器应该采用服务而不是简单地把耗时的操作放在一个线程里是同样的道理。</p>
<h2>线程</h2>
<p>当应用程序启动时,系统会创建一个执行应用程序的线程,称为“主线程”。这个线程是非常重要的因为它负责给合适的用户界面部件调度事件,包括绘画事件。它也是应用程序和Android UI工具包里的组件(<a href="http://developer.android.com/reference/android/widget/package-summary.html">android.widget</a>和<a href="http://developer.android.com/reference/android/view/package-summary.html">android.view</a>包里的组件)交互的线程。这样,主线程有时也被称为UI线程。</p>
<p>系统不会为一个组件的每个实例创建一个单独的线程。所有运行在同一个进程中的组件都在UI线程里初始化,并且系统调用每个从那个线程分发的组件。因此,响应系统的回调方法(例如用<a href="http://developer.android.com/reference/android/view/View.html#onKeyDown(int,%20android.view.KeyEvent)">onKeyDown()</a> 来报告用户活动或是一个生命周期回调方法总是在进程中的UI线程里运行。</p>
<p>例如,当用户触摸屏幕上一个按钮时,应用程序的UI线程给这个部件调度触摸事件,从而设置其按下状态和将一个无效请求发送到事件队列中。UI 线程出队队列中的请求并通知这个部件它应该重绘自己。</p>
<p>当应用程序响应用户交互进行深入细致的工作时,这种单一的线程模型可以产生性能差除非正确地实现应用程序。特别是,如果一切都发生在UI线程,执行长时间的操作如网络访问和数据库查询将会阻塞整个UI。当这个线程被阻塞时,没有事件可以被分发出来,包括绘制事件。从用户的角度来看,应用程序似乎挂起。更糟的是,如果UI线程被阻塞超过几秒钟(目前大约是5秒)会给用户呈现臭名昭著的”<a href="http://developer.android.com/guide/practices/responsiveness.html">application not responding</a>“ (ANR) 对话框。用户也许会决定退出程序并且如果他们不开心的话会卸载程序。</p>
<p>此外,Android UI工具包不是线程安全的。所以,不能在一个工作线程里操作UI——对用户界面的所有操作必须都在UI线程里执行。因此,Android的单线程模型有简单的两条规则:</p>
<ol>
<li><p>不要阻塞UI线程。</p></li>
<li><p>不要从UI线程以外的线程里访问Android UI工具包。</p></li>
</ol>
<h3>工作线程</h3>
<p>因为上述的单线程模型,对不阻塞UI线程的应用程序UI的响应是至关重要的。如果你有不是瞬时的操作,你应该确保在单独的线程(“后台”或“工作”线程)里执行它们。</p>
<p>例如,下面是在一个单独的线程里下载一张图片并显示在<a href="http://developer.android.com/reference/android/widget/ImageView.html">ImageView</a>里的点击监听器的一段代码:</p>
<figure class='code'><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>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kt">void</span> <span class="nf">onClick</span><span class="o">(</span><span class="n">View</span> <span class="n">v</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">new</span> <span class="nf">Thread</span><span class="o">(</span><span class="k">new</span> <span class="n">Runnable</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'> <span class="n">Bitmap</span> <span class="n">b</span> <span class="o">=</span> <span class="n">loadImageFromNetwork</span><span class="o">(</span><span class="s">"http://example.com/image.png"</span><span class="o">);</span>
</span><span class='line'> <span class="n">mImageView</span><span class="o">.</span><span class="na">setImageBitmap</span><span class="o">(</span><span class="n">b</span><span class="o">);</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'> <span class="o">}).</span><span class="na">start</span><span class="o">();</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>起初,这似乎工作得很好,因为它会创建一个新的线程去处理网络操作。然而,它违反了单线程模型的第二条规则:不要在UI线程以外的线程里访问Android UI工具包——这个样例在工作线程里而不是UI线程里修改<a href="http://developer.android.com/reference/android/widget/ImageView.html">ImageView</a>。这可能导致不确定的和意外的行为,这追查起来可能是困难且耗时的。</p>
<p>要解决这个问题,Android提供了几种方法从其它线程里来访问UI线程。以下是一个可以帮助的方法的列表:</p>
<ul>
<li><p><a href="http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)">Activity.runOnUiThread(Runnable)</a></p></li>
<li><p><a href="http://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable)">View.post(Runnable)</a></p></li>
<li><p><a href="http://developer.android.com/reference/android/view/View.html#postDelayed(java.lang.Runnable,%20long)">View.postDelayed(Runnable, long)</a></p></li>
</ul>
<p>例如,你可以使用<a href="http://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable)">View.post(Runnable)</a>方法修复上面的代码:</p>
<figure class='code'><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>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kt">void</span> <span class="nf">onClick</span><span class="o">(</span><span class="n">View</span> <span class="n">v</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">new</span> <span class="nf">Thread</span><span class="o">(</span><span class="k">new</span> <span class="n">Runnable</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'> <span class="kd">final</span> <span class="n">Bitmap</span> <span class="n">bitmap</span> <span class="o">=</span> <span class="n">loadImageFromNetwork</span><span class="o">(</span><span class="s">"http://example.com/image.png"</span><span class="o">);</span>
</span><span class='line'> <span class="n">mImageView</span><span class="o">.</span><span class="na">post</span><span class="o">(</span><span class="k">new</span> <span class="n">Runnable</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
</span><span class='line'> <span class="n">mImageView</span><span class="o">.</span><span class="na">setImageBitmap</span><span class="o">(</span><span class="n">bitmap</span><span class="o">);</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'> <span class="o">});</span>
</span><span class='line'> <span class="o">}</span>
</span><span class='line'> <span class="o">}).</span><span class="na">start</span><span class="o">();</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>现在这个实现是线程安全的:网络操作在一个单独的线程里完成而<a href="http://developer.android.com/reference/android/widget/ImageView.html">ImageView</a>在UI线程里操作。</p>
<p>然而,随着操作的复杂性的增加,这种代码可以变得复杂且难以维护。为了处理和一个工作线程更复杂的操作,你可以考虑在工作线程里用<a href="http://developer.android.com/reference/android/os/Handler.html">Handler</a>来处理从UI线程里传递的消息。但是,最好的解决方法是用<a href="http://developer.android.com/reference/android/os/AsyncTask.html">Asynctask</a>的扩展类,它简化了需要与UI交互的工作线程任务的执行。</p>
<h3>使用 AsyncTask</h3>
<p><a href="http://developer.android.com/reference/android/os/AsyncTask.html">Asynctask</a>允许在用户界面上异步工作。它在一个工作线程里执行阻塞操作然后将结果发布到UI线程上来,而不需要自己来处理线程和/或 handlers。</p>
<p>要使用它,你必须继承<a href="http://developer.android.com/reference/android/os/AsyncTask.html">Asynctask</a>并实现<a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground(Params...)">doInBackground()</a>回调方法,它在一个后台线程池里运行。要更新UI,你应该实现<a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute(Result)">onPostExecute()</a>方法,它传递<a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground(Params...)">doInBackground()</a>方法里的结果且运行在UI 线程里,所以你可以安全地更新你的UI。然后你可以通过在UI线程里调用<a href="http://developer.android.com/reference/android/os/AsyncTask.html#execute(Params...)">execute()</a>方法运行这个任务。</p>
<p>例如,你可以这么用<a href="http://developer.android.com/reference/android/os/AsyncTask.html">Asynctask</a>实现上一个例子:</p>
<figure class='code'><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>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kt">void</span> <span class="nf">onClick</span><span class="o">(</span><span class="n">View</span> <span class="n">v</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">new</span> <span class="nf">DownloadImageTask</span><span class="o">().</span><span class="na">execute</span><span class="o">(</span><span class="s">"http://example.com/image.png"</span><span class="o">);</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">private</span> <span class="kd">class</span> <span class="nc">DownloadImageTask</span> <span class="kd">extends</span> <span class="n">AsyncTask</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">Void</span><span class="o">,</span> <span class="n">Bitmap</span><span class="o">></span> <span class="o">{</span>
</span><span class='line'> <span class="cm">/** The system calls this to perform work in a worker thread and</span>
</span><span class='line'><span class="cm"> * delivers it the parameters given to AsyncTask.execute() */</span>
</span><span class='line'> <span class="kd">protected</span> <span class="n">Bitmap</span> <span class="nf">doInBackground</span><span class="o">(</span><span class="n">String</span><span class="o">...</span> <span class="n">urls</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="k">return</span> <span class="nf">loadImageFromNetwork</span><span class="o">(</span><span class="n">urls</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="cm">/** The system calls this to perform work in the UI thread and delivers</span>
</span><span class='line'><span class="cm"> * the result from doInBackground() */</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">Bitmap</span> <span class="n">result</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'> <span class="n">mImageView</span><span class="o">.</span><span class="na">setImageBitmap</span><span class="o">(</span><span class="n">result</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>现在UI是安全的且代码是简单的,因为它将这项工作分离成了应该在工作线程里完成的部分和应该在UI线程里完成的部分。</p>
<p>要充分了解如何使用这个类你应该阅读<a href="http://developer.android.com/reference/android/os/AsyncTask.html">Asynctask</a>参考,但下面是它是如何工作的简要概述:</p>
<ul>
<li><p>你可以指定参数的类型,进展值和任务的最终值,泛型使用。</p></li>
<li><p><a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground(Params...)">doInBackground()</a>方法在工作线程里会自动执行。</p></li>
<li><p><a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPreExecute()">onPreExecute()</a>,<a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute(Result)">onPostExecute()</a>和<a href="http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate(Progress...)">onProgressUpdate()</a>方法都在UI线程里调用。</p></li>
<li><p><a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground(Params...)">doInBackground()</a>方法返回的值被发送到<a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute(Result)">onPostExecute()</a>方法里。</p></li>
<li><p>你可以在<a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground(Params...)">doInBackground()</a>方法里在任何时间调用<a href="http://developer.android.com/reference/android/os/AsyncTask.html#publishProgress(Progress...)">publishProgress()</a>来在UI线程里执行<a href="http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate(Progress...)">onProgressUpdate()</a>方法。</p></li>
<li><p>你可以在任何时间,从任何线程里取消任务。</p></li>
</ul>
<blockquote><p>注意:当你使用一个在活动里由于<a href="http://developer.android.com/guide/topics/resources/runtime-changes.html">runtime configuration change</a>(例如当用户改变了屏幕方向)而意外重新启动的工作线程时,你可能遇到的另一个问题,它也许会销毁你的工作线程。要看如何在这些重启动的活动之一期间保持你的任务和当活动被销毁时如何正确地取消任务,请参阅<a href="http://code.google.com/p/shelves/">Shelves</a>样例中的源代码。</p></blockquote>
<h3>线程安全的方法</h3>
<p>在某些情况下,你实现的方法可能会从多个线程里调用,因此写入必须是线程安全的。</p>
<p>这主要适用于可以被称为远程的方法——例如<a href="http://developer.android.com/guide/components/bound-services.html">bound service</a>里的方法。当一个实现了<a href="http://developer.android.com/reference/android/os/IBinder.html">IBinder</a>方法的调用源自于这个<a href="http://developer.android.com/reference/android/os/IBinder.html">IBinder</a>运行的同样的进程中时,这个方法就在调用者的线程里执行。然而,当这个调用源自于另一个进程时,这个方法就在系统维护的和运行<a href="http://developer.android.com/reference/android/os/IBinder.html">IBinder</a>同一个进程里的线程池里选择一个线程运行(它不会在进程的 UI 线程里执行)。 例如,尽管一个服务的<a href="http://developer.android.com/reference/android/app/Service.html#onBind(android.content.Intent)">onBind()</a>方法会从这个服务的进程的UI线程调用,但是在<a href="http://developer.android.com/reference/android/app/Service.html#onBind(android.content.Intent)">onBind()</a>返回(例如,一个实现了RPC方法的子类)的这个对象里实现的方法会再线程池里调用。因为一个服务可以有多个客户,池中的多个线程可以同时占用同一个<a href="http://developer.android.com/reference/android/os/IBinder.html">IBinder</a>。因此,<a href="http://developer.android.com/reference/android/os/IBinder.html">IBinder</a>方法必须被实现为线程安全的。</p>
<p>类似的,一个content provider可以接收其它进程的数据请求。尽管<a href="http://developer.android.com/reference/android/content/ContentResolver.html">ContentResolver</a> 和<a href="http://developer.android.com/reference/android/content/ContentProvider.html">ContentProvider</a>类隐藏了进程间通信是如何管理的细节,响应这些请求的<a href="http://developer.android.com/reference/android/content/ContentProvider.html">ContentProvider</a>方法——<a href="http://developer.android.com/reference/android/content/ContentProvider.html#query(android.net.Uri,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lang.String)">query()</a>, <a href="http://developer.android.com/reference/android/content/ContentProvider.html#insert(android.net.Uri,%20android.content.ContentValues)">insert()</a>, <a href="http://developer.android.com/reference/android/content/ContentProvider.html#delete(android.net.Uri,%20java.lang.String,%20java.lang.String[])">delete()</a>, <a href="http://developer.android.com/reference/android/content/ContentProvider.html#update(android.net.Uri,%20android.content.ContentValues,%20java.lang.String,%20java.lang.String[])">update()</a>,和<a href="http://developer.android.com/reference/android/content/ContentProvider.html#getType(android.net.Uri)">getType()</a>方法——在这个content provider的进程的一个线程池里调用,而不是这个进程的UI线程里调用。因为这些方法可以同时被任意数量的线程调用,所以它们也必须被实现为线程安全的。</p>
<h3>进程间通信</h3>
<p>Android提供了一种机制,使用远程过程调用(RPC)来进程间通信(IPC),其中一个方法被一个activity或其它应用程序组件调用,但是远端执行(另一个进程中),并返回任何结果给调用者。这需要分解一个方法的调用和它的数据到一个操作系统能理解的层级,从本地进程和地址空间传送到元曾进程和空间地址,然后重新组装和在那里重新再次调用。然后返回值在相反的方向上传输。Android提供了所有执行这些IPC交易的代码,所以你可以专注于定义和实现RPC编程接口。</p>
<p>要执行IPC,你的应用程序必须使用<a href="http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)">bindService()</a>绑定到一个服务。欲了解更多信息,请参阅<a href="http://developer.android.com/guide/components/services.html">Services</a>开发指南。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Tasks and Back Stack]]></title>
<link href="http://jeffreylyg.github.io/blog/2014/05/19/tasks-and-back-stack/"/>
<updated>2014-05-19T12:27:49+08:00</updated>
<id>http://jeffreylyg.github.io/blog/2014/05/19/tasks-and-back-stack</id>
<content type="html"><![CDATA[<blockquote><p>注:此为毕业设计中学院要求的翻译与自己所做毕设相关且不少于2万字符英文原始资料的任务,由于自己毕设做的是Android方面的开发,所以决定翻译一下Android官方文档中Training和API Guides中的部分内容。由于水平有限,如有错误,望理解。</p></blockquote>
<h2><a href="http://developer.android.com/guide/components/tasks-and-back-stack.html">原文链接</a></h2>
<br/>
<p>一个应用程序通常包含多个<a href="http://developer.android.com/guide/components/activities.html">活动</a>。每个活动应该围绕一个用户可以执行和开始其它的活动特定的动作来设计。例如,电子邮件应用程序可能有一个展示新邮件的列表的活动。当用户选择一封邮件时,一个新的活动被打开来查看这封邮件。</p>
<!--more-->
<p>一个活动甚至能启动设备上其它应用程序中存在的活动。例如,如果你的应用程序想要发送一封邮件,你可以定义一个意图来执行“发送”任务并包含一些数据,例如邮件地址和消息。另一个应用程序的活动声明自己处理这种意图然后打开邮件。在这种情况下,意图是发送一封邮件,所以邮件应用程序的 “compose” 活动启动(如果有多个活动支持相同的意图)然后系统让用户选择使用哪一个。当邮件被发送出后,你的活动恢复并且似乎邮件的活动看起来是你的应用程序的一部分。尽管这些活动可能来自不同的应用程序,Android 通过保持两个活动在同一任务中维护了这种无缝的用户体验。</p>
<p>任务是当用户执行某项作业时和用户交互的活动的集合。这些活动以被打开的顺序安排在一个栈(“回退栈”)中。</p>
<p>设备的主屏幕是大多数任务的起点。当用户按下应用启动器中的图标(或主屏幕上的快捷方式)时,该应用程序的任务来到前台。如果应用程序不存在任务,然后一个新的任务会被创建并且那个应用程序的”主”活动打开并作为栈中的根活动。</p>
<p>当当前的活动启动另一个活动时,新的活动被压入堆栈的顶部并获取焦点。前一个活动保持在栈中,但被停止了。当一个活动停止时,系统保留其用户接口的当前状态。当用户按下返回按钮时,当前的活动从栈顶弹出(这个活动就被销毁了)并且前一个活动恢复(其之前的UI状态恢复)。栈中的活动从来没有重新排列,仅仅是从栈中压栈和出栈——当被当前的活动启动时压入栈和当用户用后退按钮离开它时弹出栈。因此,回退栈是作为一个“后进先出”的对象结构操作着。图 1 用展示了活动和当前回退栈在每个时间点的过程的时间线可视化了这种行为。</p>
<p><img src="http://jeffreylyg.github.io/images/2014-5-19/1.png" title="图 1" ></p>
<p>图1:表示了任务中每一个新的活动是怎样在回退栈中增加了一个项目。当用户按下返回按钮时,当前的活动被销毁,前一个活动恢复。</p>
<p>如果用户继续按返回,然后再堆栈中的每个活动都弹出来显示前一个,直到用户返回到主屏幕(或者任何任务开始时正在运行的活动)。当所有的活动从堆栈中移除后,这个任务就不再存在。</p>
<p><img class="right" src="http://jeffreylyg.github.io/images/2014-5-19/2.png" title="图 2" >
任务是一个当用户开始新任务或通过 <em>Home</em> 按钮去到主屏幕时可以移动到后台的内聚单位。当在后台时,堆栈中所有的活动被停止,但是回退栈的任务保持不变——任务仅仅是失去了焦点当另一个任务替换了它时,如图 2 所示。再一个任务可以返回到“前台”使用户可以在它们停止的地方获得它们。假设,例如,当前的任务(A任务)在它的堆栈里有三个活动——两个在当前这个活动之下。用户按了主 按钮,然后从应用启动器里启动了一个新的任务。当主屏幕出现时,A 任务进入后台。当新的应用程序启动时,系统为那个应用程序启动了一个带有自己的活动堆栈的任务(B任务)。与应用程序交互后,用户再次返回主页并选择最初启动的 A 任务的应用程序。现在 A 任务来到了前台——栈中所有的三个活动完好并且栈顶的活动恢复。此时,用户还可以通过返回主页选择启动了那个任务的应用程序图标(或通过从最近使用程序屏幕选择那个应用程序的任务)切回到 B 任务。这是一个 Android 上多任务的例子。</p>
<blockquote><p>注:多个任务在后台只能保留一次。然而,如果用户同一时间运行多个后台任务,系统可能会为了恢复内存开始销毁后台活动,导致活动状态丢失。详情请参见 <a href="http://developer.android.com/guide/components/tasks-and-back-stack.html#ActivityState">Activity state</a> 部分。</p></blockquote>
<p><img class="right" src="http://jeffreylyg.github.io/images/2014-5-19/3.png" title="图 3" >
因为在回退栈中的活动永远不会重新排列,如果你的应用程序允许用户从多个活动启动特定的活动,那么一个新的活动实例会被创建并且压入栈中(而不是把这个活动之前的任何实例拿到栈顶来)。因此,在你的应用程序中一个活动可能会被多次实例化(即使来自不同的任务),如图 3 所示。因此,如果用户使用后退按钮向后导航,活动的每个实例(和每个的UI状态)会按它们被打开的顺序显示。然而,如果你不想一个活动被实例化多次你可以改变这种行为。怎么做在接下来关于 <a href="http://developer.android.com/guide/components/tasks-and-back-stack.html#ManagingTasks">Managing Tasks</a> 的章节中会讨论。</p>
<p>活动和任务的默认行为的总结:</p>
<ul>
<li><p>当活动 A 启动活动 B 时,活动 A 被停止,但系统会保留它的状态(例如滚动条的位置和输入到表单中的文字)。如果用户在活动 B 中按了后退按钮,活动 A 和它被保留的状态一起恢复。</p></li>
<li><p>当用户按下主按钮离开任务时,当前的活动被停止并且它的任务进入后台。系统保留任务中每个活动的状态。如果用户之后通过选择启动图标开始那个任务恢复任务,这个任务会进入前台并且恢复在栈顶的那个活动。</p></li>
<li><p>如果用户按了返回按钮,当前的活动从栈顶弹出并且销毁。栈里前一个活动被恢复。当一个活动被销毁时,系统不会保留活动的状态。</p></li>
<li><p>活动可以被实例化多次,甚至从其它任务上。</p></li>
</ul>
<blockquote><p>设计导航</p>
<p>欲了解更多有关应用程序导航在 Android 上是如何工作的,请阅读 Android 设计<a href="http://developer.android.com/design/patterns/navigation.html">导航</a>指导。</p></blockquote>
<h2>保存活动状态</h2>
<p>正如上面讨论的,系统的默认行为会保存一个活动的状态当它停止时。这样,当用户导航返回到前一个活动时,它的用户界面展示的是它消失时的样子。然而,你能——并且也应该——使用回调方法主动保留活动的状态,以防活动被销毁后必须重新创建。</p>
<p>当系统停止你的其中一个活动时(例如当一个新的活动启动或任务进入了后台),系统如果为了恢复系统内存也许会完全地销毁那个活动。当这种情况发生时,活动的状态信息也就丢失了。如果发生了这种情况,系统仍然知道这个活动在回退栈中有一个位置,但是当这个活动被拿到栈顶时系统必须重新创建它(而不是恢复它)。为了避免丢失用户的工作,你应该在活动里通过实现 <a href="http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle">onSaveInstanceState()</a>) 回调方法主动保留用户的工作。</p>
<p>有关更多如何保存活动状态的信息,请参见 <a href="http://developer.android.com/guide/components/activities.html#SavingActivityState">Activities</a> 文档。</p>
<h2>任务管理</h2>
<p>如上所述,Android 通过将所有的活动逐次地放进同样的任务中和一个“先进先出”的堆栈中的方式来管理任务和回退栈——这对大多数应用程序有效并且你不必担心你的活动和任务是怎么关联的以及它们在回退栈中是怎么存在的。然而,你也许会决定要中断正常的行为。也许你想要应用程序的活动当它启动的时候是开始一个新的任务(而不是被放置在当前的任务内)。或者,当你开始一个新的活动,你想要将已经存在的实例拿到前面来(而不是在回退栈的顶部创建一个新的实例)。或者,你想要你的回退栈当用户离开任务时除了根活动外清除所有的活动。</p>
<p>你可以通过 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html"><activity></a> manifest 元素的属性和传递给 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)">startActivity()</a> 的意图的标志做这些事情和更多这样的事情。</p>
<p>在这方面,你可以使用的主要的 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html"><activity></a> 属性如下:</p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#aff">taskAffinity</a></p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#lmode">launchMode</a></p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#reparent">allowTaskReparenting</a></p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a></p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a></p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#finish">finishOnTaskLaunch</a></p>
<p>和你可以使用的主要的意图标志如下:</p>
<p><a href="http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK">FLAG_ACTIVITY_NEW_TASK</a></p>
<p><a href="http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP">FLAG_ACTIVITY_CLEAR_TOP</a></p>
<p><a href="http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_SINGLE_TOP">FLAG_ACTIVITY_SINGLE_TOP</a></p>
<p>在接下来的章节里,你将会看到如何使用这些清单属性和意图标志来定义活动和任务是如何相关的以及活动在回退栈中是如何表现的。</p>
<blockquote><p>注意:大多数应用程序不应该打断活动和任务的默认行为。如果你确定有必要修改活动的默认行为,在启动和用返回按钮从其它的活动和任务中导航返回时谨慎使用并且确保测试活动的可用性。确保测试导航行为可能与用户预期的行为发生生冲突的情况。</p></blockquote>
<h3>定义启动模式</h3>
<p>启动模式允许你定义一个活动的新实例如何与当前任务相关联。你可以通过两种方式定义不同的启动模式:</p>
<p><a href="http://developer.android.com/guide/components/tasks-and-back-stack.html#ManifestForTasks">使用清单文件</a></p>
<p>当你在清单文件中声明一个活动时,你可以当活动启动的时候指定活动如何与任务相关联。</p>
<p><a href="http://developer.android.com/guide/components/tasks-and-back-stack.html#IntentFlagsForTasks">使用意图标志</a></p>
<p>当你调用 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)">startActivity()</a> 时,你可以在<a href="http://developer.android.com/reference/android/content/Intent.html">意图</a>里包含一个声明了新的活动如何(或是否)与当前的任务相关联的标志。</p>
<p>因此,如果活动 A 启动活动 B, 活动 B 可以在其清单定义应该如何与当前任务相关联(如果有的话)和活动 A 还可以要求活动 B 应该如何与当前任务相关联。如果这两个活动都定义了活动 B 应该如何与任务相关联,那么活动 A 的请求(如在意图里定义的)高于活动 B 的请求(如在清单中定义的)。</p>
<blockquote><p>注:一些适用于清单文件的启动模式并不是适用于意图标志,同样,一些适用于意图标志的启动模式也不适用于清单文件。</p></blockquote>
<h4>使用清单文件</h4>
<p>当在清单文件声明活动时,你可以用 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html"><activity></a> 元素的<a href="http://developer.android.com/guide/topics/manifest/activity-element.html#lmode">启动模式</a>属性指定活动应该如何与任务相关联。</p>
<p>该<a href="http://developer.android.com/guide/topics/manifest/activity-element.html#lmode">启动模式</a>属性指定一个活动应该如何启动进任务中的指令。有四种不同的启动模式你可以给<a href="http://developer.android.com/guide/topics/manifest/activity-element.html#lmode">启动模式</a>属性赋值:</p>
<p>“standard”(默认模式)</p>
<p>默认。从任务里启动活动和发送意图给活动系统会创建这个活动的一个新实例。活动会被多次实例化,每个实例可以属于不同的任务,并且一个任务可以有多个实例。</p>
<p>“singleTop”</p>
<p>如果活动的一个实例已经在当前任务的栈顶存在,系统会通过调用它的 <a href="http://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent)">onNewIntent()</a> 方法给那个实例发送一个意图,而不是创建活动的一个新实例。该活动可以被实例化多次,每个实例可以属于不同的任务,一个任务可以有多个实例(但仅当回退栈顶部的活动不是一个该活动已存在的实例)。</p>
<p>例如,假设一个任务的回退栈由根活动 A 和活动 B,活动 C 和顶部的活动 D 组成(栈的顺序为 A-B-C-D; D在顶部)。一个意图过来请求 D 类型的活动。如果 D 有默认的 “standard” 启动模式,一个类的新实例会被启动并且栈变为 A-B-C-D-D。然而,D 的启动模式为 “singleTop”,D 已存在的实例通过 <a href="http://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent)">onNewIntent()</a> 方法接收到这个意图,因为它是在栈的顶部,栈仍然保持为 A-B-C-D。然后,如果一个意图过来请求B类型的活动,则 B 的新实例被添加进堆栈中,即使它的启动模式为 “singleTop”。</p>
<blockquote><p>注意:当活动的一个新实例被创建时,用户可以按返回按钮来返回到前一个活动。但是当一个活动已存在的实例处理一个新意图时,用户不可以按返回键来返回到新意图通过 <a href="http://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent)">onNewIntent()</a> 方法到达之前时的状态。</p></blockquote>
<p>“singleTask”</p>
<p>系统会创建一个新任务并从新任务的根部实例化活动。然而,如果该活动的一个实例已经存在于一个单独的任务,系统会通过调用它的 <a href="http://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent)">onNewIntent()</a> 方法发送意图给这个已存在的实例,而不是创建一个新的实例。一个活动在同一时间只能存在一个实例。</p>
<blockquote><p>注意:虽然活动在一个新的任务里启动,但是后退按钮仍然使用户返回到前一个活动。</p></blockquote>
<p>“singleInstance”</p>
<p>和 “singleTask” 一样,除了系统不会在任务里启动任何其它的活动来持有这个实例。该活动始终是任务里单一且唯一的成员;任何被这个活动启动的活动在一个单独的任务里打开。</p>
<p>再举一个例子,Android 浏览器程序声明网页浏览器的活动应该总是在它自己的任务里打开——通过在 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html"><activity></a> 元素里指定为 <strong>singleTask</strong> 启动模式。这意味着你的应用程序发出一个意图来打开 Android 浏览器时,它的活动不是放置在跟你的应用程序一样的任务里。相反,无论是启动浏览器的新的一个任务还是浏览器有一个运行在后台的任务,那个任务都会被拿到前台来处理这个新的意图。</p>
<p>不管活动是否启动一个新的任务还是启动了它的同一个任务,后退键总是把用户带到前一个活动。然而,如果你启动了一个为 <strong>singleTask</strong> 启动模式的活动,然后如果那个活动在一个后台任务里存在一个实例,那么那整个任务会被带到前台来。此时,回退栈包含所有被拿到前台的活动放在栈顶上。图 4 说明了这种类型的方案。</p>
<p><img src="http://jeffreylyg.github.io/images/2014-5-19/4.png" title="图 4" ></p>
<p>图4:”singleTask”启动模式的活动是如何被添加进回退栈的一个展示。如果活动已经是带有自己回退栈的后台任务的一部分,那么这整个回退栈也会被拿到前台来放在当前任务的顶部。</p>
<p>有关在清单文件中使用启动模式的更多信息,请参阅 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html"><activity></a> 元素的文档,其中有更多关于启动模式属性和可被接受的值的讨论。</p>
<blockquote><p>注:你为活动设置的启动模式属性的行为会被启动活动的带有标志的意图所覆盖,如在下一节讨论的。</p></blockquote>
<h4>使用意图标志</h4>
<p>当启动一个活动,你可以通过传递给 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)">startActivity()</a> 一个带有标志的意图来改变活动和它的任务默认的关联行为。你可以使用的改变默认行为的标志如下:</p>
<p><a href="http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK">FLAG_ACTIVITY_NEW_TASK</a></p>
<p>启动一个新的任务。如果一个任务已经运行你正在启动的活动,那个任务会被带到前台来并带着上次的状态恢复并且这个活动在 <a href="http://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent)">onNewIntent()</a> 方法里接收新的意图。</p>
<p>这将产生和 “singleTask” 启动模式值一样的行为,这在上一节已经讨论过了。</p>
<p><a href="http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_SINGLE_TOP">FLAG_ACTIVITY_SINGLE_TOP</a></p>
<p>如果要启动的活动是当前活动(在回退栈的顶部),那么现有的实例接收 <a href="http://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent)">onNewIntent()</a> 方法的调用,而不是创建这个活动的一个新的实例。</p>
<p>这将产生和 “singleTop” 启动模式值一样的行为,这在上一节已经讨论过了。</p>
<p><a href="http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP">FLAG_ACTIVITY_CLEAR_TOP</a></p>
<p>如果要启动的活动已经在当前任务中运行,然后所有它顶部的其它活动都被销毁并且这个意图被传送来通过 <a href="http://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent)">onNewIntent()</a> 方法来恢复这个活动的实例,而不是创建这个活动的一个新的实例。</p>
<p>在<a href="http://developer.android.com/guide/topics/manifest/activity-element.html#lmode">启动模式</a>属性里没有一个值可以产生这样的的行为。</p>
<p><strong>FLAG_ACTIVITY_CLEAR_TOP</strong> 是最经常与 <strong>FLAG_ACTIVITY_NEW_TASK</strong> 一起使用的。当一起使用时,这些标志是定位另一个任务中存在的活动和把它放在它所能响应的Intent的某个位置的一种方式。</p>
<blockquote><p>注意:如果指定的活动启动模式为 “standard”,它也能从堆栈中删除并且一个新的实例在它的地方启动来处理传人的意图。这是因为对于一个新的意图当启动模式为 “standard” 时一个新的实例总是被创建。</p></blockquote>
<h3>处理亲和性</h3>
<p>亲和性指的是一个活动更倾向于属于哪个任务。默认情况下,同一应用程序的所有活动彼此具有亲和性。所以,默认的,同一应用程序的所有活动倾向于在同一个任务中。然而,你可以改变一个活动的默认亲和性。不同应用程序的活动可以共享一个亲和性,或者同一应用程序中的活动可以被赋值为不同任务的亲和性。</p>
<p>你可以用 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html"><activity></a> 元素的 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html#aff">taskAffinity</a> 属性改变任何已给的活动的亲和性。</p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#aff">taskAffinity</a> 属性需要一个字符串值,它必须唯一的,来自 <manifest> 元素中声明的默认包名,因为系统使用那个名字来识别应用程序的默认任务亲和性。</p>
<p>亲和性在两种情况下发挥作用:</p>
<ul>
<li>当一个启动活动的意图包括 <a href="http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK">FLAG_ACTIVITY_NEW_TASK</a> 标志时。</li>
</ul>
<p>一个新的活动,默认情况下,启动进入这个叫做 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)">startActivity()</a> 活动的任务。它被推送到和调用者一样的回退栈里。然而,如果传给 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)">startActivity()</a> 的意图包含 <a href="http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK">FLAG_ACTIVITY_NEW_TASK</a> 标志的话,系统会寻找一个不同的任务来容纳这个新活动。通常情况下,它是一个新的任务,但并不是必须如此。如果有一个已经存在的任务和这个新活动是一样的亲和性,这个活动会启动进入那个任务。如果不一样的话,它开始一个新的任务。</p>
<p>如果这个标志导致一个活动开始一个新的任务并且用户按了 <em>Home</em> 按钮离开了它,那么必须有某种方式使用户导航返回到这个任务。一些实体(例如通知管理器)总是在一个外部的任务里启动活动,从来不是作为它们自己的一部分,所以它们总是把 FLAG_ACTIVITY_NEW_TASK 标志放进它们传给 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)">startActivity()</a>的意图里。如果你有一个通过一个也许使用了这个标志的外部实体可以调用的活动,注意用户有一种独立的方式来返回那个启动的任务,例如用启动图标(任务的根活动有一个 <a href="http://developer.android.com/reference/android/content/Intent.html#CATEGORY_LAUNCHER">CATEGORY_LAUNCHER</a> 的意图过滤器;见下面的 <a href="http://developer.android.com/guide/components/tasks-and-back-stack.html#Starting">Starting a task</a> 章节)。</p>
<ul>
<li>当一个活动它的 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html#reparent">allowTaskReparenting</a> 属性被设置为 “true”时。</li>
</ul>
<p>这种情况下,当这个活动有一个亲和性的任务进入前台时,该活动可以从它启动的任务移动到这个任务。</p>
<p>例如,假设一个报告选定城市天气情况的活动被定义为一个旅游应用程序的一部分。它在同样的应用程序里和其它的活动有同样的亲和性(默认的应用亲和性)并且允许它用这个属性重新设置。当你其中的一个活动启动天气报告活动时,它最初和你的活动属于同样的任务。然而,当旅游应用程序的任务进入到前台时,天气报告的活动被重新分配到那个任务并在它里面展示。</p>
<blockquote><p>提示:如果一个apk文件从用户的角度包含不止一个“应用程序”,你可能想使用 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html#aff">taskAffinity</a> 属性给和每个“应用程序”相关的活动赋予不同的亲和性。</p></blockquote>
<h3>清除回退栈</h3>
<p>如果用户离开一个任务很长一段时间后,系统将清除这个任务里的所有活动除了根活动。当用户再一次返回这个任务,仅仅只有根活动被恢复。系统这样的行为是因为在一段很长的时间后,用户很可能放弃了他们之前正在做的事情并且返回到这个任务开始一些事情。</p>
<p>有一些活动属性你可以使用来修改这种行为:</p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a></p>
<p>如果这个属性在任务的根活动里设置为 “true”,刚刚描述的默认行为就不会发生。任务会保留在它堆栈里的所有活动即使在一段很长的时间之后。</p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a></p>
<p>如果这个属性在任务的根活动里设置为 “true”,堆栈会清除到只剩根活动无论何时用户离开任务和返回任务。换句话说,它正好与 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a> 相反。用户总是返回到任务的初始状态,即使是离开任务一会儿。</p>
<p><a href="http://developer.android.com/guide/topics/manifest/activity-element.html#finish">finishOnTaskLaunch</a></p>
<p>这个属性有点像 <a href="http://developer.android.com/guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a>,但是它运行在一个单一的活动之上,而不是整个任务。它还可能导致任何活动离开,包括根活动。当它被设置为 “true”时,这个活动只会保留当前会话的部分。如果这个用户离开了然后又回到任务,它不再存在。</p>
<h3>启动任务</h3>
<p>你可以设置一个活动作为一个任务的切入点通过给它一个用 “android.intent.action.MAIN” 作为指定动作和 “android.intent.category.LAUNCHER” 作为指定类别的意图过滤器。例如:</p>