在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Android/ 第5章 深入理解常見類
第4章 ?深入理解 Zygote
第10章 深入理解MediaScanner
第3章 ?深入理解init
第8章 ?深入理解Surface系統(tǒng)
第5章 深入理解常見類
第7章 ?深入理解Audio系統(tǒng)
第一章 ?閱讀前的準備工作
<span>第6章 深入理解Binder</span>
第9章 ?深入理解Vold和Rild
第2章? 深入理解JNI

第5章 深入理解常見類

本章主要內(nèi)容

·? 分析RefBase、sp,wp和LightRefBase類。

·? 分析Native的Thread類和常用同步類。

·? 分析Java層的Handler、Looper,以及HandlerThread類。

本章涉及的源代碼文件名稱及位置

下面是我們本章分析的源碼文件名和它的位置。

·? 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也是避不開的。本章的目的,就是把經(jīng)常碰見的這些內(nèi)容中的“攔路虎”一網(wǎng)打盡,將它們徹底搞懂。至于弄明白它們有什么好處,就是仁者見仁,智者見智了。我個人覺得,可能Looper和Handler會相對更實用一些。

5.2 ?以“三板斧”揭秘RefBase、sp和wp

RefBase是Android中所有對象的始祖,類似MFC中的CObject及Java中的Object對象。在Android中,RefBase結合sp和wp,實現(xiàn)了一套通過引用計數(shù)的方法來控制對象生命周期的機制。就如我們想像的那樣,這三者的關系非常曖昧。初次接觸Android源碼的人往往會被那個隨處可見的sp和wp搞暈了頭。

什么是sp和wp呢?其實,sp并不是我開始所想的smart pointer(C++語言中有這個東西),它真實的意思應該是strong pointer,而wp是weak pointer的意思。我認為,Android推出這一套機制可能是模仿Java,因為Java世界中有所謂weak reference之類的東西。sp和wp的目的,就是為了幫助健忘的程序員回收new出來的內(nèi)存。

我還是喜歡赤裸裸地管理內(nèi)存的分配和釋放。不過,目前sp和wp的使用已經(jīng)深入到Android系統(tǒng)的各個角落,想把它去掉真是不太可能了。

這三者的關系比較復雜,都說程咬金的“三板斧”很厲害,那么我們就借用這三板斧,揭密其相互間的曖昧關系。

5.2.1 ?第一板斧——初識影子對象

我們的“三板斧”,其實就是三個例子。相信這三板斧劈下去,你會很容易理解它們。

[-->例子1]

//類A從RefBase派生,RefBase是萬物的始祖

class A:public RefBase

{

?//A沒有任何自己的功能

}

int main()

{

? A* pA =new A;

? {

? ?//注意我們的sp,wp對象是在{}中創(chuàng)建的,下面的代碼先創(chuàng)建sp,然后創(chuàng)建wp

?? sp<A>spA(A);

?? wp<A>wpA(spA);

??? //大括號結束前,先析構wp,再析構sp

?? }

}

例子夠簡單吧?但也需一步一步分析這斧子是怎么劈下去的。

1. RefBase和它的影子

類A從RefBase中派生。使用的是RefBase構造函數(shù)。代碼如下所示:

[-->RefBase.cpp]

RefBase::RefBase()

??? :mRefs(new weakref_impl(this))//注意這句話

{

? //mRefs是RefBase的成員變量,類型是weakref_impl,我們暫且叫它影子對象

? //所以A有一個影子對象

}

mRefs是引用計數(shù)管理的關鍵類,需要進去觀察。它是從RefBase的內(nèi)部類weakref_type中派生出來的。

先看看它的聲明:

class RefBase::weakref_impl : public RefBase::weakref_type

//從RefBase的內(nèi)部類weakref_type派生

由于Android頻繁使用C++內(nèi)部類的方法,所以初次閱讀Android代碼時可能會有點不太習慣,C++的內(nèi)部類和Java內(nèi)部類相似,但不同的是,它需要一個顯示的成員指向外部類對象,而Java內(nèi)部類對象就有一個隱式的成員指向外部類對象。

說明:內(nèi)部類在C++中的學名叫nested class(內(nèi)嵌類)。

[-->RefBase.cpp::weakref_imple構造]

weakref_impl(RefBase* base)

??????? :mStrong(INITIAL_STRONG_VALUE) //強引用計數(shù),初始值為0x1000000

??????? ,mWeak(0) //弱引用計數(shù),初始值為0

??????? ,mBase(base)//該影子對象所指向的實際對象

??????? ,mFlags(0)

??????? ,mStrongRefs(NULL)

??????? ,mWeakRefs(NULL)

??????? ,mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)

??????? ,mRetain(false)

??? {

???? }

如你所見,new了一個A對象后,其實還new了一個weakref_impl對象,這里稱它為影子對象,另外我們稱A為實際對象。

