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(bool, bool) { } 繼續我們的征程。再看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 != 1) return; //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 != 1) return; //這次弱引用計數終於變爲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 != 1) return; //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》的支持。 |
深入理解android中的常用類
C++ Code
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.