[深刻理解Android卷一全文-第五章]深刻理解常见类

因为《深刻理解Android 卷一》和《深刻理解Android卷二》再也不出版,而知识的传播不该该由于纸质媒介的问题而中断,因此我将在OSC博客中全文转发这两本书的所有内容。


第5章 深刻理解常见类

本章主要内容 java

·  分析RefBase、sp,wp和LightRefBase类。 android

·  分析Native的Thread类和经常使用同步类。 程序员

·  分析Java层的Handler、Looper,以及HandlerThread类。 编程

本章涉及的源代码文件名称及位置 多线程

下面是咱们本章分析的源码文件名和它的位置。 框架

·  RefBase.h 函数

framework/base/include/utils/RefBase.h oop

·  RefBase.cpp 学习

framework/base/libs/utils/RefBase.cpp this

·  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(A);

   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(A);

请看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_tc = 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(constsp<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(constvoid* id) const

{

//调用影子对象的incWeak,这个咱们刚才讲过了,将致使影子对象的弱引用计数增长1

 mRefs->incWeak(id);

 returnmRefs;  //返回影子对象自己

}

咱们能够看到,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

  constint32_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;

        }

    }

}

OK,在例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

    constint32_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

    constint32_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(A);

  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

{

    returnsp<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]

boolRefBase::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();

    }

    returntrue; //由弱生强成功

}

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

{

   publicA()

   {

      extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在构造函数中调用

   }

}

int main()

{

   A *pA =new A();

   wp<A> wpA(A);//弱引用计数加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);

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

    constint32_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:

    inlineLightRefBase() : 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);

        }

    }

    inlineint32_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_tpriority, 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_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;

}

它调用的是androidCreateThreadEtc函数,相关代码以下所示:

// gCreateThreadFn是函数指针,初始化时和mCanCallJava为false时使用的是同一个

//线程建立函数。那么有地方会修改它吗?

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

}

若是没有人修改这个函数指针,那么mCanCallJava就是虚晃一枪,并没有什么做用,很惋惜,代码中有的地方是会修改这个函数指针的指向的,请看:

2. zygote偷梁换柱

在第四章4.2.1的第2小节AndroidRuntime调用startReg的地方,就有可能修改这个函数指针,其代码以下所示:

[-->AndroidRuntime.cpp]

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

{

   //这里会修改函数指针为javaCreateThreadEtc

  androidSetCreateThreadFunc((android_create_thread_fn)javaCreateThreadEtc);

  return0;

}

因此,若是mCanCallJava为true,则将调用javaCreateThreadEtc。那么,这个函数有什么特殊之处呢?来看其代码,以下所示:

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

    //调用的仍是androidCreateRawThreadEtc,但线程函数却换成了javaThreadShell。

    result= androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,

                         threadName, threadPriority,threadStackSize, threadId);

    returnresult;

}

[-->AndroidRuntime.cpp]

int AndroidRuntime::javaThreadShell(void* args){

      ......

     intresult;

    //把这个线程attach到JNI环境中,这样这个线程就能够调用JNI的函数了

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

       return -1;

     //调用实际的线程函数干活

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

   //从JNI环境中detach出来。

   javaDetachThread();

   free(name);

    returnresult;

}

3. 费力而讨好

你明白mCanCallJava为true的目的了吗?它建立的新线程将:

·  在调用你的线程函数以前会attach到 JNI环境中,这样,你的线程函数就能够无忧无虑地使用JNI函数了。

·  线程函数退出后,它会从JNI环境中detach,释放一些资源。

第二点尤为重要,由于进程退出前,dalvik虚拟机会检查是否有attach了,可是最后未detach的线程若是有,则会直接abort(这不是一件好事)。若是你关闭JNI check选项,就不会作这个检查,但我以为,这个检查和资源释放有关系。建议仍是重视JNIcheck。若是直接使用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

 

    boolfirst = 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);

   

    return0;

}