這里有一個問題:影子對象有什么用?

可以仔細想一下,是不是發(fā)現(xiàn)影子對象成員中有兩個引用計數(shù)?一個強引用,一個弱引用。如果知道引用計數(shù)和對象生死有些許關聯(lián)的話,就容易想到影子對象的作用了。

按上面的分析,在構造一個實際對象的同時,還會悄悄地構造一個影子對象,在嵌入式設備的內(nèi)存不是很緊俏的今天,這個影子對象的內(nèi)存占用已不成問題了。

2. sp上場

程序繼續(xù)運行,現(xiàn)在到了

sp<A> spA(A);

請看sp的構造函數(shù),它的代碼如下所示:(注意,sp是一個模板類,對此不熟悉的讀者可以去翻翻書,或者干脆把所有出現(xiàn)的T都換成A。)

[-->RefBase.h::sp(T*other)]

template<typename T>

sp<T>::sp(T* other) //這里的other就是剛才創(chuàng)建的pA

??? :m_ptr(other)// sp保存了pA的指針

{

??? if(other) other->incStrong(this);//調(diào)用pA的incStrong

}

OK,戰(zhàn)場轉到RefBase的incStrong中。它的代碼如下所示:

[-->RefBase.cpp]

void RefBase::incStrong(const void* id) const

{

?//mRefs就是剛才RefBase構造函數(shù)中new出來的影子對象

?weakref_impl*const refs = mRefs;

?

//操作影子對象,先增加弱引用計數(shù)

?refs->addWeakRef(id);

?refs->incWeak(id);

?......

先來看看影子對象的這兩個weak函數(shù)都干了些什么。??

(1)眼見而心不煩

先來看第一個函數(shù)addWeakRef,代碼如下所示:

[-->RefBase.cpp]

void addWeakRef(const void* /*id*/) { }

呵呵,addWeakRef啥都沒做,因為這是release版走的分支。調(diào)試版的代碼我們就不討論了,它是給創(chuàng)造RefBase、 sp,以及wp的人調(diào)試用的。

調(diào)試版分支的代碼很多,看來創(chuàng)造它們的人,也為不理解它們之間的曖昧關系痛苦不已。

總之,一共有這么幾個不用考慮的函數(shù),我們都已列出來了。以后再碰見它們,干脆就直接跳過的是:

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) { }

繼續(xù)我們的征程。再看incWeak函數(shù),代碼如下所示:

[-->RefBase.cpp]

void RefBase::weakref_type::incWeak(const void*id)

{

???weakref_impl* const impl = static_cast<weakref_impl*>(this);

???impl->addWeakRef(id);? //上面說了,非調(diào)試版什么都不干

?? const int32_tc = android_atomic_inc(&impl->mWeak);

? //原子操作,影子對象的弱引用計數(shù)加1

? //千萬記住影子對象的強弱引用計數(shù)的值,這是徹底理解sp和wp的關鍵

}

好,我們再回到incStrong,繼續(xù)看代碼:

[-->RefBase.cpp]

?? ......

? //剛才增加了弱引用計數(shù)

? //再增加強引用計數(shù)

? refs->addStrongRef(id);//非調(diào)試版這里什么都不干

? //下面函數(shù)為原子加1操作,并返回舊值。所以c=0x1000000,而mStrong變?yōu)?x1000001

? ?const int32_t c =android_atomic_inc(&refs->mStrong);

?? if (c!= INITIAL_STRONG_VALUE)? {

????? //如果c不是初始值,則表明這個對象已經(jīng)被強引用過一次了

???????return;

??? }

? //下面這個是原子加操作,相當于執(zhí)行refs->mStrong +(-0x1000000),最終mStrong=1

? android_atomic_add(-INITIAL_STRONG_VALUE,&refs->mStrong);

?/*

?? 如果是第一次引用,則調(diào)用onFirstRef,這個函數(shù)很重要,派生類可以重載這個函數(shù),完成一些

?? 初始化工作。

?*/

?const_cast<RefBase*>(this)->onFirstRef();

}

?

說明:android_atomic_xxx是Android平臺提供的原子操作函數(shù),原子操作函數(shù)是多線程編程中的常見函數(shù),讀者可以學習原子操作函數(shù)知識,本章后面將對其做介紹。

(2)sp構造的影響

sp構造完后,它給這個世界帶來了什么?

·? 那就是RefBase中影子對象的強引用計數(shù)變?yōu)?,弱引用計數(shù)也變?yōu)?。

更準確的說法是,sp的出生導致影子對象的強引用計數(shù)加1,弱引用計數(shù)加1。

(3)wp構造的影響

繼續(xù)看wp,例子中的調(diào)用方式如下:

wp<A> wpA(spA)

wp有好幾個構造函數(shù),原理都一樣。來看這個最常見的:

[-->RefBase.h::wp(constsp<T>& other)]

template<typename T>

