編輯:關於Android編程
Android的framework層都是由c++來實現的 大家都知道c++最令人頭痛的莫過於內存洩漏了 ,如果是一個人開發還好 當new出來一個對象後 應該會記得delete掉 但是當多人開發的時候難免會出現這裡調用了 那裡忘記釋放了 特別是對於Android是一個系統來說 那就更加了! 所以才有了用智能指針來處理這個問題,當沒有地方引用的時候實現自動釋放 避免內存洩漏或者野指針的問題。
在分析源碼的過程中時常會出現sp
首先我們來看看:
templateclass LightRefBase { public: inline LightRefBase() : mCount(0) { }//初始化引用的次數為0 inline void incStrong(const void* id) const {//增加一次引用 android_atomic_inc(&mCount);//原子操作 由匯編語言完成 } inline void decStrong(const void* id) const {//減少一次引用 if (android_atomic_dec(&mCount) == 1) { delete static_cast (this); } } //獲取該對象當前被引用的次數 inline int32_t getStrongCount() const { return mCount; } typedef LightRefBase basetype; protected: inline ~LightRefBase() { }//析構 private: mutable volatile int32_t mCount;//用來記錄對象的引用次數 };
template再來看看他的構造函數和拷貝構造class sp { public: inline sp() : m_ptr(0) { }//默認構造函數 他沒有初始化m_ptr sp(T* other);//初始化T 這裡的T代表是實際引用的對象 sp(const sp & other);//拷貝構造函數 同理 template sp(U* other);//帶有返回值的構造函數 template sp(const sp& other); ~sp(); // 操作符重載 sp& operator = (T* other); sp& operator = (const sp & other); template sp& operator = (const sp& other); template sp& operator = (U* other); //動態來設置T的對象 void force_set(T* other); // Reset void clear(); // 操作符重載 這樣的話當使用這些操作符 返回的就是引用的實際對象 inline T& operator* () const { return *m_ptr; } inline T* operator-> () const { return m_ptr; } inline T* get() const { return m_ptr; } // 宏定義 一些運算符 COMPARE(==) COMPARE(!=) COMPARE(>) COMPARE(<) COMPARE(<=) COMPARE(>=) private: template friend class sp; template friend class wp;//友元類 void set_pointer(T* ptr); T* m_ptr;//這裡的T就是實際引用的對象了 };
templatesp ::sp(T* other)//這裡的other實際就是LigthRefBase : m_ptr(other)//把other賦值給m_ptr { if (other) other->incStrong(this);//調用LigthRefBase的incstrong } template sp ::sp(const sp & other) : m_ptr(other.m_ptr) { if (m_ptr) m_ptr->incStrong(this);//同上面構造函數 }
看看他的析構函數:
templatesp ::~sp() { if (m_ptr) m_ptr->decStrong(this);//實際是調用LightRefBase的decStrong函數-1了 如果引用次數為0則delete該對象 }
#include現在來分析下我們寫的代碼 當執行到 sp#include using namespace android; class MyLightClass : public LightRefBase { public: MyLightClass() { printf("開始調用構造函數\n"); } virtual ~MyLightClass() { printf("析構\n"); } }; int main(int argc, char** argv) { MyLightClass* light = new MyLightClass(); sp lp = light; printf("當前的引用次數為%d\n", light->getStrongCount()); { sp lp2 = lp; printf("當前的引用次數為%d\n", light->getStrongCount()); } printf("當前的引用次數為%d\n", light->getStrongCount()); return 0; }
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_MODULE := mylight LOCAL_SRC_FILES := MyLightClass.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils include $(BUILD_EXECUTABLE)
xielinhua@ubuntu:~/Downloads/Android$ mmm ./external/mylight
編譯完成後我們就可以來打包:
xielinhua@ubuntu:~/Downloads/Android$make snod然後在打開模擬器來執行剛才編譯好的有我們寫的代碼
xielinhua@ubuntu:~/Downloads/Android$ emulator -kernel ./kernel/goldfish/arch/arm/boot/zImage &最後來執行我們剛才寫的程序看看他的打印是不是我們剛才分析的一樣呢:
xielinhua@ubuntu:~/Downloads/Android$ adb shell
root@android:/ # cd system/bin/ root@android:/system/bin # ./mylight 開始調用構造函數 當前次數為1 當前次數為2 當前次數為1 析構
恩 現在應該明白輕量指針是怎麼來控制對象的了 不過當我們遇到復雜一點的比如說A引用了B B也引用了A這種情況的話 兩者相互引用的話那麼利用上面的顯然是釋放不了的,因為程序再執行的過程中會判斷 對象的計數是否為0才釋放,假設程序只執行一邊的代碼(比如就執行A類裡面的代碼,A裡面的B引用其他類裡面的對象,但是沒有去執行那邊的類代碼)時候那麼這兩個對象都得不到釋放 因為對象減一操作好還不為0! Android中它采用了對象的引用計數同時存在強引用和弱引用兩種計數。例如A引用B則B的強引用計數和弱引用計數+1,而B引用A則A僅僅弱引用數+1,在回收時只要對象的強引用計數為0,則不管弱引用數是否為0都進行回收,類似於死鎖解決中的強制釋放資源,那現在我們來看看更強大的引用計數器 RefBase
class RefBase { public: void incStrong(const void* id) const;//增加一個引用計數 void decStrong(const void* id) const;//減少一個引用計數 void forceIncStrong(const void* id) const; class weakref_type//內部類 主要是通過這個類來實現強弱引用的計數 { public: RefBase* refBase() const;//實際對象 void incWeak(const void* id); void decWeak(const void* id);//減少弱引用的次數 bool attemptIncStrong(const void* id); //! This is only safe if you have set OBJECT_LIFETIME_FOREVER. bool attemptIncWeak(const void* id); }; weakref_type* createWeak(const void* id) const;//創建一個管理引用的對象 weakref_type* getWeakRefs() const; protected: RefBase(); virtual ~RefBase(); //! Flags for extendObjectLifetime()對象的生命周期控制 默認為0則代表是受強引用的控制 enum { OBJECT_LIFETIME_WEAK = 0x0001,//代表受弱引用的控制 OBJECT_LIFETIME_FOREVER = 0x0003//代表不受控制 自己來管理 }; void extendObjectLifetime(int32_t mode);//改變對象的生命周期的控制 //! Flags for onIncStrongAttempted() enum { FIRST_INC_STRONG = 0x0001 }; virtual void onFirstRef();//第一次強引用 子類可以重寫該方法初始化一些東西 virtual void onLastStrongRef(const void* id);//最後一次強引用了 說明該對象可能會釋放掉 virtual bool onIncStrongAttempted(uint32_t flags, const void* id); virtual void onLastWeakRef(const void* id); private: friend class weakref_type; class weakref_impl; RefBase(const RefBase& o); RefBase& operator=(const RefBase& o); weakref_impl* const mRefs;//看名字就知道weakref_type的實現類 };
RefBase::RefBase() :mRefs(new weakref_impl(this))//構造的時候 new了一個weakref_impl對象 { //mRefs是RefBase的成員變量,類型是weakref_impl }那先來看看weakref_impl類的初始化:
weakref_impl(RefBase* base) :mStrong(INITIAL_STRONG_VALUE) //強引用計數,初始值為0x1000000 ,mWeak(0) //弱引用計數,初始值為0 ,mBase(base)//所指向的實際對象 ,mFlags(0)//生命周期受什麼影響 默認是0 也就是強引用 ,mStrongRefs(NULL) ,mWeakRefs(NULL) ,mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) ,mRetain(false) { }我們再來舉一個列子:
A a=new A(); sp sa=a;這裡是new了一個A對象a 然後賦值給了 sp sa; 那現在來看看sp的構造函數:
template這裡的A如果要實現智能指針 那一定是繼承了RefBase類了,那other->incStrong(this)實際調用的是RefBase的對應方法:sp ::sp(T* other) : m_ptr(other) { if (other) other->incStrong(this);//這裡調用的是實際對象的incStrong方法 }
void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs; refs->incWeak(id);//增加弱引用的計數 refs->addStrongRef(id);//debug 正式不做處理 const int32_t c = android_atomic_inc(&refs->mStrong);//原子加1 後返回+1前的值 ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); if (c != INITIAL_STRONG_VALUE) {//判斷INITIAL_STRONG_VALUE是否等於c 如果不等於那就代表已經引用了 所以返回 return; } android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); refs->mBase->onFirstRef();//第一次被引用 }上面的方法首先調用incWeak方法增加了弱引用計數然後再增加強引用計數 那我們來看看這個函數:
void RefBase::weakref_type::incWeak(const void* id) { weakref_impl* const impl = static_cast可以看到當調用incStrong方法的時候會使強引用加1操作 同時弱引用也增加1 當sp退出作用域的時候會調用 析構函數:(this); impl->addWeakRef(id);//調試用的 這裡是空實現 const int32_t c = android_atomic_inc(&impl->mWeak);//原子操作加1 ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); }
templatesp ::~sp() { if (m_ptr)//如果m_ptr不為空則滿足條件 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); #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) {//如果對象的引用計數為0 則滿足條件 const_cast上面可以看到刪除一個強引用並且判斷是不是要刪除對象自己 然後再減少弱引用 再來看看decWeak:(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {//如果對象生命周期是受弱引用的話還不能刪除該對象 否則就delete 自己了 delete this; } } refs->removeWeakRef(id); refs->decWeak(id); }
void RefBase::weakref_type::decWeak(const void* id) { weakref_impl* const impl = static_castdecWeak函數首先是把弱引用-1操作 如果-1前的c是不等於那麼久返回 代表還有對象再引用 。如果沒有對象引用的話那麼繼續往下執行 首先判斷它的生命周期是不是受強引用的控制 如果是的話,那麼它有兩種情況 ,一種是這個對象是強引用初始化的 它會分別把強引用和弱引用計數加一;一種是沒有初始化強引用 直接是弱引用來初始化的 這樣只會增加弱引用的計數加一; 既然這樣的話 就代表強引用可能沒有初始化 所以會判斷下 如果是沒有初始化強引用 則直接刪除對象 因為若引用的計數是大於等於強引用的!而這裡的弱引用的計數為0 那肯定代表強引用是0了!如果對象曾經是被強引用過的 那麼直接刪除impl也就是計數的管理類 因為再初始化sp的時候我們new了這個對象 !因為上面再刪除強引用計數的時候已經把實際對象給刪除了 到此所有的對象也就都釋放了!這是在對象生命周期受強引用控制的時候 那如果不是的話 調用onLastWeakRef首先通知這個對象可能馬上要釋放了 再判斷該對象是不是受弱引用控制的 如果是那麼此時弱引用為0了 也就是可以刪除實際對象了 那麼此時會調用RefBase的析構 再來看看RefBase的析構函數:(this); impl->removeWeakRef(id); const int32_t c = android_atomic_dec(&impl->mWeak);//弱引用-1操作 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) {//判斷是不是初始值 如果是(代表弱引用計數不是通過強引用的方式來加1)那麼刪掉實際對象 否則刪掉管理計數的對象 也就是 weakref_impl實際對象則在Refbase中的decStrong中刪除 delete impl->mBase; } else { delete impl; } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { delete impl->mBase;//如果是受弱引用控制 那麼就刪掉實際對象 } } }
RefBase::~RefBase() { // LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); if (mRefs->mWeak == 0) {//如果弱引用計數為0 那麼刪除引用計數管理 也就是weakref_impl的對象 // LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); delete mRefs; } }
跟著上面分析繼續!在decWeak方法執行delete impl->mBase時由於這裡滿足條件 所以會刪除計數管理 現在知道為什麼在decWeak函數中 如果如果對象生命周期受強引用控制但是又不是使用強引用初始化的話 為什麼會delete impl了 因為impl是RefBase new出來的通常應該是誰new誰delete的 主要是因為RefBase的析構函數會判斷弱引用是不是為0 但是這裡的判斷是不為0 的 所以才會在這裡delete impl的! 通過上面可知 當對象a 超過了作用域的話那麼 就會調用sp的析構函數 然後就會調用RefBase的decStrong方法 減少強弱引用計數的值 根據相應的生命周期來釋放a的對象!! 下面來看看wp 也就是弱指針
template從上面可以看到有兩個成員變量 T 實際對象 也就是RefBase 類 裡面有個管理類weakref_impl* const mRefs ;weakref_type* m_refs這是用來干嘛的呢?class wp { public: typedef typename RefBase::weakref_type weakref_type; inline wp() : m_ptr(0) { }//初始化m_ptr wp(T* other); wp(const wp & other); wp(const sp & other); template wp(U* other); template wp(const sp& other); template wp(const wp& other); ~wp(); // Assignment wp& operator = (T* other); wp& operator = (const wp & other); wp& operator = (const sp & other); template wp& operator = (U* other); template wp& operator = (const wp& other); template wp& operator = (const sp& other); void set_object_and_refs(T* other, weakref_type* refs); // promotion to sp 升級成為強指針 sp promote() const; // Reset void clear(); // Accessors inline weakref_type* get_refs() const { return m_refs; } inline T* unsafe_get() const { return m_ptr; } // Operators COMPARE(==) COMPARE(!=) COMPARE(>) COMPARE(<) COMPARE(<=) COMPARE(>=) private: template friend class sp; template friend class wp; T* m_ptr;//實際對象 weakref_type* m_refs;//操作計數類管理類
template再來看看createWeak方法:wp ::wp(T* other) : m_ptr(other)//初始化m_ptr { if (other) m_refs = other->createWeak(this);//調用對象的createWeak方法 也就是RefBase的方法 }
RefBase::weakref_type* RefBase::createWeak(const void* id) const { mRefs->incWeak(id);//調用incWeak 增加對象的弱引用計數 return mRefs;//返回操作計數的管理對象 也就是weakref_type的實現類 }
template弱指針是不能操作對象的因為他沒有重載* ->操作費 但是他有一個方法升級成強指針來操作對象wp ::~wp() { if (m_ptr) m_refs->decWeak(this);//減一操作 }
templatesp的構造函數:sp wp ::promote() const { return sp (m_ptr, m_refs); }
template這裡首先要判斷p是否為空 也就是實際對象還在不在,還要判斷attemptIncStrong是否是true 是的話才能升級為強指針 那我們來看看這個函數:sp ::sp(T* p, weakref_type* refs) : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0) { }
bool RefBase::weakref_type::attemptIncStrong(const void* id) { incWeak(id);//增加弱引用 weakref_impl* const impl = static_cast(this); int32_t curCount = impl->mStrong;//強引用的個數 //如果強引用的個數大於0並且還不等於初始值 那對象有強引用 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; if (curCount == INITIAL_STRONG_VALUE) {//沒有強引用過 還是初始值 //判斷生命周期是不是受弱引用控制 onIncStrongAttempted()默認返回true allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK/*不受弱引用控制的話為true 代表允許升級*/ || 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) { decWeak(id); return false; } curCount = android_atomic_inc(&impl->mStrong); // If the strong reference count has already been incremented by // someone else, the implementor of onIncStrongAttempted() is holding // an unneeded reference. So call onLastStrongRef() here to remove it. // (No, this is not pretty.) Note that we MUST NOT do this if we // are in fact acquiring the first reference. if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { impl->mBase->onLastStrongRef(id); } } impl->addWeakRef(id); impl->addStrongRef(id); #if PRINT_REFS LOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); #endif if (curCount == INITIAL_STRONG_VALUE) { android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); impl->mBase->onFirstRef(); } return true; }
本文實例總結了Android TextView高級顯示技巧。分享給大家供大家參考,具體如下:1. 自定義字體可以使用setTypeface(Typeface)方法來設置文
許多項目都必須用到上傳圖片的功能,有了圖片會更加精彩,最近我的項目也需要選擇圖片的功能,所以把我寫的代碼共享出來,也算是筆記吧!好,廢話少說,下面看看效果圖: 效果還
本文實例講述了Android編程根據系列圖片繪制動畫的方法。分享給大家供大家參考,具體如下:一、采用系統提供的Animation類,用自帶的方法其中的animation.
Android N 仍處於活動的開發狀態,但現在您可以將其作為 N Developer Preview 的一部分進行試用。 以下部分重點介紹面向開發者的一些新功能。請務必