編輯:關於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自定義UI手勢密碼改進版源碼下載
在之前文章的鋪墊下,再為大家分享一篇:Android手勢密碼,附源碼下載,不要錯過。源碼下載:http://xiazai.jb51.net/201610/yuanma/a
Android使用AIDL實現進程間通信
為使應用程序之間能夠彼此通信,Android提供了IPC (Inter Process Communication,進程間通信)的一種獨特實現: AIDL (Androi
Android框架分析系列之Android traces.txt文件
Android的traces.txt文件記錄了進程的堆棧情況,對於分析應用響應慢,以及ANR的原因很有幫助。traces.txt文件的位置位於/data/anr/。1.
手把手教你做視頻播放器(一)
前言通過“計算器”應用我們已經熟悉了安卓應用開發的大致流程,具備了開發的初步知識。接下來,我們將開始制作一個“視頻播放器”