wp<T>::wp(const sp<T>& other)

??? :m_ptr(other.m_ptr) //wp的成員變量m_ptr指向實際對象

{

??? if(m_ptr) {

?????? //調(diào)用pA的createWeak,并且保存返回值到成員變量m_refs中

???????m_refs = m_ptr->createWeak(this);

??? }

}

[-->RefBase.cpp]

RefBase::weakref_type* RefBase::createWeak(constvoid* id) const

{

//調(diào)用影子對象的incWeak,這個我們剛才講過了,將導致影子對象的弱引用計數(shù)增加1

?mRefs->incWeak(id);

?returnmRefs;? //返回影子對象本身

}

我們可以看到,wp化后,影子對象的弱引用計數(shù)將增加1,所以現(xiàn)在弱引用計數(shù)為2,而強引用計數(shù)仍為1。另外,wp中有兩個成員變量,一個保存實際對象,另一個保存影子對象。sp只有一個成員變量用來保存實際對象,但這個實際對象內(nèi)部已包含了對應的影子對象。

OK,wp創(chuàng)建完了,現(xiàn)在開始進入wp的析構。

(4)wp析構的影響

wp進入析構函數(shù),這表明它快要離世了。

[-->RefBase.h]

template<typename T>

wp<T>::~wp()

{

??? if(m_ptr) m_refs->decWeak(this); //調(diào)用影子對象的decWeak,由影子對象的基類實現(xiàn)

}

[-->RefBase.cpp]

void RefBase::weakref_type::decWeak(const void*id)

{

? //把基類指針轉換成子類(影子對象)的類型,這種做法有些違背面向對象編程的思想

? weakref_impl*const impl = static_cast<weakref_impl*>(this);

? impl->removeWeakRef(id);//非調(diào)試版不做任何事情

?

? //原子減1,返回舊值,c=2,而弱引用計數(shù)從2變?yōu)?

? constint32_t c = android_atomic_dec(&impl->mWeak);

? if (c !=1) return; //c=2,直接返回

??

? //如果c為1,則弱引用計數(shù)為0,這說明沒用弱引用指向實際對象,需要考慮是否釋放內(nèi)存

? // 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;

??????? }

??? }

}

OK,在例1中,wp析構后,弱引用計數(shù)減1。但由于此時強引用計數(shù)和弱引用計數(shù)仍為1,所以沒有對象被干掉,即沒有釋放實際對象和影子對象占據(jù)的內(nèi)存。

(5)sp析構的影響

下面進入sp的析構。

[-->RefBase.h]

template<typename T>

sp<T>::~sp()

{

??? if(m_ptr) m_ptr->decStrong(this); //調(diào)用實際對象的decStrong。由RefBase實現(xiàn)

}

[-->RefBase.cpp]

void RefBase::decStrong(const void* id) const

