深入理解android中的常用類

 C++ Code 
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
轉:http://www.cnblogs.com/innost/archive/2011/09/06/2168453.html
感謝cnblogs網友的支持。

PDF版的可以從以下網址下載:

http://download.csdn.net/source/3578482

第5章
深入理解常見類
本章涉及的源代碼文件名稱及位置
下面是本章分析的源碼文件名和它的位置。
RefBase.h(framework / base / include / utils / RefBase.h)
RefBase.cpp(framework / base / libs / utils / RefBase.cpp)
Thread.cpp(framework / base / libs / utils / Thread.cpp)
Thread.h(framework / base / include / utils / Thread.h)
Atomic.h(system / core / include / cutils / Atomic.h)
AndroidRuntime.cpp(framework / base / core / jni / AndroidRuntime.cpp)
Looper.java(framework / base / core / Java / Android / os / Looper.java)
Handler.java(framework / base / core / Java / Android / os / Handler.java)
HandlerThread.java(framework / base / core / Java / Android / os / HandlerThread.java)
5.1 概述
初 次接觸Android源碼時,見到最多的一定是sp和wp。即使你只是沉迷於Java世界的編碼,那麼Looper和Handler也是避不開的。本章的 目的,就是把經常碰到的這些內容中的“攔路虎”一網打盡,將它們徹底搞懂。至於弄明白它們有什麼好處,就仁者見仁,智者見智了。個人覺得Looper和 Handler相對會更實用一些。
5.2 以“三板斧”揭祕RefBase、sp和wp
RefBase是Android中所有對象的始 祖,類似於MFC中的CObject及Java中的Object對象。在Android中,RefBase結合sp和wp,實現了一套通過引用計數的方法 來控制對象生命週期的機制。就如我們想像的那樣,這三者的關係非常曖昧。初次接觸Android源碼的人往往會被那個隨處可見的sp和wp搞暈了頭。
什麼是sp和wp呢?其實,sp並不是我開始所想的smart pointer(C++語言中有這個東西),它真實的意思應該是strong pointer,而wp則是weak pointer的意思。我認爲,Android推出這一套機制可能是模仿Java,因爲Java世界中有所謂weak reference之類的東西。sp和wp的目的,就是爲了幫助健忘的程序員回收new出來的內存。
說明我還是喜歡赤裸裸地管理內存的分配和釋放。不過,目前sp和wp的使用已經深入到Android系統的各個角落,想把它去掉真是不太可能了。
這三者的關係比較複雜,都說程咬金的“三板斧”很厲害,那麼我們就借用這三板斧,揭密其間的曖昧關係。
5.2.1 第一板斧—初識影子對象
我們的“三板斧”,其實就是三個例子。相信這三板斧劈下去,你會很容易理解它們。
[-- > 例子1]
//類A從RefBase派生,RefBase是萬物的始祖。
class A:public RefBase
{
    //A沒有任何自己的功能。
}
int main()
{
    A *pA = new A;
    {
        //注意我們的sp、wp對象是在{}中創建的,下面的代碼先創建sp,然後創建wp。
        sp<A> spA(pA);
        wp<A> wpA(spA);
        //大括號結束前,先析構wp,再析構sp。
    }
}
例子夠簡單吧?但也需一步一步分析這斧子是怎麼劈下去的。
1. RefBase和它的影子
類A從RefBase中派生。使用的是RefBase構造函數。代碼如下所示:
[-- > RefBase.cpp]
RefBase::RefBase()
    : mRefs(new weakref_impl(this))//注意這句話
{
    //mRefs是RefBase的成員變量,類型是weakref_impl,我們暫且叫它影子對象。
    //所以A有一個影子對象。
}
mRefs是引用計數管理的關鍵類,需要進一步觀察。它是從RefBase的內部類weakref_type中派生出來的。
先看看它的聲明:
class RefBase::weakref_impl : public RefBase::weakref_type
    //從RefBase的內部類weakref_type派生。
    由於Android頻繁使用C++內部類的方法,所以初次閱讀Android代碼時可能會有點不太習慣,C++的內部類和Java的內部類相似,但有一點不同,即它需要一個顯式的成員指向外部類對象,而Java的內部類對象有一個隱式的成員指向外部類對象的。
    說明 內部類在C++中的學名叫nested class(內嵌類)。
        [-- > RefBase.cpp::weakref_imple構造]
        weakref_impl(RefBase *base)
        : mStrong(INITIAL_STRONG_VALUE) //強引用計數,初始值爲0x1000000。
        , mWeak(0//弱引用計數,初始值爲0。
        , mBase(base)//該影子對象所指向的實際對象。
        , mFlags(0)
        , mStrongRefs(NULL)
        , mWeakRefs(NULL)
        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
        , mRetain(false)
    {
    }
如你所見,new了一個A對象後,其實還new了一個weakref_impl對象,這裏稱它爲影子對象,另外我們稱A爲實際對象。
這裏有一個問題:影子對象有什麼用?
可以仔細想一下,是不是發現影子對象成員中有兩個引用計數?一個強引用,一個弱引用。如果知道引用計數和對象生死有些許關聯的話,就容易想到影子對象的作用了。
說明 按上面的分析來看,在構造一個實際對象的同時,還會悄悄地構造一個影子對象,在嵌入式設備的內存不是很緊俏的今天,這個影子對象的內存佔用已經不成問題了。
2.sp上場
程序繼續運行,現在到了:
sp<A> spA(pA);
請看sp的構造函數,它的代碼如下所示(注意,sp是一個模板類,對此不熟悉的讀者可以去翻翻書,或者乾脆把所有出現的T都換成A):
[-- > RefBase.h::sp(T *other)]
template<typename T>
sp<T>::sp(T *other) //這裏的other就是剛纔創建的pA。
    : m_ptr(other)// sp保存了pA的指針。
{
    if (other) other->incStrong(this);//調用pA的incStrong。
}
OK,戰場轉到RefBase的incStrong中。它的代碼如下所示:
[-- > RefBase.cpp]
void RefBase::incStrong(const void *id) const
{
    //mRefs就是剛纔在RefBase構造函數中new出來的影子對象。
    weakref_impl *const refs = mRefs;

    //操作影子對象,先增加弱引用計數。
    refs->addWeakRef(id);
    refs->incWeak(id);
    ......
    先來看看影子對象的這兩個weak函數都幹了些什麼。
    (1)眼見而心不煩
    下面看看第一個函數addWeakRef,代碼如下所示:
    [-- > RefBase.cpp]
    void addWeakRef(const void * /*id*/) { }
    呵呵,addWeakRef啥都沒做,因爲這是release版走的分支。調試版的代碼我們就不討論了,它是給創造RefBase、 sp,以及wp的人調試用的。
    說明 調試版分支的代碼很多,看來創造它們的人也在爲不理解它們之間的曖昧關係痛苦不已。
    總之,一共有這麼幾個不用考慮的函數,下面都已列出來了。以後再碰見它們,乾脆就直接跳過去:
    void addStrongRef(const void * /*id*/) { }
    void removeStrongRef(const void * /*id*/) { }
    void addWeakRef(const void * /*id*/) { }
    void removeWeakRef(const void * /*id*/) { }
    void printRefs() const { }
    void trackMe(boolbool) { }
    繼續我們的征程。再看incWeak函數,代碼如下所示:
    [-- > RefBase.cpp]
    void RefBase::weakref_type::incWeak(const void * id)
    {
        weakref_impl *const impl = static_cast<weakref_impl *>(this);
        impl->addWeakRef(id);  //上面說了,非調試版什麼都不幹。
        const int32_t c = android_atomic_inc(&impl->mWeak);
        //原子操作,影子對象的弱引用計數加1。
        //千萬記住影子對象的強弱引用計數的值,這是徹底理解sp和wp的關鍵。
    }
    好,我們再回到incStrong,繼續看代碼:
    [-- > RefBase.cpp]
    ......
    //剛纔增加了弱引用計數。
    //再增加強引用計數。
    refs->addStrongRef(id); //非調試版這裏什麼都不幹。
    //下面函數爲原子加1操作,並返回舊值。所以c=0x1000000,而mStrong變爲0x1000001。
    const int32_t c = android_atomic_inc(&refs->mStrong);
    if (c != INITIAL_STRONG_VALUE)
    {
        //如果c不是初始值,則表明這個對象已經被強引用過一次了。
        return;
    }
    //下面這個是原子加操作,相當於執行refs->mStrong +(-0x1000000),最終mStrong=1。
    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    /*
      如果是第一次引用,則調用onFirstRef,這個函數很重要,派生類可以重載這個函數,完成一些
      初始化工作。
    */

    const_cast<RefBase *>(this)->onFirstRef();
}
說明 android_atomic_xxx是Android平臺提供的原子操作函數,原子操作函數是多線程編程中的常見函數,讀者可以學習原子操作函數的相關知識,本章後面也會對其進行介紹。
(2)sp構造的影響
sp構造完後,它給這個世界帶來了什麼?
那就是在RefBase中影子對象的強引用計數變爲1,且弱引用計數也變爲1。
更準確的說法是,sp的出生導致影子對象的強引用計數加1,且弱引用計數也加1。
(3)wp構造的影響
繼續看wp,例子中的調用方式如下:
wp<A> wpA(spA)
wp有好幾個構造函數,原理都一樣。來看這個最常見的:
[-- > RefBase.h::wp(const sp<T> &other)]
template<typename T>
wp<T>::wp(const sp<T> &other)
    : m_ptr(other.m_ptr) //wp的成員變量m_ptr指向實際對象。
{
    if (m_ptr)
    {
        //調用pA的createWeak,並且保存返回值到成員變量m_refs中。
        m_refs = m_ptr->createWeak(this);
    }
}
[-- > RefBase.cpp]
RefBase::weakref_type *RefBase::createWeak(const void *id) const
{
    //調用影子對象的incWeak,這個我們剛纔講過了,它會導致影子對象的弱引用計數增加1。
    mRefs->incWeak(id);
    return mRefs;  //返回影子對象本身。
}
我們可以看到,wp化後,影子對象的弱引用計數將增加1,所以現在弱引用計數爲2,而強引用計數仍爲1。另外,wp中有兩個成員變量,一個保存實際對象,另一個保存影子對象。sp只有一個成員變量,用來保存實際對象,但這個實際對象內部已包含了對應的影子對象。
OK,wp創建完了,現在開始進行wp的析構。
(4)wp析構的影響
wp進入析構函數,則表明它快要離世了,代碼如下所示:
[-- > RefBase.h]
template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this); //調用影子對象的decWeak,由影子對象的基類實現。
}
[-- > RefBase.cpp]
void RefBase::weakref_type::decWeak(const void *id)
{
    //把基類指針轉換成子類(影子對象)的類型,這種做法有些違背面向對象編程的思想。
    weakref_impl *const impl = static_cast<weakref_impl *>(this);
    impl->removeWeakRef(id);//非調試版不做任何事情。

    //原子減1,返回舊值,c=2,而弱引用計數從2變爲1。
    const int32_t c = android_atomic_dec(&impl->mWeak);
    if (c != 1return//c=2,直接返回。

    //如果c爲1,則弱引用計數爲0,這說明沒用弱引用指向實際對象,需要考慮是否釋放內存。
    // OBJECT_LIFETIME_XXX和生命週期有關係,我們後面再說。
    if ((impl->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK)
    {
        if (impl->mStrong == INITIAL_STRONG_VALUE)
            delete impl->mBase;
        else
        {
            delete impl;
        }
    }
    else
    {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags & OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER)
        {
            delete impl->mBase;
        }
    }
}
在例1中,wp析構後,弱引用計數減1。但由於此時強引用計數和弱引用計數仍爲1,所以沒有對象被幹掉,即沒有釋放實際對象和影子對象佔據的內存。
(5)sp析構的影響
下面進入sp的析構。
[-- > RefBase.h]
template<typename T>
sp<T>::~sp()
{
    if (m_ptr) m_ptr->decStrong(this); //調用實際對象的decStrong,由RefBase實現。
}
[-- > RefBase.cpp]
void RefBase::decStrong(const void *id) const
{
    weakref_impl *const refs = mRefs;
    refs->removeStrongRef(id);//調用影子對象的removeStrongRef,啥都不幹。
    //注意,此時強弱引用計數都是1,下面函數調用的結果是c=1,強引用計數爲0。
    const int32_t c = android_atomic_dec(&refs->mStrong);
    if (c == 1)   //對於我們的例子, c爲1
    {
        //調用onLastStrongRef,表明強引用計數減爲0,對象有可能被delete。
        const_cast<RefBase *>(this)->onLastStrongRef(id);
        //mFlags爲0,所以會通過delete this把自己幹掉。
        //注意,此時弱引用計數仍爲1。
        if ((refs->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK)
        {
            delete this;
        }
        ......
    }
    先看delete this的處理,它會導致A的析構函數被調用。再來看A的析構函數,代碼如下所示:
    [-- > 例子1::~A()]
    //A的析構直接導致進入RefBase的析構。
    RefBase::~RefBase()
    {
        if (mRefs->mWeak == 0)   //弱引用計數不爲0,而是1。
        {
            delete mRefs;
        }
    }
    RefBase的delete this自殺行爲沒有把影子對象幹掉,但我們還在decStrong中,可從delete this接着往下看:
    [-- > RefBase.cpp]
    .... //接前面的delete this
    if ((refs->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK)
    {
        delete this;
    }
    //注意,實際數據對象已經被幹掉了,所以mRefs也沒有用了,但是decStrong剛進來
    //的時候就把mRefs保存到refs了,所以這裏的refs指向影子對象。
    refs->removeWeakRef(id);
    refs->decWeak(id);//調用影子對象decWeak
}
[-- > RefBase.cpp]
void RefBase::weakref_type::decWeak(const void *id)
{
    weakref_impl *const impl = static_cast<weakref_impl *>(this);
    impl->removeWeakRef(id);//非調試版不做任何事情。

    //調用前影子對象的弱引用計數爲1,強引用計數爲0,調用結束後c=1,弱引用計數爲0。
    const int32_t c = android_atomic_dec(&impl->mWeak);
    if (c != 1return;

    //這次弱引用計數終於變爲0了,並且mFlags爲0, mStrong也爲0。
    if ((impl->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK)
    {
        if (impl->mStrong == INITIAL_STRONG_VALUE)
            delete impl->mBase;
        else
        {
            delete impl; //impl就是this,把影子對象也就是自己幹掉。
        }
    }
    else
    {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags & OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER)
        {
            delete impl->mBase;
        }
    }
}
好,第一板斧劈下去了!來看看它的結果是什麼。
3.第一板斧的結果
第一板斧過後,來總結一下剛纔所學的知識:
RefBase中有一個隱含的影子對象,該影子對象內部有強弱引用計數。
sp化後,強弱引用計數各增加1,sp析構後,強弱引用計數各減1。
wp化後,弱引用計數增加1,wp析構後,弱引用計數減1。
完全徹底地消滅RefBase對象,包括讓實際對象和影子對象滅亡,這些都是由強弱引用計數控制的,另外還要考慮flag的取值情況。當flag爲0時,可得出如下結論:
強引用爲0將導致實際對象被delete。
弱引用爲0將導致影子對象被delete。
5.2.2 第二板斧—由弱生強
再看第二個例子,代碼如下所示:
[-- > 例子2]
int main()
{
    A *pA = new A();
    wp<A> wpA(pA);
    sp<A> spA = wpA.promote();//通過promote函數,得到一個sp。
}
對A的wp化,不再做分析了。按照前面所講的知識,wp化後僅會使弱引用計數加1,所以此處wp化的結果是:
影子對象的弱引用計數爲1,強引用計數仍然是初始值0x1000000。
wpA的promote函數是從一個弱對象產生一個強對象的重要函數,試看—
1. 由弱生強的方法
代碼如下所示:
[-- > RefBase.h]
template<typename T>
sp<T> wp<T>::promote() const
{
    return sp<T>(m_ptr, m_refs);  //調用sp的構造函數。
}
[-- > RefBase.h]
template<typename T>
sp<T>::sp(T *p, weakref_type *refs)
    : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)//有點看不清楚。
{
    //上面那行代碼夠簡潔,但是不方便閱讀,我們寫成下面這樣:
    /*
      T* pTemp = NULL;
      //關鍵函數attemptIncStrong
      if(p != NULL && refs->attemptIncStrong(this) == true)
          pTemp = p;

      m_ptr = pTemp;
    */

}
2.成敗在此一舉
由弱生強的關鍵函數是attemptIncStrong,它的代碼如下所示:
[-- > RefBase.cpp]
bool RefBase::weakref_type::attemptIncStrong(const void *id)
{
    incWeak(id); //增加弱引用計數,此時弱引用計數變爲2。
    weakref_impl *const impl = static_cast<weakref_impl *>(this);
    int32_t curCount = impl->mStrong; //這個仍是初始值。
    //下面這個循環,在多線程操作同一個對象時可能會循環多次。這裏可以不去管它,
    //它的目的就是使強引用計數增加1。
    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE)
    {
        if (android_atomic_cmpxchg(curCount, curCount + 1, &impl->mStrong) == 0)
        {
            break;
        }
        curCount = impl->mStrong;
    }

    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE)
    {
        bool allow;
        /*
         下面這個allow的判斷極爲精妙。impl的mBase對象就是實際對象,有可能已經被delete了。
         curCount爲0,表示強引用計數肯定經歷了INITIAL_STRONG_VALUE->1->...->0的過程。
         mFlags就是根據標誌來決定是否繼續進行||或&&後的判斷,因爲這些判斷都使用了mBase,
         如不做這些判斷,一旦mBase指向已經回收的地址,你就等着segment fault吧!
         其實,咱們大可不必理會這些東西,因爲它不影響我們的分析和理解。
        */

        if (curCount == INITIAL_STRONG_VALUE)
        {
            allow = (impl->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
                    || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
        }
        else
        {
            allow = (impl->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
                    && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
        }
        if (!allow)
        {
            //allow爲false,表示不允許由弱生強,弱引用計數要減去1,這是因爲咱們進來時加過一次。
            decWeak(id);
            return false//由弱生強失敗。
        }

        //允許由弱生強,強引用計數要增加1,而弱引用計數已經增加過了。
        curCount = android_atomic_inc(&impl->mStrong);
        if (curCount > 0 && curCount < INITIAL_STRONG_VALUE)
        {
            impl->mBase->onLastStrongRef(id);
        }
    }
    impl->addWeakRef(id);
    impl->addStrongRef(id);//兩個函數調用沒有作用。
    if (curCount == INITIAL_STRONG_VALUE)
    {
        //強引用計數變爲1。
        android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
        //調用onFirstRef,通知該對象第一次被強引用。
        impl->mBase->onFirstRef();
    }
    return true//由弱生強成功。
}
3. 第二板斧的結果
promote完成後,相當於增加了一個強引用。根據上面所學的知識可知:
由弱生強成功後,強弱引用計數均增加1。所以現在影子對象的強引用計數爲1,弱引用計數爲2。
5.2.3 第三板斧—破解生死魔咒
1. 延長生命的魔咒
RefBase爲我們提供了一個這樣的函數:
extendObjectLifetime(int32_t mode)
另外還定義了一個枚舉:
enum
{
    OBJECT_LIFETIME_WEAK    =  0x0001,
    OBJECT_LIFETIME_FOREVER = 0x0003
};
注意:FOREVER的值是3,用二進制表示是B11,而WEAK的二進制是B01,也就是說FOREVER包括了WEAK的情況。
上面這兩個枚舉值,是破除強弱引用計數作用的魔咒。先觀察flags爲OBJECT_LIFETIME_ WEAK的情況,見下面的例子。
[-- > 例子3]
class A:public RefBase
{
    public A()
    {
        extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在構造函數中調用。
    }
}
int main()
{
    A *pA = new A();
    wp<A> wpA(pA);//弱引用計數加1。
    {
        sp<A> spA(pA) //sp後,結果是強引用計數爲1,弱引用計數爲2。
    }
    ....
}
sp的析構將直接調用RefBase的decStrong,它的代碼如下所示:
[-- > RefBase.cpp]
void RefBase::decStrong(const void *id) const
{
    weakref_impl *const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = android_atomic_dec(&refs->mStrong);
    if (c == 1)   //上面進行原子操作後,強引用計數爲0
    {
        const_cast<RefBase *>(this)->onLastStrongRef(id);
        //注意這句話。如果flags不是WEAK或FOREVER的話,將delete 數據對象。
        //現在我們的flags是WEAK,所以不會delete 它。
        if ((refs->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK)
        {
            delete this;
        }
    }
    refs->removeWeakRef(id);
    refs->decWeak(id);//調用前弱引用計數是2。
}
然後調用影子對象的decWeak。再來看它的處理,代碼如下所示:
[-- > RefBase.cpp::weakref_type的decWeak()函數]
void RefBase::weakref_type::decWeak(const void *id)
{
    weakref_impl *const impl = static_cast<weakref_impl *>(this);
    impl->removeWeakRef(id);
    const int32_t c = android_atomic_dec(&impl->mWeak);
    if (c != 1return;  //c爲2,弱引用計數爲1,直接返回。
    /*
      假設我們現在到了例子中的wp析構之處,這時也會調用decWeak,在調用上面的原子減操作後
      c=1,弱引用計數變爲0,此時會繼續往下運行。由於mFlags爲WEAK ,所以不滿足if的條件。
    */

    if ((impl->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK)
    {
        if (impl->mStrong == INITIAL_STRONG_VALUE)
            delete impl->mBase;
        else
        {
            delete impl;
        }
    }
    else    //flag爲WEAK,滿足else分支的條件。
    {
        impl->mBase->onLastWeakRef(id);
        /*
         由於 flags值滿足下面這個條件,所以實際對象會被delete,根據前面的分析可知,實際對象的delete會檢查影子對象的弱引用計數,如果它爲0,則會把影子對象也delete掉。
         由於影子對象的弱引用計數此時已經爲0,所以影子對象也會被delete。
        */

        if ((impl->mFlags & OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER)
        {
            delete impl->mBase;
        }
    }
}
2. LIFETIME_WEAK的魔力
看完上面的例子,我們發現什麼了?
在LIFETIME_WEAK的魔法下,強引用計數爲0,而弱引用計數不爲0的時候,實際對象沒有被delete!只有當強引用計數和弱引用計數同時爲0時,實際對象和影子對象纔會被delete。
3. 魔咒大揭祕
至於LIFETIME_FOREVER的破解,就不用再來一斧子了,我直接給出答案:
flags爲0,強引用計數控制實際對象的生命週期,弱引用計數控制影子對象的生命週期。強引用計數爲0後,實際對象被delete。所以對於這種情況,應記住的是,使用wp時要由弱生強,以免收到segment fault信號。
flags爲LIFETIME_WEAK,強引用計數爲0,弱引用計數不爲0時,實際對象不會被delete。當弱引用計數減爲0時,實際對象和影子對象會同時被delete。這是功德圓滿的情況。
flags爲LIFETIME_FOREVER,對象將長生不老,徹底擺脫強弱引用計數的控制。所以你要在適當的時候殺死這些“老妖精”,免得她禍害“人間”。
5.2.4 輕量級的引用計數控制類LightRefBase
上面介紹的RefBase,是一個重量級的引用計數控制類。那麼,究竟有沒有一個簡單些的引用計數控制類呢?Android爲我們提供了一個輕量級的LightRefBase。這個類非常簡單,我們不妨一起來看看。
[-- > RefBase.h]
template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(const void *id) const
    {
        //LightRefBase只有一個引用計數控制量mCount。incStrong的時候使它增加1。
        android_atomic_inc(&mCount);
    }
    inline void decStrong(const void *id) const
    {
        //decStrong的時候減1,當引用計數變爲零的時候,delete掉自己。
        if (android_atomic_dec(&mCount) == 1)
        {
            delete static_cast<const T *>(this);
        }
    }
    inline int32_t getStrongCount() const
    {
        return mCount;
    }

protected:
    inline ~LightRefBase() { }

private:
    mutable volatile int32_t mCount;//引用計數控制變量。
};
LightRefBase類夠簡單吧?不過它是一個模板類,我們該怎麼用它呢?下面給出一個例子,其中類A是從LightRefBase派生的,寫法如下:
class A: public LightRefBase<A> //注意派生的時候要指明是LightRefBase<A>。
{
public:
    A() {};
    ~A() {};
};
另外,我們從LightRefBase的定義中可以知道,它支持sp的控制,因爲它只有incStrong和decStrong函數。
5.2.5 題外話—三板斧的來歷
從 代碼量上看,RefBase、sp和wp的代碼量並不多,但裏面的關係,尤其是flags的引入,曾一度讓我眼花繚亂。當時,我確實很希望能自己調試一下 這些例子,但在設備上調試native代碼,需要花費很大的精力,即使是通過輸出log的方式來調試也需要花很多時間。該怎麼解決這一難題?
既然 它的代碼不多而且簡單,那何不把它移植到臺式機的開發環境下,整一個類似的RefBase呢?有了這樣的構想,我便用上了Visual Studio。至於那些原子操作,Windows平臺上有很直接的InterlockedExchangeXXX與之對應,真的是踏破鐵鞋無覓處,得來全 不費功夫!(在Linux平臺上,不考慮多線程的話,將原子操作換成普通的非原子操作不是也可以嗎?如果更細心更負責任的話,你可以自己用匯編來實現常用 的原子操作,內核代碼中有現成的函數,一看就會明白。)
如果把破解代碼看成是攻城略地的話,我們必須學會靈活多變,而且應力求破解方法日臻極致!
5.3 Thread類及常用同步類分析
Thread類是Android爲線程操作而做的一個封裝。代碼在Thread.cpp中,其中還封裝了一些與線程同步相關的類(既然是封裝,要掌握它,最重要的當然是掌握與Pthread相關的知識)。我們先分析Threa類,進而再介紹與常用同步類相關的知識。
5.3.1 一個變量引發的思考
Thread 類雖說挺簡單,但其構造函數中的那個canCallJava卻一度讓我感到費解。因爲我一直使用的是自己封裝的Pthread類。當發現Thread構造 函數中竟然存在這樣一個東西時,很擔心自己封裝的Pthread類會不會有什麼重大問題,因爲當時我還從來沒考慮過Java方面的問題。
// canCallJava表示這個線程是否會使用JNI函數。爲什麼需要一個這樣的參數呢?
Thread(bool canCallJava = true)。
我們必須得了解它實際創建的線程函數是什麼。Thread類真實的線程是創建在run函數中的。
1.一個變量,兩種處理
先來看一段代碼:
[-- > Thread.cpp]
status_t Thread::run(const char *name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);
    ....
    //如果mCanCallJava爲真,則調用createThreadEtc函數,線程函數是_threadLoop。
    //_threadLoop是Thread.cpp中定義的一個函數。
    if (mCanCallJava)
    {
        res = createThreadEtc(_threadLoop, this, name, priority,
                              stack, &mThread);
    }
    else
    {
        res = androidCreateRawThreadEtc(_threadLoop, this, name, priority,
                                        stack, &mThread);
    }
    上面的mCanCallJava將線程創建函數的邏輯分爲兩個分支,雖傳入的參數都有_threadLoop,但它們調用的函數卻不同。先直接看mCanCallJava爲true的這個分支,代碼如下所示:
    [-- > Thread.h::createThreadEtc()函數]
    inline bool createThreadEtc(thread_func_t entryFunction,
                                void * userData,
                            const char *threadName = “android: unnamed_thread”,
                                int32_t threadPriority = PRIORITY_DEFAULT,
                                size_t threadStackSize = 0,
                                thread_id_t *threadId = 0)
    {
        return androidCreateThreadEtc(entryFunction, userData, threadName,
                                      threadPriority, threadStackSize, threadId) ? true : false;
    }
    它調用的是androidCreateThreadEtc函數,相關代碼如下所示:
    // gCreateThreadFn是函數指針,它在初始化時和mCanCallJava爲false時使用的是同一個
    //線程創建函數。那麼有地方會修改它嗎?
    static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc;
    int androidCreateThreadEtc(android_thread_func_t entryFunction,
                               void * userData, const char * threadName,
                               int32_t threadPriority, size_t threadStackSize,
                               android_thread_id_t * threadId)
    {
        return gCreateThreadFn(entryFunction, userData, threadName,
                               threadPriority, threadStackSize, threadId);
    }
    如果沒有人修改這個函數指針,那麼mCanCallJava就是虛晃一槍,並無什麼作用。不過,代碼中有的地方是會修改這個函數指針的指向的,請看—
    2. zygote偷樑換柱
    在本書4.2.1節的第2點所介紹的AndroidRuntime調用startReg的地方,就有可能修改這個函數指針,其代碼如下所示:
    [-- > AndroidRuntime.cpp]
    /*static*/ int AndroidRuntime::startReg(JNIEnv * env)
    {
        //這裏會修改函數指針爲javaCreateThreadEtc。
        androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
        return 0;
    }
    如果mCanCallJava爲true,則將調用javaCreateThreadEtc。那麼,這個函數有什麼特殊之處呢?來看其代碼,如下所示:
    [-- > AndroidRuntime.cpp]
    int AndroidRuntime::javaCreateThreadEtc(
        android_thread_func_t entryFunction,
        void * userData,
        const char * threadName,
        int32_t threadPriority,
        size_t threadStackSize,
        android_thread_id_t * threadId)
    {
        void **args = (void **) malloc(3 * sizeof(void *));
        int result;
        args[0] = (void *) entryFunction;
        args[1] = userData;
        args[2] = (void *) strdup(threadName);
        //調用的還是androidCreateRawThreadEtc,但線程函數卻換成了javaThreadShell。
        result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,
                                           threadName, threadPriority, threadStackSize, threadId);
        return result;
    }
    [-- > AndroidRuntime.cpp]
    int AndroidRuntime::javaThreadShell(void * args)
    {
        ......
        int result;
        //把這個線程attach到JNI環境中,這樣這個線程就可以調用JNI的函數了。
        if (javaAttachThread(name, &env) != JNI_OK)
            return -1;
        //調用實際的線程函數幹活。
        result = (*(android_thread_func_t)start)(userData);
        //從JNI環境中detach出來。
        javaDetachThread();
        free(name);
        return result;
    }
    3. 費力能討好
    你明白mCanCallJava爲true的目的了嗎?它創建的新線程將:
    在調用你的線程函數之前會attach到 JNI環境中,這樣,你的線程函數就可以無憂無慮地使用JNI函數了。
    線程函數退出後,它會從JNI環境中detach,釋放一些資源。
    注 意 第二點尤其重要,因爲進程退出前,dalvik虛擬機會檢查是否有attach了,如果最後有未detach的線程,則會直接abort(這不是一件 好事)。如果你關閉JNI check選項,就不會做這個檢查,但我覺得,這個檢查和資源釋放有關係,建議還是重視。如果直接使用POSIX的線程創建函數,那麼凡是使用過 attach的,最後就都需要detach!
    Android爲了dalvik的健康真是費盡心機呀。
    4. 線程函數_threadLoop介紹
    無論一分爲二是如何處理的,最終都會調用線程函數_threadLoop,爲什麼不直接調用用戶傳入的線程函數呢?莫非_threadLoop會有什麼暗箱操作嗎?下面我們來看:
    [-- > Thread.cpp]
    int Thread::_threadLoop(void * user)
    {
        Thread *const self = static_cast<Thread *>(user);
        sp<Thread> strong(self->mHoldSelf);
        wp<Thread> weak(strong);
        self->mHoldSelf.clear();

#if HAVE_ANDROID_OS
        self->mTid = gettid();
#endif

        bool first = true;

        do
        {
            bool result;
            if (first)
            {
                first = false;
                //self代表繼承Thread類的對象,第一次進來時將調用readyToRun,看看是否準備好。
                self->mStatus = self->readyToRun();
                result = (self->mStatus == NO_ERROR);

                if (result && !self->mExitPending)
                {
                    result = self->threadLoop();
                }
            }
            else
            {
                /*
                調用子類實現的threadLoop函數,注意這段代碼運行在一個do-while循環中。
                  這表示即使我們的threadLoop返回了,線程也不一定會退出。
                */

                result = self->threadLoop();
            }
            /*
             線程退出的條件:
             1)result 爲false。這表明,如果子類在threadLoop中返回false,線程就可以
             退出。這屬於主動退出的情況,是threadLoop自己不想繼續幹活了,所以返回false。
             讀者在自己的代碼中千萬別寫錯threadLoop的返回值。
             2)mExitPending爲true,這個變量可由Thread類的requestExit函數設置,這種
             情況屬於被動退出,因爲由外界強制設置了退出條件。
            */

            if (result == false || self->mExitPending)
            {
                self->mExitPending = true;
                self->mLock.lock();
                self->mRunning = false;
                self->mThreadExitedCondition.broadcast();
                self->mLock.unlock();
                break;
            }
            strong.clear();
            strong = weak.promote();
        }
        while(strong != 0);

        return 0;
    }
    關於_threadLoop,我們就介紹到這裏。請讀者務必注意下面一點:
    threadLoop運行在一個循環中,它的返回值可以決定是否退出線程。
    5.3.2 常用同步類
    同 步,是多線程編程中不可迴避的話題,同時也是一個非常複雜的問題。這裏只簡單介紹一下Android提供的同步類。這些類,只對系統提供的多線程同步函數 (這種函數我們稱爲Raw API)進行了面向對象的封裝,讀者必須先理解Raw API,然後才能真正掌握其具體用法。
    提示 要了解 Windows下的多線程編程,有很多參考資料,而有關Linux下完整系統闡述多線程編程的書籍目前較少,這裏推薦一本含金量較高的著作 《Programming with POSIX Thread》(本書只有英文版,由Addison - Wesley出版)。
    Android提供了兩個封裝好的同步類,它們是Mutex和Condition。這是重量級的同步技術,一般內核都會有對應的支持。另外,OS還提供了簡單的原子操作,這些也算是同步技術中的一種。下面分別來介紹這三種東西。
    1. 互斥類—Mutex
    Mutex 是互斥類,用於多線程訪問同一個資源的時候,保證一次只有一個線程能訪問該資源。在《Windows核心編程》①一書中,對於這種互斥訪問有一個很形象的 比喻:想象你在飛機上如廁,這時衛生間的信息牌上顯示“有人”,你必須等裏面的人出來後纔可進去。這就是互斥的含義。
    下面來看Mutex的實現方式,它們都很簡單。
    (1)Mutex介紹
    其代碼如下所示:
    [-- > Thread.h::Mutex的聲明和實現]
    inline Mutex::Mutex(int type, const char * name)
    {
        if (type == SHARED)
        {
            //type如果是SHARED,則表明這個Mutex支持跨進程的線程同步。
            //以後我們在Audio系統和Surface系統中會經常見到這種用法。
            pthread_mutexattr_t attr;
            pthread_mutexattr_init(&attr);
            pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
            pthread_mutex_init(&mMutex, &attr);
            pthread_mutexattr_destroy(&attr);
        }
        else
        {
            pthread_mutex_init(&mMutex, NULL);
        }
    }
    inline Mutex::~Mutex()
    {
        pthread_mutex_destroy(&mMutex);
    }
    inline status_t Mutex::lock()
    {
        return -pthread_mutex_lock(&mMutex);
    }
    inline void Mutex::unlock()
    {
        pthread_mutex_unlock(&mMutex);
    }
    inline status_t Mutex::tryLock()
    {
        return -pthread_mutex_trylock(&mMutex);
    }
    關於Mutex的使用,除了初始化外,最重要的是lock和unlock函數的使用,它們的用法如下:
    要想獨佔衛生間,必須先調用Mutex的lock函數。這樣,這個區域就被鎖住了。如果這塊區域之前已被別人鎖住,lock函數則會等待,直到可以進入這塊區域爲止。系統保證一次只有一個線程能lock成功。
    當你“方便”完畢,記得調用Mutex的unlock以釋放互斥區域。這樣,其他人的lock纔可以成功返回。
    另外,Mutex還提供了一個trylock函數,該函數只是嘗試去鎖住該區域,使用者需要根據trylock的返回值來判斷是否成功鎖住了該區域。
    注意 以上這些內容都和Raw API有關,不瞭解它的讀者可自行學習相關知識。在Android系統中,多線程也是常見和重要的編程手段,務必請大家重視。
    Mutex類確實比Raw API方便好用,不過還是稍顯麻煩。
    (2)AutoLock介紹
    AutoLock類是定義在Mutex內部的一個類,它其實是一幫“懶人”搞出來的,爲什麼這麼說呢?先來看看使用Mutex有多麻煩:
    顯示調用Mutex的lock。
    在某個時候記住要調用該Mutex的unlock。
    以 上這些操作都必須一一對應,否則會出現“死鎖”!在有些代碼中,如果判斷分支特別多,你會發現unlock這句代碼被寫得比比皆是,如果稍有不慎,在某處 就會忘了寫它。有什麼好辦法能解決這個問題嗎?終於有人想出來一個好辦法,就是充分利用了C++的構造和析構函數,只需看一看AutoLock的定義就會 明白。代碼如下所示:
    [-- > Thread.h Mutex::Autolock聲明和實現]
    class Autolock
    {
    public:
        //構造的時候調用lock。
        inline Autolock(Mutex &mutex) : mLock(mutex)
        {
            mLock.lock();
        }
        inline Autolock(Mutex *mutex) : mLock(*mutex)
        {
            mLock.lock();
        }
        //析構的時候調用unlock。
        inline ~Autolock()
        {
            mLock.unlock();
        }
    private:
        Mutex &mLock;
    };
    AutoLock的用法很簡單:
    先定義一個Mutex,如 Mutex xlock。
    在使用xlock的地方,定義一個AutoLock,如 AutoLock autoLock(xlock)。
    由於C++對象的構造和析構函數都是自動被調用的,所以在AutoLock的生命週期內,xlock的lock和unlock也就自動被調用了,這樣就省去了重複書寫unlock的麻煩,而且lock和unlock的調用肯定是一一對應的,這樣就絕對不會出錯。
    2. 條件類—Condition
    多線程同步中的條件類對應的是下面這種使用場景:
    線程A做初始化工作,而其他線程比如線程B、C必須等到初始化工作完後才能工作,即線程B、C在等待一個條件,我們稱B、C爲等待者。
    當線程A完成初始化工作時,會觸發這個條件,那麼等待者B、C就會被喚醒。觸發這個條件的A就是觸發者。
    上面的使用場景非常形象,而且條件類提供的函數也非常形象,它的代碼如下所示:
    [-- > Thread.h:: Condition的聲明和實現]
    class Condition
    {
    public:
        enum
        {
            PRIVATE = 0,
            SHARED = 1
        };

        Condition();
        Condition(int type);//如果type是SHARED,表示支持跨進程的條件同步
        ~Condition();
        //線程B和C等待事件,wait這個名字是不是很形象呢?
        status_t wait(Mutex &mutex);
        //線程B和C的超時等待,B和C可以指定等待時間,當超過這個時間,條件卻還不滿足,則退出等待。
        status_t waitRelative(Mutex &mutex, nsecs_t reltime);
        //觸發者A用來通知條件已經滿足,但是B和C只有一個會被喚醒。
        void signal();
        //觸發者A用來通知條件已經滿足,所有等待者都會被喚醒。
        void broadcast();

    private:
#if defined(HAVE_PTHREADS)
        pthread_cond_t mCond;
#else
        void   *mState;
#endif
    }
    聲明很簡單,定義也很簡單,代碼如下所示:
    inline Condition::Condition()
    {
        pthread_cond_init(&mCond, NULL);
    }
    inline Condition::Condition(int type)
    {
        if (type == SHARED)  //設置跨進程的同步支持。
        {
            pthread_condattr_t attr;
            pthread_condattr_init(&attr);
            pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
            pthread_cond_init(&mCond, &attr);
            pthread_condattr_destroy(&attr);
        }
        else
        {
            pthread_cond_init(&mCond, NULL);
        }
    }
    inline Condition::~Condition()
    {
        pthread_cond_destroy(&mCond);
    }
    inline status_t Condition::wait(Mutex & mutex)
    {
        return -pthread_cond_wait(&mCond, &mutex.mMutex);
    }
    inline status_t Condition::waitRelative(Mutex & mutex, nsecs_t reltime)
    {
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
        struct timespec ts;
        ts.tv_sec  = reltime / 1000000000;
        ts.tv_nsec = reltime % 1000000000;
        return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);
        ...... //有些系統沒有實現POSIX的相關函數,所以不同的系統需要調用不同的函數。
#endif
    }
    inline void Condition::signal()
    {
        pthread_cond_signal(&mCond);
    }
    inline void Condition::broadcast()
    {
        pthread_cond_broadcast(&mCond);
    }
    可以看出,Condition的實現全是憑藉調用了Raw API的pthread_cond_xxx函數。這裏要重點說明的是,Condition類必須配合Mutex來使用。什麼意思?
    在上面的代碼中,不論是wait、waitRelative、signal還是broadcast的調用,都放在一個Mutex的lock和unlock範圍中,尤其是wait和waitRelative函數的調用,這是強制性的。
    來看一個實際的例子,加深一下對Condition類和Mutex類的印象。這個例子是Thread類的requestExitAndWait,目的是等待工作線程退出,代碼如下所示:
    [-- > Thread.cpp]
    status_t Thread::requestExitAndWait()
    {
        ......
        requestExit(); //設置退出變量mExitPending爲true。
        Mutex::Autolock _l(mLock);//使用Autolock,mLock被鎖住。
        while (mRunning == true)
        {
            /*
             條件變量的等待,這裏爲什麼要通過while循環來反覆檢測mRunning?
             因爲某些時候即使條件類沒有被觸發,wait也會返回。關於這個問題,強烈建議讀者閱讀
             前面推薦的《Programming with POSIX Thread》一書。
            */

            mThreadExitedCondition.wait(mLock);
        }

        mExitPending = false;
        //退出前,局部變量Mutex::Autolock _l的析構會被調用,unlock也就會被自動調用。
        return mStatus;
    }
    那麼,什麼時候會觸發這個條件呢?是在工作線程退出前。其代碼如下所示:
    [-- > Thread.cpp]
    int Thread::_threadLoop(void * user)
    {
        Thread *const self = static_cast<Thread *>(user);
        sp<Thread> strong(self->mHoldSelf);
        wp<Thread> weak(strong);
        self->mHoldSelf.clear();

        do
        {
            ......
            result = self->threadLoop();//調用子類的threadLoop函數。
            ......
            //如果mExitPending爲true,則退出。
            if (result == false || self->mExitPending)
            {
                self->mExitPending = true;
                //退出前觸發條件變量,喚醒等待者。
                self->mLock.lock();//lock鎖住。
                //mRunning的修改位於鎖的保護中。如果你閱讀了前面推薦的書,這裏也就不難理解了。
                self->mRunning = false;
                self->mThreadExitedCondition.broadcast();
                self->mLock.unlock();//釋放鎖。
                break;//退出循環,此後該線程函數會退出。
            }
            ......
        }
        while(strong != 0);

        return 0;
    }
    關於Android多線程的同步類,暫時介紹到此吧。當然,這些類背後所隱含的知識及技術是讀者需要倍加重視的。
    提 示 希望我們能養成一種由點及面的學習方法。以我們的同步類爲例,假設你是第一次接觸多線程編程,也學會了如何使用Mutex和Condition這兩個 類,不妨以這兩個類代碼中所傳遞的知識作爲切入點,把和多線程相關的所有知識(這個知識不僅僅是函數的使用,還包括多線程的原理,多線程的編程模型,甚至 是現在很熱門的並行多核編程)普遍瞭解一下。只有深刻理解並掌握了原理等基礎和框架性的知識後,才能以不變應萬變,才能做到遊刃有餘。
    3. 原子操作函數介紹
    什麼是原子操作?所謂原子操作,就是該操作絕不會在執行完畢前被任何其他任務或事件打斷,也就說,原子操作是最小的執行單位。
    上面這句話放到代碼中是什麼意思?請看一個例子:
    [-- > 例子]
    static int g_flag = 0//全局變量g_flag
    static Mutex  lock  ;//全局的鎖
    //線程1執行thread1。
    void thread1()
    {
        //g_flag遞減,每次操作前鎖住。
        lock.lock();
        g_flag--;
        lock.unlock();
    }
    //線程2中執行thread2函數。
    void thread2()
    {
        lock.lock();
        g_flag++; //線程2對g_flag進行遞增操作,每次操作前要取得鎖。
        lock.unlock();
    }
    爲什麼需要Mutex來幫忙呢?因爲g_flags++或g_flags--操作都不是原子操作。從彙編指令的角度看,C / C++中的一條語句對應了數條彙編指令。以g_flags++操作爲例,它生成的彙編指令可能就是以下三條:
    從內存中取數據到寄存器。
    對寄存器中的數據進行遞增操作,結果還在寄存器中。
    寄存器的結果寫回內存。
    這 三條彙編指令,如果按正常的順序連續執行是沒有問題的,但在多線程時就不能保證了。例如,線程1在執行第一條指令後,線程2由於調度的原因,搶在線程1之 前連續執行完了三條指令。這樣,線程1繼續執行指令時,它所使用的值就不是線程2更新後的值,而是之前的舊值。再對這個值進行操作便沒有意義了。
    在一般情況下,處理這種問題可以使用Mutex來加鎖保護,但Mutex的使用方法比它所要保護的內容還要複雜,例如,鎖的使用將導致從用戶態轉入內核態,有較大的浪費。那麼,有沒有簡便些的辦法讓這些加、減等操作不被中斷呢?
    答案是肯定的,但這需要CPU的支持。在X86平臺上,一個遞增操作可以用下面的內嵌彙編語句來實現:
#define LOCK "lock;"
    INT32 InterlockedIncrement(INT32 * lpAddend)
    {
        /*
         這是我們在Linux平臺上實現Windows API時使用的方法。
         其中在SMP系統上,LOCK定義成"lock;"表示鎖總線,這樣同一時刻就只能有一個CPU訪問總線了。
         非SMP系統,LOCK定義成空。由於InterlockedIncrement要返回遞增前的舊值,所以我們
         使用了xaddl指令,它先交換源和目的的操作數,再進行遞增操作。
        */

        INT32 i = 1;
        __asm__ __volatile__(
            LOCK "xaddl %0, %1"
            :"+r" (i), "+m" (*lpAddend)
            : : "memory");
        return *lpAddend;
    }
    Android提供了相關的原子操作函數。這裏有必要介紹一下各個函數的作用。
    [-- > Atomic.h],注意該文件位於system / core / include / cutils目錄中。
    //原子賦值操作,結果是*addr=value。
    void android_atomic_write(int32_t value, volatile int32_t * addr);
    //下面所有函數的返回值都是操作前的舊值。
    //原子加1和原子減1。
    int32_t android_atomic_inc(volatile int32_t * addr);
    int32_t android_atomic_dec(volatile int32_t * addr);
    //原子加法操作,value爲被加數。
    int32_t android_atomic_add(int32_t value, volatile int32_t * addr);
    //原子“與”和“或”操作。
    int32_t android_atomic_and(int32_t value, volatile int32_t * addr);
    int32_t android_atomic_or(int32_t value, volatile int32_t * addr);
    /*
    條件交換的原子操作。只有在oldValue等於*addr時,纔會把newValue賦值給*addr。
    這個函數的返回值須特別注意。返回值非零,表示沒有進行賦值操作。返回值爲零,表示
    進行了原子操作。
    */

    int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue,
                               volatile int32_t * addr);
    有興趣的話,讀者可以對上述函數的實現進行深入研究,其中:
    X86平臺的實現在system / core / libcutils / Atomic.c中,注意其代碼在#elif defined(__i386__) || defined(__x86_64__)所包括的代碼段內。
    ARM平臺的實現在system / core / libcutils / atomic - android - arm.S彙編文件中。
    原子操作的最大好處在於避免了鎖的使用,這對整個程序運行效率的提高有很大幫助。目前,在多核並行編程中,最高境界就是完全不使用鎖。當然,它的難度可想而知是巨大的。
    5.4 Looper和Handler類分析
    就應用程序而言,Android系統中Java的應用程序和其他系統上相同,都是靠消息驅動來工作的,它們大致的工作原理如下:
    有一個消息隊列,可以往這個消息隊列中投遞消息。
    有一個消息循環,不斷從消息隊列中取出消息,然後處理。
    我們用圖5 - 1來展示這個工作過程:

    圖5 - 1 線程和消息處理的原理圖
    從圖中可以看出:
    事件源把待處理的消息加入到消息隊列中,一般是加至隊列尾,一些優先級高的消息也可以加至隊列頭。事件源提交的消息可以是按鍵、觸摸屏等物理事件產生的消息,也可以是系統或應用程序本身發出的請求消息。
    處理線程不斷從消息隊列頭中取出消息並處理,事件源可以把優先級高的消息放到隊列頭,這樣,優先級高的消息就會首先被處理。
    在Android系統中,這些工作主要由Looper和Handler來實現:
    Looper類,用於封裝消息循環,並且有一個消息隊列。
    Handler類,有點像輔助類,它封裝了消息投遞、消息處理等接口。
    Looper類是其中的關鍵。先來看看它是怎麼做的。
    5.4.1 Looper類分析
    我們以Looper使用的一個常見例子來分析這個Looper類。
    [-- > 例子1]
    //定義一個LooperThread。
    class LooperThread extends Thread
    {
        public Handler mHandler;
        public void run()
        {
            //① 調用prepare。
            Looper.prepare();
            ......
            //② 進入消息循環。
            Looper.loop();
        }
    }
    //應用程序使用LooperThread。
    {
        ......
        new LooperThread().start();//啓動新線程,線程函數是run
    }
    上面的代碼一共有兩個關鍵調用(即①和②),我們對其逐一進行分析。
    1. 準備好了嗎
    第一個調用函數是Looper的prepare函數。它會做什麼工作呢?其代碼如下所示:
    [-- > Looper.java]
    public static final void prepare()
    {
        //一個Looper只能調用一次prepare。
        if (sThreadLocal.get() != null)
        {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //構造一個Looper對象,設置到調用線程的局部變量中。
        sThreadLocal.set(new Looper());
    }
    //sThreadLocal定義
    private static final ThreadLocal sThreadLocal = new ThreadLocal();
    ThreadLocal是Java中的線程局部變量類,全名應該是Thread Local Variable。我覺得它的實現和操作系統提供的線程本地存儲(TLS)有關係。總之,該類有兩個關鍵函數:
    set:設置調用線程的局部變量。
    get:獲取調用線程的局部變量。
    注意 set / get的結果都和調用這個函數的線程有關。ThreadLocal類可參考JDK API文檔或Android API文檔。
    根據上面的分析可知,prepare會在調用線程的局部變量中設置一個Looper對象。這個調用線程就是LooperThread的run線程。先看看Looper對象的構造,其代碼如下所示:
    [-- > Looper.java]
    private Looper()
    {
        //構造一個消息隊列。
        mQueue = new MessageQueue();
        mRun = true;
        //得到當前線程的Thread對象。
        mThread = Thread.currentThread();
    }
    prepare函數很簡單,它主要乾了一件事:
    在調用prepare的線程中,設置了一個Looper對象,這個Looper對象就保存在這個調用線程的TLV中。而Looper對象內部封裝了一個消息隊列。
    也就是說,prepare函數通過ThreadLocal機制,巧妙地把Looper和調用線程關聯在一起了。要了解這樣做的目的是什麼,需要再看第二個重要函數。
    2.Looper循環
    代碼如下所示:
    [-- > Looper.java]
    public static final void loop()
    {
        Looper me = myLooper();//myLooper返回保存在調用線程TLV中的Looper對象。
        //取出這個Looper的消息隊列。
        MessageQueue queue = me.mQueue;
        while (true)
        {
            Message msg = queue.next();
            //處理消息,Message對象中有一個target,它是Handler類型。
            //如果target爲空,則表示需要退出消息循環。
            if (msg != null)
            {
                if (msg.target == null)
                {
                    return;
                }
                //調用該消息的Handler,交給它的dispatchMessage函數處理。
                msg.target.dispatchMessage(msg);
                msg.recycle();
            }
        }
    }
    //myLooper函數返回調用線程的線程局部變量,也就是存儲在其中的Looper對象。
    public static final Looper myLooper()
    {
        return (Looper)sThreadLocal.get();
    }
    通過上面的分析會發現,Looper的作用是:
    封裝了一個消息隊列。
    Looper的prepare函數把這個Looper和調用prepare的線程(也就是最終的處理線程)綁定在一起了。
    處理線程調用loop函數,處理來自該消息隊列的消息。
    當事件源向這個Looper發送消息的時候,其實是把消息加到這個Looper的消息隊列裏了。那麼,該消息就將由和Looper綁定的處理線程來處理。可事件源又是怎麼向Looper消息隊列添加消息的呢?來看下一節。
    3.Looper、Message和Handler的關係
    Looper、Message和Handler之間也存在曖昧關係,不過要比RefBase那三個簡單得多,用兩句話就可以說清楚:
    Looper中有一個Message隊列,裏面存儲的是一個個待處理的Message。
    Message中有一個Handler,這個Handler是用來處理Message的。
    其中,Handler類封裝了很多瑣碎的工作。先來認識一下這個Handler。
    5.4.2 Handler分析
    1.初識Handler
    Handler中所包括的成員:
    [-- > Handler.java]
    final MessageQueue mQueue;//Handler中也有一個消息隊列。
    final Looper mLooper;//也有一個Looper。
    final Callback mCallback;//有一個回調用的類。
    這幾個成員變量是怎麼使用的呢?這首先得分析Handler的構造函數。Handler一共有四個構造函數,它們主要的區別是在對上面三個重要成員變量的初始化上。我們試對其進行逐一的分析。
    [-- > Handler.java]
    //構造函數1
    public Handler()
    {
        //獲得調用線程的Looper。
        mLooper = Looper.myLooper();
        if (mLooper == null)
        {
            throw new RuntimeException(......);
        }
        //得到Looper的消息隊列。
        mQueue = mLooper.mQueue;
        //無callback設置。
        mCallback = null;
    }

    //構造函數2
    public Handler(Callback callback)
    {
        mLooper = Looper.myLooper();
        if (mLooper == null)
        {
            throw new RuntimeException(......);
        }
        //和構造函數1類似,只不過多了一個設置callback。
        mQueue = mLooper.mQueue;
        mCallback = callback;
    }
    //構造函數3
    public Handler(Looper looper)
    {
        mLooper = looper; //looper由外部傳入,是哪個線程的Looper不確定。
        mQueue = looper.mQueue;
        mCallback = null;
    }
    //構造函數4,和構造函數3類似,只不過多了callback設置。
    public Handler(Looper looper, Callback callback)
    {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
    }
    在上述構造函數中,Handler中的消息隊列變量最終都會指向Looper的消息隊列,Handler爲何要如此做?
    2. Handler的真面目
    根據前面的分析可知,Handler中的消息隊列實際就是某個Looper的消息隊列,那麼,Handler如此安排的目的何在?
    在回答這個問題之前,我先來問一個問題:
    怎麼往Looper的消息隊列插入消息?
    如果不知道Handler,這裏有一個很原始的方法可解決上面這個問題:
    調用Looper的myQueue,它將返回消息隊列對象MessageQueue。
    構造一個Message,填充它的成員,尤其是target變量。
    調用MessageQueue的enqueueMessage,將消息插入消息隊列。
    這種原始方法的確很麻煩,且極容易出錯。但有了Handler後,我們的工作就變得異常簡單了。Handler更像一個輔助類,幫助我們簡化編程的工作。
    (1)Handler和Message
    Handler提供了一系列函數,幫助我們完成創建消息和插入消息隊列的工作。這裏只列舉其中一二。要掌握詳細的API,則需要查看相關的文檔。
    //查看消息隊列中是否有消息碼是what的消息。
    final boolean  hasMessages(int what)
    //從Handler中創建一個消息碼是what的消息。
    final Message  obtainMessage(int what)
    //從消息隊列中移除消息碼是what的消息。
    final void    removeMessages(int what)
    //發送一個只填充了消息碼的消息。
    final boolean  sendEmptyMessage(int what)
    //發送一個消息,該消息添加到隊列尾。
    final boolean  sendMessage(Message msg)
    //發送一個消息,該消息添加到隊列頭,所以優先級很高。
    final boolean  sendMessageAtFrontOfQueue(Message msg)
    只需對上面這些函數稍作分析,就能明白其他的函數。現以sendMessage爲例,其代碼如下所示:
    [-- > Handler.java]
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0); //調用sendMessageDelayed
    }
    [-- > Handler.java]
    // delayMillis是以當前調用時間爲基礎的相對時間
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0)
        {
            delayMillis = 0;
        }
        //調用sendMessageAtTime,把當前時間算上
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    [-- > Handler.java]
    //uptimeMillis 是絕對時間,即sendMessageAtTime函數處理的是絕對時間
    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null)
        {
            //把Message的target設置爲自己,然後加入到消息隊列中
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        return sent;
    }
    看到上面這些函數我們可以預見,如果沒有Handler的輔助,當我們自己操作MessageQueue的enqueueMessage時,得花費多大工夫!
    Handler把Message的target設爲自己,是因爲Handler除了封裝消息添加等功能外還封裝了消息處理的接口。
    (2)Handler的消息處理
    剛纔,我們往Looper的消息隊列中加入了一個消息,按照Looper的處理規則,它在獲取消息後會調用target的dispatchMessage函數,再把這個消息派發給Handler處理。Handler在這塊是如何處理消息的呢?
    [-- > Handler.java]
    public void dispatchMessage(Message msg)
    {
        //如果Message本身有callback,則直接交給Message的callback處理
        if (msg.callback != null)
        {
            handleCallback(msg);
        }
        else
        {
            //如果本Handler設置了mCallback,則交給mCallback處理
            if (mCallback != null)
            {
                if (mCallback.handleMessage(msg))
                {
                    return;
                }
            }
            //最後纔是交給子類處理
            handleMessage(msg);
        }
    }
    dispatchMessage定義了一套消息處理的優先級機制,它們分別是:
    Message如果自帶了callback處理,則交給callback處理。
    Handler如果設置了全局的mCallback,則交給mCallback處理。
    如果上述都沒有,該消息則會被交給Handler子類實現的handleMessage來處理。當然,這需要從Handler派生並重載handleMessage函數。
    在通常情況下,我們一般都是採用第三種方法,即在子類中通過重載handleMessage來完成處理工作的。
    至此,Handler知識基本上講解完了,可是在實際編碼過程中還有一個重要問題需要警惕,下一節內容就會談及此問題。
    5.4.3 Looper和Handler的同步關係
    Looper和Handler會有什麼同步關係呢?它們之間確實有同步關係,而且如果不注意此關係,定會鑄成大錯!
    同步關係肯定與多線程有關,我們來看下面的一個例子:
    [-- > 例子2]
    //先定義一個LooperThread類
    class LooperThread extends Thread
    {
        public Looper myLooper = null;//定義一個public的成員myLooper,初值爲空。
        public void run()   //假設run在線程2中執行
        {
            Looper.prepare();
            // myLooper必須在這個線程中賦值
            myLooper = Looper.myLooper();
            Looper.loop();
        }
    }
    //下面這段代碼在線程1中執行,並且會創建線程2
    {
        LooperThread lpThread = new LooperThread;
        lpThread.start();//start後會創建線程2
        Looper looper = lpThread.myLooper;//<======注意
        // thread2Handler和線程2的Looper掛上鉤
        Handler thread2Handler = new Handler(looper);
        //sendMessage發送的消息將由線程2處理
        threadHandler.sendMessage(...)
    }
    上面這段代碼的目的很簡單:
    線程1中創建線程2,並且線程2通過Looper處理消息。
    線程1中得到線程2的Looper,並且根據這個Looper創建一個Handler,這樣發送給該Handler的消息將由線程2處理。
    但 很可惜,上面的代碼是有問題的。如果我們熟悉多線程,就會發現標有“注意”的那行代碼存在着嚴重問題。myLooper的創建是在線程2中,而 looper的賦值在線程1中,很有可能此時線程2的run函數還沒來得及給myLooper賦值,這樣線程1中的looper將取到myLooper的 初值,也就是looper等於null。另外,
    Handler thread2Handler = new Handler(looper) 不能替換成
    Handler thread2Handler = new Handler(Looper.myLooper())
    這是因爲,myLooper返回的是調用線程的Looper,即Thread1的Looper,而不是我們想要的Thread2的Looper。
    對這個問題,可以採用同步的方式進行處理。你是不是有點迫不及待地想完善這個例子了?其實Android早就替我們想好了,它提供了一個HandlerThread來解決這個問題。
    5.4.4 HandlerThread介紹
    HandlerThread完美地解決了myLooper可能爲空的問題。下面來看看它是怎麼做的,代碼如下所示:
    [-- > HandlerThread]
    public class HandlerThread extends Thread
    {
        //線程1調用getLooper來獲得新線程的Looper
        public Looper getLooper()
        {
            ......
            synchronized (this)
            {
                while (isAlive() && mLooper == null)
                {
                    try
                    {
                        wait();//如果新線程還未創建Looper,則等待
                    }
                    catch (InterruptedException e)
                    {
                    }
                }
            }
            return mLooper;
        }

        //線程2運行它的run函數,looper就是在run線程裏創建的。
        public void run()
        {
            mTid = Process.myTid();
            Looper.prepare();  //創建這個線程上的Looper
            synchronized (this)
            {
                mLooper = Looper.myLooper();
                notifyAll();//通知取Looper的線程1,此時Looper已經創建好了。
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
    }
    HandlerThread很簡單,小小的wait / notifyAll就解決了我們的難題。爲了避免重複發明輪子,我們還是多用HandlerThread類吧!
    5.5 本章小結
    本 章主要分析了Android代碼中最常見的幾個類:其中在Native層包括與對象生命週期相關的RefBase、sp、wp、LightRefBase 類,以及Android爲多線程編程提供的Thread類和相關的同步類;Java層則包括使用最爲廣泛的Handler類和Looper類。另外,還分析了類HandlerThread,它降低了創建和使用帶有消息隊列的線程的難度。

    謝謝大家對《深入理解android 卷I》的支持。
發佈了50 篇原創文章 · 獲贊 10 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章