关于_threadLoop,咱们就介绍到这里。请读者务必注意下面一点:

·  threadLoop运行在一个循环中,它的返回值能够决定是否退出线程。

5.3.2  经常使用同步类

同步,是多线程编程中不可回避的话题,同时也是一个很是复杂的问题。这里,只简单介绍一下Android提供的同步类。这些类,只对系统提供的多线程同步函数(这种函数咱们也称之为Raw API)进行了面向对象的封装,读者必须先理解Raw API,而后才能真正掌握其具体用法。

了解Windows下的多线程编程,有不少参考资料,但我觉得,如今先学习MSDN就能够了。有关Linux下完整系统阐述多线程编程的书籍目前较少,这里推荐一本含金量较高的著做《Programmingwith 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声明和实现]

    classAutolock {

   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只有一个会被唤醒

    voidsignal();

    //触发者A用来通知条件已经知足,全部等待者都会被唤醒

    voidbroadcast();

 

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_tCondition::waitRelative(Mutex& mutex, nsecs_t reltime) {

#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)

    structtimespec 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也就会被自动调用。

    returnmStatus;

}

那么,什么地方会触发这个条件呢?是在工做线程退出前。其代码以下所示:

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

   

    return0;

}

关于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指令,它先交换源和目的的操做数,再进行递增操做。

*/

        INT32i = 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 {

    publicHandler mHandler;

public void run() {

     //① 调用prepare

     Looper.prepare();

    ......

     //② 进入消息循环

Looper.loop();

   }

}

//应用程序使用LooperThread

{

  ......

  newLooperThread().start();//启动新线程,线程函数是run

}

上面的代码一共有两个关键调用,咱们对其逐一进行分析。

1. 准备好了吗?

第一个调用函数是Looper的prepare函数。它会作什么工做呢?其代码以下所示:

[-->Looper.java]

  publicstatic final void prepare() {

   //一个Looper只能调用一次prepare 

  if(sThreadLocal.get() != null) {

     thrownew RuntimeException("Only one Looper may be created per thread");

  }

   //构造一个Looper对象,设置到调用线程的局部变量中

   sThreadLocal.set(newLooper());

}

//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封装了一个消息队列。

·  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

   publicHandler(Callback callback) {

        mLooper = Looper.myLooper();

        if(mLooper == null) {

throw new RuntimeException(......);

        }

        //和构造函数1相似,只不过多了一个设置callback

       mQueue = mLooper.mQueue;

       mCallback = callback;

    }

//构造函数3

   publicHandler(Looper looper) {

       mLooper = looper; //looper由外部传入,是哪一个线程的Looper不肯定

       mQueue = looper.mQueue;

       mCallback = null;

    }

//构造函数4,和构造函数3相似,只不过多了callback设置

   publicHandler(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更像一个辅助类,帮助咱们简化编程的工做。

2.1 Handler和Message

Handle提供了一系列函数,帮助咱们完成建立消息和插入消息队列的工做。这里只列举其中一二。要掌握详细的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.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 {

    publicLooper myLooper = null;//定义一个public的成员myLooper,初值为空。

public void run() { //假设run在线程2中执行

         Looper.prepare();

        // myLooper必须在这个线程中赋值

         myLooper = Looper.myLooper();

Looper.loop();

   }

}

//下面这段代码在线程1中执行,而且会建立线程2

{

  LooperThreadlpThread= 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

 publicLooper getLooper() {

       ......      

       synchronized (this) {

           while (isAlive() && mLooper == null) {

               try {

                    wait();//若是新线程还未建立Looper,则等待

               } catch (InterruptedException e) {

               }

           }

        }

       return mLooper;

    }

   

//线程2运行它的run函数,looper就是在run线程里建立的。

  publicvoid 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,它下降了建立和使用带有消息队列的线程的难度。



 本书中文版由机械工业出版社出版,原书做者Jeffrey Richter。

相关文章
相关标签/搜索