{

???weakref_impl* const refs = mRefs;

??? refs->removeStrongRef(id);//調(diào)用影子對象的removeStrongRef,啥都不干

??? //注意,此時強弱引用計數(shù)都是1,下面函數(shù)調(diào)用的結果是c=1,強引用計數(shù)為0

??? constint32_t c = android_atomic_dec(&refs->mStrong);

??? if (c== 1) { //對于我們的例子, c為1

??????? //調(diào)用onLastStrongRef,表明強引用計數(shù)減為0,對象有可能被delete

???????const_cast<RefBase*>(this)->onLastStrongRef(id);

???? ??//mFlags為0,所以會通過delete this把自己干掉

????? //注意,此時弱引用計數(shù)仍為1

??????? if((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {

???????????delete this;

??????? }

?? ......

}

先看delete this的處理,它會導致A的析構函數(shù)被調(diào)用;再看A的析構函數(shù),代碼如下所示:

[-->例子1::~A()]

//A的析構直接導致進入RefBase的析構。

RefBase::~RefBase()

{

?? if(mRefs->mWeak == 0) { //弱引用計數(shù)不為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;

??????? }

? //注意,實際數(shù)據(jù)對象已經(jīng)被干掉了,所以mRefs也沒有用了,但是decStrong剛進來

? //的時候就保存mRefs到refs了,所以這里的refs指向影子對象

???refs->removeWeakRef(id);

???refs->decWeak(id);//調(diào)用影子對象decWeak

}

[-->RefBase.cpp]

void RefBase::weakref_type::decWeak(const void*id)

{

? weakref_impl*const impl = static_cast<weakref_impl*>(this);

? impl->removeWeakRef(id);//非調(diào)試版不做任何事情

?

??? //調(diào)用前影子對象的弱引用計數(shù)為1,強引用計數(shù)為0,調(diào)用結束后c=1,弱引用計數(shù)為0

??? constint32_t c = android_atomic_dec(&impl->mWeak);

??? if (c!= 1) return;

???

??? //這次弱引用計數(shù)終于變?yōu)?,并且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中有一個隱含的影子對象,該影子對象內(nèi)部有強弱引用計數(shù)。

·? sp化后,強弱引用計數(shù)各增加1,sp析構后,強弱引用計數(shù)各減1。

·? wp化后,弱引用計數(shù)增加1,wp析構后,弱引用計數(shù)減1。

完全徹底地消滅RefBase對象,包括讓實際對象和影子對象滅亡,這些都是由強弱引用計數(shù)控制的,另外還要考慮flag的取值情況。當flag為0時,可得出如下結論:

·? 強引用為0將導致實際對象被delete。

·? 弱引用為0將導致影子對象被delete。

?

5.2.2 ?第二板斧——由弱生強

再看第二個例子,代碼如下所示:

[-->例子2]

int main()

{

?? A *pA =new A();

??wp<A> wpA(A);

??sp<A> spA = wpA.promote();//通過promote函數(shù),得到一個sp。

}

對A的wp化,不再做分析了。按照前面所學的知識,wp化后僅會使弱引用計數(shù)加1,所以此處wp化的結果是:

·? 影子對象的弱引用計數(shù)為1,強引用計數(shù)仍然是初始值0x1000000。

wpA的promote函數(shù)是從一個弱對象產(chǎn)生一個強對象的重要函數(shù),試看:

1. 由弱生強的方法

代碼如下所示:

[-->RefBase.h]

template<typename T>

sp<T> wp<T>::promote() const

{

??? returnsp<T>(m_ptr, m_refs);? //調(diào)用sp的構造函數(shù)。

}

[-->RefBase.h]

template<typename T>

sp<T>::sp(T* p, weakref_type* refs)

??? :m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)//有點看不清楚

{

//上面那行代碼夠簡潔,但是不方便閱讀,我們寫成下面這樣:

/*

? T* pTemp= NULL;

? //關鍵函數(shù)attemptIncStrong

? if(p !=NULL && refs->attemptIncStrong(this) == true)

?? ???pTemp = p;

?

? m_ptr =pTemp;

*/

}

2. 成敗在此一舉

由弱生強的關鍵函數(shù)是attemptIncStrong,它的代碼如下所示:

[-->RefBase.cpp]

boolRefBase::weakref_type::attemptIncStrong(const void* id)

{

???? incWeak(id);//增加弱引用計數(shù),此時弱引用計數(shù)變?yōu)?

????weakref_impl* const impl = static_cast<weakref_impl*>(this);

??? ??int32_t curCount = impl->mStrong; //這個仍是初始值

???? //下面這個循環(huán),在多線程操作同一個對象時可能會循環(huán)多次。這里可以不去管它,

??? ?//它的目的就是使強引用計數(shù)增加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對象就是實際對象,有可能已經(jīng)被delete了。

?? curCount為0,表示強引用計數(shù)肯定經(jīng)歷了INITIAL_STRONG_VALUE->1->...->0的過程。

?? mFlags就是根據(jù)標志來決定是否繼續(xù)進行||或&&后的判斷,因為這些判斷都使用了mBase,

?? 如不做這些判斷,一旦mBase指向已經(jīng)回收的地址,你就等著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,表示不允許由弱生強,弱引用計數(shù)要減去1,這是因為咱們進來時加過一次

???? ???????decWeak(id);

???????????return false; //由弱生強失敗

??????? }

?

???? //允許由弱生強,則強引用計數(shù)要增加1,而弱引用計數(shù)已經(jīng)增加過了

???????curCount = android_atomic_inc(&impl->mStrong);

??????? if(curCount > 0 && curCount < INITIAL_STRONG_VALUE) {

???????????impl->mBase->onLastStrongRef(id);

??????? }

??? }

???impl->addWeakRef(id);

???impl->addStrongRef(id);//兩個函數(shù)調(diào)用沒有作用

???? if(curCount == INITIAL_STRONG_VALUE) {

?? ??????//強引用計數(shù)變?yōu)?

???????android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);

????? ??//調(diào)用onFirstRef,通知該對象第一次被強引用

???????impl->mBase->onFirstRef();

??? }

??? returntrue; //由弱生強成功

}

3. 第二板斧的結果

promote完成后,相當于增加了一個強引用。根據(jù)上面所學的知識可知:

·? 由弱生強成功后,強弱引用計數(shù)均增加1。所以現(xiàn)在影子對象的強引用計數(shù)為1,弱引用計數(shù)為2。

5.2.3 ?第三板斧——破解生死魔咒

1. 延長生命的魔咒

RefBase為我們提供了一個這樣的函數(shù):

extendObjectLifetime(int32_t mode)

另外還定義了一個枚舉:

enum {

???????OBJECT_LIFETIME_WEAK??? = ?0x0001,

???????OBJECT_LIFETIME_FOREVER = 0x0003

};

注意:FOREVER的值是3,二進制表示是B11,而WEAK的二進制是B01,也就是說FOREVER包括了WEAK的情況。

