Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android - 引用計數(sp、wp、Refbase)

Android - 引用計數(sp、wp、Refbase)

編輯:關於Android編程

Boost和Loki是應用比較廣泛的引用計數方案,Android提供了另外一個引用計數方案,就是sp、wp和Refbase組合。 強引用和弱引用區別 在Android裡面,sp是強引用,它是應用最多的引用形式,而且後面的分析,我們將知道,強引用直接管理著對象的銷毀;wp是弱引用,弱引用的用途是能夠對某個對象進行引用,但是即使該對象弱引用還存在,這個對象也可能會被銷毀。弱引用看上去更復雜一些,以下是在網上摘錄的一句話,“弱引用適合那些數據成員特別多,而且重新創建又相對容易的類,也就是俗稱的胖子類,建立弱引用可以引用對象,但也不阻止其被垃圾回收,在內存的使用方面取得一定的平衡”。還是看看實際的例子比較好,如Android Framework/base/services/camera/libcamera/cameraservice.cpp中的一段代碼: [html]  sp<Client> client;       if (cameraId < 0 || cameraId >= mNumberOfCameras) {           LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",               callingPid, cameraId);           return NULL;       }          Mutex::Autolock lock(mServiceLock);       if (mClient[cameraId] != 0) {           client = mClient[cameraId].promote();           if (client != 0) {               if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {                   LOG1("CameraService::connect X (pid %d) (the same client)",                       callingPid);                   return client;               } else {                   LOGW("CameraService::connect X (pid %d) rejected (existing client).",                       callingPid);                   return NULL;               }           }           mClient[cameraId].clear();       }   代碼說明 該代碼是函數connect的一部分,connect函數是客戶端camera.cpp連接cameraservice以獲得獲得服務。Android的cameraservice機制是可以支持多個攝像頭同時供多個客戶端使用,並且,每個攝像頭可能會被多個客戶端輪流使用(你可以想象,在不關閉攝像頭的情況下,兩個應用程序交替顯示一個攝像頭的圖像,如果幀數足夠多,那麼這個攝像頭采集的數據就可以同時供兩個應用程序順暢顯示)。那麼應用程序A和B是如何交替使用呢,就是分別調用service提供的connect函數,而mClient數組就是service部分將具體的某個攝像頭和某個具體的應用程序掛鉤的變量(該變量就是CameraService::Client類對象),從這也可以看出,一個時間點,一個攝像頭只能同時供一個應用程序使用。所以,在交替切換過程中,就伴隨著CameraService::Client類對象的創建和銷毀。 這個時候之所以使用弱引用更方便,是因為如果程序A正在使用攝像頭,如果按照強引用,程序B是無法從程序A搶到攝像頭的;而如果是弱引用,即使程序A占用攝像頭,程序B依然可以將程序A在CameraService的資源釋放,然後創建自己的資源,然後將建立自己的弱引用。這確實很巧妙。 所以弱引用適合用在資源可以被多個程序同時交替使用,時刻可能被其他程序搶走資源的情況,而且資源可以自己負責,也可以自己不負責的情況。   sp如何管理強引用計數 sp指的是對象的強引用,一般定義如下,sp<Camera> mCamera,那麼,mCamera對應的實際對象就交由sp進行管理了,當引用計數為0時,該實際對象就會被銷毀。那麼在什麼情況要增加引用計數,在什麼情況要減少引用計數呢? 以下是增加引用計數的情況: 1. 指針賦值拷貝 如: sp<Camera> mCamera = new camera(); 這是一個用得非常多的情況,當new一個對象時,就將該對象的指針交由sp進行管理了,那麼這個時候sp就應該增加這個new對象的引用計數,當然這個時候應該是1; Camera* c = new camera(); sp<Camera> mCamera = c; 該情況是將一個已經new好了的對象指針賦值給mCamera,那麼這個時候sp就應該增加這個new對象的引用計數。 sp相應代碼如下: [html]   template<typename T>   sp<T>& sp<T>::operator = (T* other)   {       if (other) other->incStrong(this);       if (m_ptr) m_ptr->decStrong(this);       m_ptr = other;       return *this;   }   其中m_ptr是對象的指針,該代碼的邏輯如下 # 如果被復制的對象存在,即other不為NULL,那麼就將該對象的引用計數加1; # 如果原來m_ptr已經指向了某個對象,那麼就將某個對象的引用計數減1; # 然後將other賦值給m_ptr,從此,m_ptr就指向了剛剛設置的對象;   2. 引用賦值拷貝 如: sp<Camera> c = new camera(); sp<Camera> mCamera = c; 將一個對象c賦值給mCamera,sp對應代碼如下: [html]   template<typename T>   sp<T>& sp<T>::operator = (const sp<T>& other) {       T* otherPtr(other.m_ptr);       if (otherPtr) otherPtr->incStrong(this);       if (m_ptr) m_ptr->decStrong(this);       m_ptr = otherPtr;       return *this;   }   # 將被賦值對象other的對象引用計數加1; # 將sp對應對象的引用計數減1; # 將sp的對象改為other對象;   3. 指針構造拷貝 如: sp<Camera> mCamera(new camera()); sp相應代碼如下: [html]   template<typename T>   sp<T>::sp(T* other)       : m_ptr(other)   {       if (other) other->incStrong(this);   }   # 將被賦值對象other的對象指針m_ptr賦值給當前sp的m_ptr; # 如果被賦值對象other的對象存在,則將該對象的引用計數加1;   4. 引用構造拷貝  如: sp<Camera> c = new camera(); sp<Camera> mCamera(c); sp相應代碼如下: [html]  template<typename T>   sp<T>::sp(const sp<T>& other)       : m_ptr(other.m_ptr)   {       if (m_ptr) m_ptr->incStrong(this);   }   # 將被賦值對象other的對象指針m_ptr賦值給當前sp的m_ptr; # 如果被賦值對象other的對象存在,則將該對象的引用計數加1;   以下是減少引用計數的情況: 1. sp析構 如: { sp<Camera> c = new camera(); /* do something*/ } 由於c是局部變量,那麼,在大括號結束前,c將被析構。 sp對應代碼如下: [html]   template<typename T>   sp<T>::~sp()   {       if (m_ptr) m_ptr->decStrong(this);   }   # 析構並不代表對象真的銷毀,因為可能將該對象賦值給了其他sp管理,所以,析構函數只是將對象的引用計數減1;   2. 指針賦值拷貝和引用賦值拷貝 在上面的增加引用計數已經介紹,重新設置sp管理的對象,必須將現在管理的對象的引用計數減1   3. 清除 如: sp<Camera> mCamera = new camera(); mCamera.clear(); 意思設置mCamera不再管理任何對象。 sp代碼如下: [html]   template<typename T>   void sp<T>::clear()   {       if (m_ptr) {           m_ptr->decStrong(this);           m_ptr = 0;       }   }   # 將管理的對象引用計數減1; # 將m_ptr置空;   wp如何管理弱引用計數 對於wp的處理和sp基本相同,代碼部分就不做細致分析了。 我們可以記住,弱引用計數可以通過promote函數升級為強引用。   Refbase機制 我們在上面一節可以看到,sp和wp就是調用Refbase提供的incStrong、decStrong、incWeak和decWeak設置對象的引用計數,那麼Refbase是如何實現的呢,以及什麼情況下才會將對象銷毀呢? 如: { sp<Camera> mCamera = new camera(); } 上面如此簡單的代碼,sp先將camera對象的引用計數加1,然後由於作用域結束,所以mCamera被析構,析構時會將camera對象的引用計數減1,那麼這個時候,這個對象的引用計數恰好是0,那麼這個時候對象會被銷毀嗎?我們還是看看Refbase是如何被構造,如何管理引用計數,如何管理對象的。 構造 [html]   RefBase::RefBase()       : mRefs(new weakref_impl(this))   {   //    LOGV("Creating refs %p with RefBase %p\n", mRefs, this);   }   很簡單,只是又創建了一個weakref_impl,從這個名字就可以看出它是處理弱引用的類,其實除了弱引用,它還處理強引用。我們還是根據《心得 - 如何讀大型代碼和寫代碼》所描述的方法,我們只需要從宏觀上了解weakref_impl的作用,我們現在正在研究Refbase,所以,只要在調用weakref_impl的地方,我們只要能夠猜出它完成了什麼功能即可,不進行深入分析。 # 創建了weakref_impl對象,並將this指針傳遞過去,看來weakref_impl對象要通過this指針來回調Refbase的某些方法;   增加強引用計數 mCamera指針賦值拷貝時會增加引用計數,代碼如下: [html]   void RefBase::incStrong(const void* id) const   {       weakref_impl* const refs = mRefs;       refs->addWeakRef(id);       refs->incWeak(id);              refs->addStrongRef(id);       const int32_t c = android_atomic_inc(&refs->mStrong);       LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);   #if PRINT_REFS       LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);   #endif       if (c != INITIAL_STRONG_VALUE)  {           return;       }          android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);       const_cast<RefBase*>(this)->onFirstRef();   }   # 增加弱引用,可見,使用強引用時,還伴隨著一個弱引用; # 增加強引用; # 如果是第一次增加強引用計數,那麼就會調用onFirstRef函數,我們在代碼經常會看到onFirstRef函數,通過該函數,經常會做些初始化方面的工作;   減少強引用計數 當mCamera析構時就會減少強引用計數,代碼如下: [html]   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 PRINT_REFS       LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);   #endif       LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);       if (c == 1) {           const_cast<RefBase*>(this)->onLastStrongRef(id);           if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {               delete this;           }       }       refs->removeWeakRef(id);       refs->decWeak(id);   }     # 減小強引用計數; # 銷毀對象,如果減小強引用計數之前,強引用計數是1,也就是減小後,強引用計數是0,那麼這個時候就delete this了; # 減小弱引用計數,看來弱引用計數和對象實際存不存在沒有必然的關系;   weakref_type功能 我們通過Refbase看出,weakref_type實際管理著強引用和弱引用兩個計數。   構造 [html]  weakref_impl(RefBase* base)       : mStrong(INITIAL_STRONG_VALUE)       , mWeak(0)       , mBase(base)       , mFlags(0)   {   }   # 初始化,mStrong(強引用計數)、mWeak(弱引用計數)、mBase(管理的Refbase) 另外,和構造函數一起的還有     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) { } 它們什麼都沒有做。   增加弱引用計數 [html]   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);       LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);   }   # 通過原子操作增加mWeak;    減少弱引用計數 [html]  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);       LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);       if (c != 1) return;              if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {           if (impl->mStrong == INITIAL_STRONG_VALUE)               delete impl->mBase;           else {   //            LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);               delete impl;           }       } else {           impl->mBase->onLastWeakRef(id);           if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {               delete impl->mBase;           }       }   }   # 通過原子操作減少弱引用計數 # 當弱引用計數為0時,那麼就會根據某種條件delete this或者delete Refbase 我們在減少強引用和弱引用,看到了OBJECT_LIFETIME_FOREVER和OBJECT_LIFETIME_WEAK兩個標志位,那麼他們是干什麼的呢。www.2cto.com OBJECT_LIFETIME_FOREVER可能代碼裡面沒有設置為這個標識符的類,而OBJECT_LIFETIME_WEAK只有Binder設置了,我們從名字上看出,OBJECT_LIFETIME_FOREVER指的是對象的生命是永遠,而OBJECT_LIFETIME_WEAK可能是生命時間按照弱引用計數來決定(一般都是通過強引用計數來決定)。 由於用得很少,所以,這兩個標志位對我們的應用而言沒有多大關系,但是,我們還是看看代碼怎麼特殊處理這兩個標志位。 在decStrong函數中,         if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {             delete this;         } 意思是當標志位不是OBJECT_LIFETIME_WEAK時,才會銷毀對象。也就是如果標志位是OBJECT_LIFETIME_WEAK,那麼如果強引用為0時,也不會銷毀對象,看來銷毀對象的任務交給其他地方執行了。 還是看上面的decWeak函數,當標志位是OBJECT_LIFETIME_WEAK時,只有當標志位不是OBJECT_LIFETIME_FOREVER才會銷毀對象;當標志不是OBJECT_LIFETIME_WEAK時,如果強引用計數也為0,那麼就將對象銷毀,否則,將weakref_impl銷毀。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved