編輯:關於Android編程
在Android native編寫代碼時,會經常接觸到sp、wp,sp並不是smart pointer的意思,而是strong point;wp就是weak pointer。這兩個概念比較像JAVA中的強弱引用,使用sp和wp可以讓編程人員不需要再關系內存的釋放問題,防止內存洩露。下面先來看它們的類關系圖:
要實現內存的自動釋放,sp、wp必須結合RefBase這個類來使用,在Android中,大多數類的最上層基類都是RefBase類。我們舉個簡單的例子,然後順著這個例子來分析RefBase、sp和wp四種不同的應用,並介紹其實現。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">class A : public RefBase
{
}
上面定義一個類A,繼承與RefBase,下面我們首先來看RefBases的構造函數:
RefBase::RefBase() : mRefs(new weakref_impl(this)) { } weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE) , mWeak(0) , mBase(base) , mFlags(0) { }
在RefBase中,首先構造weakref_impl對象,在weakref_impl對mStong和mWeak進行強弱引用計數賦初始值,INITIAL_STRONG_VALUE是0X10000000,這裡不直接賦初始值為0,是方便我們區分,0到底是初始化的值,還是在sp釋放後再變為0,方便做不同的處理。
列舉第一種應用:只有sp指針,沒有wp指針的應用
{
sp spA(new A);
}
首先來看sp的構造函數:
templatesp ::sp(T* other) : m_ptr(other) { if (other) other->incStrong(this); }
void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs; refs->incWeak(id); refs->addStrongRef(id); const int32_t c = android_atomic_inc(&refs->mStrong); ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); if (c != INITIAL_STRONG_VALUE) { return; } android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); refs->mBase->onFirstRef(); }
void RefBase::weakref_type::incWeak(const void* id) { weakref_impl* const impl = static_cast(this); impl->addWeakRef(id); const int32_t c = android_atomic_inc(&impl->mWeak); ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); }
這裡還是調用android_atomic_inc去增加weakref_impl的mWeak計數。經過構造函數,mStong和mWeak的計數都變成了1。當spA對象退出作用域以後,就會調用其析構函數來釋放這個對象:
templatesp ::~sp() { if (m_ptr) m_ptr->decStrong(this); } void RefBase::decStrong(const void* id) const { weakref_impl* const refs = mRefs; refs->removeStrongRef(id); const int32_t c = android_atomic_dec(&refs->mStrong); ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); if (c == 1) { refs->mBase->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { delete this; } } refs->decWeak(id); }
void RefBase::weakref_type::decWeak(const void* id) { weakref_impl* const impl = static_cast(this); impl->removeWeakRef(id); const int32_t c = android_atomic_dec(&impl->mWeak); ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); if (c != 1) return; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { if (impl->mStrong == INITIAL_STRONG_VALUE) { delete impl->mBase; } else { delete impl; } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { delete impl->mBase; } } }
來看判斷是否釋放的邏輯,如果Flag設定當前對象的生命周期由sp指針決定,並且之前沒有初始化過任何sp對象,則直接刪除A對象;如果之前由初始化過sp對象,則刪除weakref_impl本身,A對象會在RefBase的decStrong中被釋放。如果Flag設定當前對象的生命周期由wp指針決定,則首先調用A對象的onLastWeakRef方法,然後刪除對象A。在刪除對象A的時候,都會調用RefBase的析構函數,我們再來分析RefBase的系統函數:
RefBase::~RefBase() { if (mRefs->mStrong == INITIAL_STRONG_VALUE) { delete mRefs; } else { if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) { if (mRefs->mWeak == 0) { delete mRefs; } } } const_cast(mRefs) = NULL; }
列舉第二種應用:只有wp指針,沒有sp指針的應用
{
wp wpA(new A);
}
首先來看wp的構造方法:
templatewp ::wp(const sp & other) : m_ptr(other.m_ptr) { if (m_ptr) { m_refs = m_ptr->createWeak(this); } }
RefBase::weakref_type* RefBase::createWeak(const void* id) const { mRefs->incWeak(id); return mRefs; } void RefBase::weakref_type::incWeak(const void* id) { weakref_impl* const impl = static_cast(this); impl->addWeakRef(id); const int32_t c = android_atomic_inc(&impl->mWeak); ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); }
templatewp ::~wp() { if (m_ptr) m_refs->decWeak(this); }
列舉第三種應用:既有sp指針,又有wp指針的應用
{
sp spA(new A)
wp wpA(spA);
}
從上面它們的構造函數我們知道,這是mStong等於1,mWeak等於2。在spA和wpA退出作用域時,首先調用wp的析構函數,再調用sp的析構函數。在wp析構函數中,只會減少mWeak計數為1,然後就然後了。再到sp的析構函數中,就和我們前面介紹的第一種應用一樣了。
列舉第四種應用:wp指針如果調用對象的方法
前面說過在wp中並沒有重載"->",所以wp並不能直接調用A對象的方法,並且由前面sp的知識,我們知道,在decStrong的時候,有可能A對象會被釋放,所以在wp中想要調用A對象的方法,必須獲得sp指針,這是通過wp的promote方法實現的:
templatesp wp ::promote() const { sp result; if (m_ptr && m_refs->attemptIncStrong(&result)) { result.set_pointer(m_ptr); } return result; }
bool RefBase::weakref_type::attemptIncStrong(const void* id) { incWeak(id); weakref_impl* const impl = static_cast(this); int32_t curCount = impl->mStrong; 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) { if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { if (curCount <= 0) { decWeak(id); return false; } while (curCount > 0) { if (android_atomic_cmpxchg(curCount, curCount + 1, &impl->mStrong) == 0) { break; } curCount = impl->mStrong; } if (curCount <= 0) { decWeak(id); return false; } } else { if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) { decWeak(id); return false; } curCount = android_atomic_inc(&impl->mStrong); } if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { impl->mBase->onLastStrongRef(id); } } impl->addStrongRef(id); curCount = impl->mStrong; while (curCount >= INITIAL_STRONG_VALUE) { if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE, &impl->mStrong) == 0) { break; } curCount = impl->mStrong; } return true; }
1. 當前面存在sp的引用,即curCount > 0 && curCount != INITIAL_STRONG_VALUE,這時直接讓mStrong加1。
2.當前面不存在sp的引用,需要結合Flag去判斷。又分為以下幾種情況:
一. Flag = OBJECT_LIFETIME_STRONG,並且curCount等於0。說明之前的sp對象已經釋放,由前面的知識我們知道,在釋放sp對象的同時也會釋放對象A,所以這裡調用decWeak來釋放前面增加的一次mWeak值並返回false
二.Flag = OBJECT_LIFETIME_STRONG,並且curCount = INITIAL_STRONG_VALUE,說明前面沒有sp引用,這時我們可以增加mStrong值。
三.Flag = OBJECT_LIFETIME_WEAK,並且curCount <= 0 || curCount == INITIAL_STRONG_VALUE,則調用RefBase的onIncStrongAttempted去嘗試增加mStrong值
當上面任何一種情況增加了mStrong值以後,mSrong的值可能大於INITIAL_STRONG_VALUE,我們需要去修正mStrong,就是通過減去INITIAL_STRONG_VALUE計算。當attemptIncStrong返回true時,promote方法就會調用sp的set_pointer方法去設置StrongPointer中的實際A對象的指針。接下來就可以通過sp調用相關的方法了。
在之前文章的鋪墊下,再為大家分享一篇:Android手勢密碼,附源碼下載,不要錯過。源碼下載:http://xiazai.jb51.net/201610/yuanma/a
為使應用程序之間能夠彼此通信,Android提供了IPC (Inter Process Communication,進程間通信)的一種獨特實現: AIDL (Androi
Android的traces.txt文件記錄了進程的堆棧情況,對於分析應用響應慢,以及ANR的原因很有幫助。traces.txt文件的位置位於/data/anr/。1.
前言通過“計算器”應用我們已經熟悉了安卓應用開發的大致流程,具備了開發的初步知識。接下來,我們將開始制作一個“視頻播放器”