上面這兩個枚舉值,是破除強弱引用計數(shù)作用的魔咒。先觀察flags為OBJECT_LIFETIME_WEAK的情況,見下面的例子。

[-->例子3]

class A:public RefBase

{

?? publicA()

?? {

????? extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在構造函數(shù)中調(diào)用

?? }

}

int main()

{

?? A *pA =new A();

???wp<A> wpA(A);//弱引用計數(shù)加1

? {

????? sp<A>spA(pA) //sp后,結果是強引用計數(shù)為1,弱引用計數(shù)為2

?? }

....

}

?

sp的析構將直接調(diào)用RefBase的decStrong,它的代碼如下所示:

[-->RefBase.cpp]

void RefBase::decStrong(const void* id) const

{

???weakref_impl* const refs = mRefs;

???refs->removeStrongRef(id);

??? constint32_t c = android_atomic_dec(&refs->mStrong);

??? if (c== 1) { //上面原子操作后,強引用計數(shù)為0

???????const_cast<RefBase*>(this)->onLastStrongRef(id);、

??????? //注意這句話。如果flags不是WEAK或FOREVER的話,將delete數(shù)據(jù)對象

?????? //現(xiàn)在我們的flags是WEAK,所以不會delete 它

????? ??if((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {

?? ?????????delete this;

??????? }

? }

? ??refs->removeWeakRef(id);

???refs->decWeak(id);//調(diào)用前弱引用計數(shù)是2。

}

然后調(diào)用影子對象的decWeak。再來看它的處理,代碼如下所示:

[-->RefBase.cpp::weakref_type的decWeak()函數(shù)]

void RefBase::weakref_type::decWeak(const void*id)

{

???weakref_impl* const impl = static_cast<weakref_impl*>(this);

???impl->removeWeakRef(id);

??? constint32_t c = android_atomic_dec(&impl->mWeak);

??? if (c!= 1) return;? //c為2,弱引用計數(shù)為1,直接返回。

?? /*

???? 假設我們現(xiàn)在到了例子中的wp析構之處,這時也會調(diào)用decWeak,調(diào)用上邊的原子減操作后

???? c=1,弱引用計數(shù)變?yōu)?,此時會繼續(xù)往下運行。由于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,根據(jù)前面的分析, 實際對象的delete會檢查影子對象的弱引用計數(shù),如果它為0,則會把影子對象也delete掉。

??????? 由于影子對象的弱引用計數(shù)此時已經(jīng)為0,所以影子對象也會被delete。

????? */

??????? if((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {

???????????delete impl->mBase;

??????? }

??? }

}

2. LIFETIME_WEAK的魔力

看完上面的例子,我們發(fā)現(xiàn)什么了?

·? 在LIFETIME_WEAK的魔法下,強引用計數(shù)為0,而弱引用計數(shù)不為0的時候,實際對象沒有被delete!只有當強引用計數(shù)和弱引用計數(shù)同時為0時,實際對象和影子對象才會被delete。

3. 魔咒大揭秘

至于LIFETIME_FOREVER的破解,就不用再來一斧子了,我直接的答案是:

·? flags為0,強引用計數(shù)控制實際對象的生命周期,弱引用計數(shù)控制影子對象的生命周期。強引用計數(shù)為0后,實際對象被delete。所以對于這種情況,應記住的是,使用wp時要由弱生強,以免收到segment fault信號。

·? flags為LIFETIME_WEAK,強引用計數(shù)為0,弱引用計數(shù)不為0時,實際對象不會被delete。當弱引用計數(shù)減為0時,實際對象和影子對象會同時被delete。這是功德圓滿的情況。

·? flags為LIFETIME_FOREVER,對象將長生不老,徹底擺脫強弱引用計數(shù)的控制。所以你要在適當?shù)臅r候殺死這些老妖精,免得她禍害“人間”。

5.2.4 ?輕量級的引用計數(shù)控制LightRefBase

上面介紹的RefBase,是一個重量級的引用計數(shù)控制類。那么,究竟有沒有一個簡單些的引用計數(shù)控制類呢?Android為我們提供了一個輕量級的LightRefBase。這個類非常簡單,我們不妨一起來看看。

[-->RefBase.h]

template <class T>

class LightRefBase

{

public:

??? inlineLightRefBase() : mCount(0) { }

inline void incStrong(const void* id) const {

?? ???//LightRefBase只有一個引用計數(shù)控制量mCount。incStrong的時候使它增加1

???????android_atomic_inc(&mCount);

??? }

inline void decStrong(const void* id) const {

?? ????//decStrong的時候減1,當引用計數(shù)變?yōu)榱愕臅r候,delete掉自己

??????? if(android_atomic_dec(&mCount) == 1) {

???????????delete static_cast<const T*>(this);

??????? }

??? }

??? inlineint32_t getStrongCount() const {

???????return mCount;

??? }

???

protected:

??? inline~LightRefBase() { }

???

private:

mutable volatile int32_t mCount;//引用計數(shù)控制變量

};

LightRefBase類夠簡單吧?不過它是一個模板類,我們該怎么用它呢?下面給出一個例子,其中類A是從LightRefBase派生的,寫法如下:

class A:public LightRefBase<A> //注意派生的時候要指明是LightRefBase<A>

{

public:

A(){};

~A(){};

};

另外,我們從LightRefBase的定義中可以知道,它支持sp的控制,因為它只有incStrong和decStrong函數(shù)。

5.2.5 ?題外話——三板斧的來歷

從代碼量上看,RefBase、sp和wp的代碼量并不多,但里邊的關系,尤其是flags的引入,曾一度讓我眼花繚亂。當時,我確實很希望能自己調(diào)試一下這些例子,但在設備上調(diào)試native代碼,需要花費很大的精力,即使是通過輸出log的方式也需要很多時間。該怎么解決這一難題?

既然它的代碼不多而且簡單,那何不把它移植到臺式機的開發(fā)環(huán)境下,整一個類似的RefBase呢?由于有了這樣的構想,我便用上了Visual Studio。至于那些原子操作,Windows平臺上有很直接的InterlockedExchangeXXX與之對應,真的是踏破鐵鞋無覓處,得來全不費功夫?。ㄔ贚inux平臺上,不考慮多線程的話,將原子操作換成普通的非原子操作不是也可以嗎?如果更細心更負責任的話,你可以自己用匯編來實現(xiàn)常用的原子操作,內(nèi)核代碼中有現(xiàn)成的函數(shù),一看就會明白。)

如果把破解代碼看成是攻城略地的話,我們必須學會靈活多變,而且應力求破解方法日臻極致!

?

5.3 ?Thread類以及常用同步類的分析

Thread類是Android為線程操作而做的一個封裝。代碼在Thread.cpp中,其中還封裝了一些與線程同步相關(既然是封裝,要掌握它,最重要的當然是與Pthread相關的知識)的類。我們擬先行分析Threa類,進而再介紹與常用同步類相關的知識。

5.3.1 ?一個變量引發(fā)的思考

Thread類雖說挺簡單,但它構造函數(shù)中的那個canCallJava卻一度使我感到費解。因為我一直使用的是自己封裝的Pthread類。當發(fā)現(xiàn)Thread構造函數(shù)中竟然存在這樣一個東西時,很擔心自己封裝的Pthread類會不會有什么重大問題,因為當時我還從來沒考慮過Java方面的問題。

// canCallJava表示這個線程是否會使用JNI函數(shù)。為什么需要一個這樣的參數(shù)呢?

Thread(bool canCallJava = true)。

我們必須得了解它實際創(chuàng)建的線程函數(shù)是什么。Thread類真實的線程是創(chuàng)建在run函數(shù)中的。

1. 一個變量,兩種處理

先來看一段代碼:

[-->Thread.cpp]

status_t Thread::run(const char* name, int32_tpriority, size_t stack)

{

?? Mutex::Autolock_l(mLock);

??? ....

?? //如果mCanCallJava為真,則調(diào)用createThreadEtc函數(shù),線程函數(shù)是_threadLoop。

?//_threadLoop是Thread.cpp中定義的一個函數(shù)。

?? if(mCanCallJava) {

???????res = createThreadEtc(_threadLoop,this, name, priority,

?????????????????????????????????? stack,&mThread);

??? } else{

???????res = androidCreateRawThreadEtc(_threadLoop, this, name, priority,

?????????????????????????????????? stack,&mThread);

??? }

上面的mCanCallJava將線程創(chuàng)建函數(shù)的邏輯分為兩個分支,雖傳入的參數(shù)都有_threadLoop,但調(diào)用的函數(shù)卻不同。先直接看mCanCallJava為true的這個分支,代碼如下所示:

[-->Thread.h::createThreadEtc()函數(shù)]

inline bool createThreadEtc(thread_func_tentryFunction,

??????????????????????????? void *userData,

??????????????????????????? const char*threadName = "android:unnamed_thread",

??????????????????????????? int32_tthreadPriority = PRIORITY_DEFAULT,

??????????????????????????? size_tthreadStackSize = 0,

??????????????????????????? thread_id_t*threadId = 0)

{

??? returnandroidCreateThreadEtc(entryFunction, userData, threadName,

??????? ???????????threadPriority, threadStackSize,threadId) ? true : false;

}

它調(diào)用的是androidCreateThreadEtc函數(shù),相關代碼如下所示:

// gCreateThreadFn是函數(shù)指針,初始化時和mCanCallJava為false時使用的是同一個

//線程創(chuàng)建函數(shù)。那么有地方會修改它嗎?

static android_create_thread_fn gCreateThreadFn= androidCreateRawThreadEtc;

int androidCreateThreadEtc(android_thread_func_tentryFunction,

??????????????????????????? void*userData,const char* threadName,

??????????????????????????? int32_tthreadPriority,size_t threadStackSize,

??????????????????????????? android_thread_id_t*threadId)

{

??? returngCreateThreadFn(entryFunction, userData, threadName,

??????? ???????????????????????threadPriority,threadStackSize, threadId);

}

如果沒有人修改這個函數(shù)指針,那么mCanCallJava就是虛晃一槍,并無什么作用,很可惜,代碼中有的地方是會修改這個函數(shù)指針的指向的,請看:

2. zygote偷梁換柱

在第四章4.2.1的第2小節(jié)AndroidRuntime調(diào)用startReg的地方,就有可能修改這個函數(shù)指針,其代碼如下所示:

[-->AndroidRuntime.cpp]

/*static*/ int AndroidRuntime::startReg(JNIEnv*env)

{

?? //這里會修改函數(shù)指針為javaCreateThreadEtc

? androidSetCreateThreadFunc((android_create_thread_fn)javaCreateThreadEtc);

? return0;

}

所以,如果mCanCallJava為true,則將調(diào)用javaCreateThreadEtc。那么,這個函數(shù)有什么特殊之處呢?來看其代碼,如下所示:

[-->AndroidRuntime.cpp]

int AndroidRuntime::javaCreateThreadEtc(

???????????????????????????????android_thread_func_tentryFunction,

??????????????????????????????? void* userData,

??????????????????????????????? const char*threadName,

??????????????????????????????? int32_tthreadPriority,

??????????????????????????????? size_t threadStackSize,

???????????????????????????????android_thread_id_t* threadId)

{

??? void**args = (void**) malloc(3 * sizeof(void*));??

??? intresult;

???args[0] = (void*) entryFunction;

???args[1] = userData;

???args[2] = (void*) strdup(threadName);

??? //調(diào)用的還是androidCreateRawThreadEtc,但線程函數(shù)卻換成了javaThreadShell。

??? result= androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,

??????? ?????????????????threadName, threadPriority,threadStackSize, threadId);

??? returnresult;

}

[-->AndroidRuntime.cpp]

int AndroidRuntime::javaThreadShell(void* args){

??? ??......

???? intresult;

??? //把這個線程attach到JNI環(huán)境中,這樣這個線程就可以調(diào)用JNI的函數(shù)了

??? if(javaAttachThread(name, &env) != JNI_OK)

???????return -1;

???? //調(diào)用實際的線程函數(shù)干活

?? ??result = (*(android_thread_func_t)start)(userData);

?? //從JNI環(huán)境中detach出來。

???javaDetachThread();

???free(name);

??? returnresult;

}

3. 費力而討好

你明白mCanCallJava為true的目的了嗎?它創(chuàng)建的新線程將:

·? 在調(diào)用你的線程函數(shù)之前會attach到 JNI環(huán)境中,這樣,你的線程函數(shù)就可以無憂無慮地使用JNI函數(shù)了。

·? 線程函數(shù)退出后,它會從JNI環(huán)境中detach,釋放一些資源。

第二點尤其重要,因為進程退出前,dalvik虛擬機會檢查是否有attach了,但是最后未detach的線程如果有,則會直接abort(這不是一件好事)。如果你關閉JNI check選項,就不會做這個檢查,但我覺得,這個檢查和資源釋放有關系。建議還是重視JNIcheck。如果直接使用POSIX的線程創(chuàng)建函數(shù),那么凡是使用過attach的,最后就都需要detach!

Android為了dalvik的健康真是費盡心機呀。

4. 線程函數(shù)_threadLoop介紹

不論一分為二是如何處理的,最終的線程函數(shù)_threadLoop都會被調(diào)用,為什么不直接調(diào)用用戶傳入的線程函數(shù)呢?莫非_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

?

??? boolfirst = true;

?

??? do {

???????bool result;

??????? if(first) {

???????????first = false;

??????????//self代表繼承Thread類的對象,第一次進來將調(diào)用readyToRun,看看是否準備好

???????? ?self->mStatus = self->readyToRun();

???????????result = (self->mStatus == NO_ERROR);

?

???????????if (result && !self->mExitPending) {

????????????????result = self->threadLoop();

???????????}

??????? }else {

??????????/*

調(diào)用子類實現(xiàn)的threadLoop函數(shù),注意這段代碼運行在一個do-while循環(huán)中。

???????????? 這表示即使我們的threadLoop返回了,線程也不一定會退出。

?????????*/

???????????result = self->threadLoop();

??????? }

?? /*

線程退出的條件:

?? ?1)result 為false。這表明,如果子類在threadLoop中返回false,線程就可以

??? 退出。這屬于主動退出的情況,是threadLoop自己不想繼續(xù)干活了,所以返回false。

讀者在自己的代碼中千萬別寫錯threadLoop的返回值。

??? 2)mExitPending為true,這個變量可由Thread類的requestExit函數(shù)設置,這種

??? 情況屬于被動退出,因為由外界強制設置了退出條件。

?? */

??????? 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);

???

??? return0;

}

