編輯:關於Android編程
在閱讀Android的Framework處的代碼可以發現,無處不在SP給予了我視覺上的沖擊,這個是什麼?初級的我,看這個當初就基本當成指針來用,熟不知其的內在美,於是在這裡和大家一起學習總結SP類的魅力所在。
1 SP這貨是個模板類,讓我們看下他的結構。
templateclass sp { public: inline sp() : m_ptr(0) { } sp(T* other); sp(const sp & other); template sp(U* other); template sp(const sp& other); ~sp(); // Assignment sp& operator = (T* other); sp& operator = (const sp & other); template sp& operator = (const sp& other); template sp& operator = (U* other); // Reset void clear(); // Accessors inline T& operator* () const { return *m_ptr; } inline T* operator-> () const { return m_ptr; } inline T* get() const { return m_ptr; } // Operators COMPARE(==) COMPARE(!=) COMPARE(>) COMPARE(<) COMPARE(<=) COMPARE(>=) private: template friend class sp; T* m_ptr; };
看到了上述的代碼結構,瞬間覺得其高大上,作為一個經典的模板類,精懂的人說這個類很好,其實沒有過多的去了解他只知道是更好的維護對象。這個SP指針內部有個T* m_ptr成員變量,它是真正指向我們new出來的變量。
我們以sp mA = new A();為例,作為一個模板類的構造函數,調用如下:
templatesp ::sp(T* other) : m_ptr(other) { if (other) other->incStrong(this); }
果然,new出來的A對象被存入到sp的成員變量之中,這裡看到對mA對象構建時,會調用A的一個incStrong,這個是什麼?閱讀過代碼的都知道一個RefBase類,他是比sp類更為常見的類,我們看她的結構可以發現內容都較多,但都是一些很特別的東西, 我們看下面的UML圖來分析我們之間構建的A為何要繼承與RefBase。
可以看到繼承了RefBase,那麼A的對象有東西可以玩了,調用RefBase的incStrong函數,接著看他的實現,先來個構造函數過過瘾:
RefBase::RefBase() : mRefs(new weakref_impl(this)) { }
做的東西不多,但是有一個成員變量mRefs,必然他先實現,好吧mRefs被賦值給了一個wekref_impl這個對象,傳入的this就是我們的這個new A()對象,好的接著再看看:
weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE) , mWeak(0) , mBase(base) , mFlags(0) { }
這裡有個mBase,他是一個RefBase類,故按C++的知識,一個從RefBase繼承的A類對象被賦值給了mBase,那基類的指針mBase就可以訪問類A自帶的重載的虛函數了,先留著過會就會用到。
回來我們來看這個剛才other->incStrong(),調用的是基類的incStrong,看他的實現:
void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs;//影子對象的refs 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 PRINT_REFS ALOGD("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); refs->mBase->onFirstRef();//mBase維護著繼承類實際對象this指針 }
這裡是增加了對這個指針的引用次數,最重要的一點是關注最後一行代碼,在我們寫自己類的時候一般都喜歡重載該函數,在這裡面對類對象進行必要的初始化。
refs為mRefs,就是我們類A的對象的一個繼承成員weakref_impl,該對象有個內部成員mBase,通過上面的分析可知mBase就是指向類A對象的指針,故當訪問虛函數時,實際調用的是派生類的重載函數,故最終首次建立一個sp mA 時,就也是所謂的第一次引用吧。
2.學一學Android Native的Thread類
上面介紹了很多的類,在onFirstRef裡面可以經常會遇到一個run函數,這是啟動一個新線程的方法,那就是你的類A 繼承了thread類,從上一UML圖可以看到,thread有幾個核心的函數,最重要的就是一個run函數。來看她的部分代碼:
status_t Thread::run(const char* name, int32_t priority, size_t stack) { Mutex::Autolock _l(mLock); if (mRunning) { // thread already started return INVALID_OPERATION; } // reset status and exitPending to their default value, so we can // try again after an error happened (either below, or in readyToRun()) mStatus = NO_ERROR; mExitPending = false; mThread = thread_id_t(-1); // hold a strong reference on ourself mHoldSelf = this; mRunning = true; bool res; if (mCanCallJava) { res = createThreadEtc(_threadLoop, this, name, priority, stack, &mThread);//啟動_threadLoop線程 } else { res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } if (res == false) { mStatus = UNKNOWN_ERROR; // something happened! mRunning = false; mThread = thread_id_t(-1); mHoldSelf.clear(); // "this" may have gone away after this. return UNKNOWN_ERROR; } // Do not refer to mStatus here: The thread is already running (may, in fact // already have exited with a valid mStatus result). The NO_ERROR indication // here merely indicates successfully starting the thread and does not // imply successful termination/execution. return NO_ERROR; // Exiting scope of mLock is a memory barrier and allows new thread to run }
源碼上深入的話會比較復雜,但是一點可以肯定的是調用的線程函數是_thread_Loop,看看他的實現:
int Thread::_threadLoop(void* user) { Thread* const self = static_cast(user);//派生類對象轉為基類指針 do { bool result; if (first) { first = false; self->mStatus = self->readyToRun();//直接調用繼承類的readyToRun result = (self->mStatus == NO_ERROR); ...... else result = self->threadLoop(); } }
這裡的self是什麼,實際是我們新建的一個對象,那麼首次就是調用類A的操作函數readyToRun(),隨後一般是調用threadLoop進入線程的循環,線程的退出主要由threadLoop函數的返回結果來決定。
3. 好了,下面再來看看這個這幾個比較常見的過程,會和C++的多態符合重載有緊密的關系。
比如mA->fun(),一眼看去感覺和普通的指針調用沒有區別,但你是否知道mA只是一個sp類的對象,那麼這個mA->的操作符是什麼?那就是所謂的符合重載,來看sp的幾個運算符的重載函數:
inline T& operator* () const { return *m_ptr; } inline T* operator-> () const { return m_ptr; } inline T* get() const { return m_ptr; }
很好,可以看到他的實現,不需要輸入參數,返回一個m_ptr,m_ptr是什麼,前面說過他是我們new A()出來的一個對象,那麼調用的就是類A所屬的成員函數.類似的還有:
templatesp & sp ::operator = (T* other) { if (other) other->incStrong(this); if (m_ptr) m_ptr->decStrong(this); m_ptr = other; return *this; } template template sp & sp ::operator = (const sp& other) { T* otherPtr(other.m_ptr); if (otherPtr) otherPtr->incStrong(this); if (m_ptr) m_ptr->decStrong(this); m_ptr = otherPtr; return *this; }
分別是對指針與引用的賦值操作。
上述的Android Framework裡面會經常遇到這些細節性的問題,了解這些基本類往往可以做到事半功倍,閱讀代碼更加心領神會!
近期小米公司推出一款電子產品——《小米錢包》想必大家都有所耳聞吧。不過小編也在網上看到很多網友表示錢包內資金具體提現方法不是很熟悉,
1. 功能介紹1.1 EventBusEventBus 是一個 Android 事件發布/訂閱框架,通過解耦發布者和訂閱者簡化 Android 事件傳遞,這裡的事件可以理
問題概述 在編輯框輸入內容時會彈出軟鍵盤,而手機屏幕區域有限往往會遮住輸入界面,我們先看一下問題效果圖: 輸入用戶名和密碼時,系統會彈出鍵盤,造成系統鍵盤會擋住
Android中的控件的使用方式和iOS中控件的使用方式基本相同,都是事件驅動。給控件添加事件也有接口回調和委托代理的方式。今天這篇博客就總結一下Android中常用的基