關于_threadLoop,我們就介紹到這里。請讀者務必注意下面一點:

·? threadLoop運行在一個循環(huán)中,它的返回值可以決定是否退出線程。

5.3.2 ?常用同步類

同步,是多線程編程中不可回避的話題,同時也是一個非常復雜的問題。這里,只簡單介紹一下Android提供的同步類。這些類,只對系統(tǒng)提供的多線程同步函數(shù)(這種函數(shù)我們也稱之為Raw API)進行了面向對象的封裝,讀者必須先理解Raw API,然后才能真正掌握其具體用法。

了解Windows下的多線程編程,有很多參考資料,但我以為,現(xiàn)在先學習MSDN就可以了。有關Linux下完整系統(tǒng)闡述多線程編程的書籍目前較少,這里推薦一本含金量較高的著作《Programmingwith POSIX Thread》(本書只有英文版的,由Addison-Wesley出版)。

Android提供了兩個封裝好的同步類,它們是Mutex和Condition。這是重量級的同步技術,一般內(nèi)核會有對應的支持。另外,OS還提供了簡單的原子操作,這些也算是同步技術的一種。下面分別來介紹這三種東西。

1. 互斥類——Mutex

Mutex是互斥類,用于多線程訪問同一個資源的時候,保證一次只能有一個線程能訪問該資源。在《Windows核心編程》一書中,對于這種互斥訪問有一個很形象的比喻:想象你在飛機上如廁,這時衛(wèi)生間的信息牌上顯示“有人”,你必須等里邊的人出來后才可進去。這就是互斥的含義。

下面來看Mutex的實現(xiàn)方式,它們都很簡單。

(1)Mutex介紹

其代碼如下所示:

[-->Thread.h::Mutex的聲明和實現(xiàn)]

inline Mutex::Mutex(int type, const char* name){

??? if(type == SHARED) {

??????//type如果是SHARED,則表明這個Mutex支持跨進程的線程同步

????? //以后我們在Audio系統(tǒng)和Surface系統(tǒng)中會經(jīng)常見到這種用法

???????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函數(shù)的使用,它們的用法如下:

·? 要想獨占衛(wèi)生間,必須先調(diào)用Mutex的lock函數(shù)。這樣,這個區(qū)域就被鎖住了。如果這塊區(qū)域之前已被別人鎖住,lock函數(shù)則會等待,直到可以進入這塊區(qū)域為止。系統(tǒng)保證一次只有一個線程能lock成功。

·? 當你“方便”完畢,記得調(diào)用Mutex的unlock以釋放互斥區(qū)域。這樣,其他人的lock才可以成功返回。

·? 另外,Mutex還提供了一個trylock函數(shù),該函數(shù)只是嘗試去鎖住該區(qū)域,使用者需要根據(jù)trylock的返回值判斷是否成功鎖住了該區(qū)域。

注意,以上這些內(nèi)容都和Raw API有關,不了解它的讀者可自行學習與它相關的知識。在Android系統(tǒng)中,多線程也是常見和重要的編程手段,務請大家重視。

Mutex類確實比Raw API方便好用,不過還是稍顯麻煩。來看下一節(jié)。

(2)AutoLock介紹

AutoLock類是定義在Mutex內(nèi)部的一個類,它其實是一幫“懶人”搞出來的,為什么這么說呢?先來看看使用Mutex夠多麻煩:

·? 顯示調(diào)用Mutex的lock。

·? 在某個時候要記住調(diào)用該Mutex的unlock。

以上這些操作都必須一一對應,否則會出現(xiàn)“死鎖”!有些代碼中,在判斷分支特別多的情況下,unlock這句代碼被寫得比比皆是,如稍有不慎,在某處就會忘寫了它。有什么好辦法能解決這個問題嗎?終于有人想出來一個好辦法,就是充分利用了C++的構造和析構函數(shù),只需一看AutoLock的定義就會明白。代碼如下所示:

[-->Thread.h Mutex::Autolock聲明和實現(xiàn)]

??? classAutolock {

???public:

??????? //構造的時候調(diào)用lock

???????inline Autolock(Mutex& mutex) : mLock(mutex)? { mLock.lock(); }

???????inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }

??????? //析構的時候調(diào)用unlock

???????inline ~Autolock() { mLock.unlock(); }

???private:

???????Mutex& mLock;

??? };

AutoLock的用法很簡單:

·? 先定義一個Mutex,如 Mutex xlock;

·? 在使用xlock的地方,定義一個AutoLock,如 AutoLock autoLock(xlock)。

由于C++對