Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android系統篇之----Android中的智能指針

Android系統篇之----Android中的智能指針

編輯:關於Android編程

一、前言

今天我們開啟Android系統篇的文章了,其實一直想弄,只是之前一直沒有太多深入的了解,最近又把這塊拿出來好好看了一下,所以想從新梳理一下,來看看Android中的這塊知識,首先我們今天來看一下:Android中的智能指針的概念,為什麼說先看一下智能指針這個知識呢?因為我們在看Android源碼的時候,會發現幾乎好多地方都用到了這個東東,所以我們在介紹後面的知識點,先來看看這個吧。

 

二、問題

那麼Android中的智能指針是個什麼東西呢?我們知道Android用的Java語言開發的,Java語言是沒有指針這個概念的,當然Java有JNI技術,C/C++有指針的概念的,那麼為什麼叫做智能呢?智能指針到底是不是真的指針,如何做到智能,我們帶著疑惑去看看這些問題。

其實Android中的智能指針是對C++中的對象回收機制的封裝,我們知道C++中的構造函數和析構函數是在對象new出來和delete的時候調用的,但是一個在銷毀一個對象的時候,我們需要手動的調用delete關鍵字來銷毀,但是在Java中我們無需在乎這些對象的銷毀工作,都是由垃圾回收器來做了,所以在Android系統層,為了達到Java的這個自動管理對象的效果,就出現了智能指針的概念了,他的出現類似於Java中的回收器,或者是OC中的自動釋放池的功能等,他內部實現也很簡單,就是用兩個變量來控制,一個是強引用計數變量,一個是弱引用計數變量,這兩個變量都是int類型的,表示一個對象被引用多少次,兩個變量會依據具體的生命管理周期模式來決定是否釋放對象。

 

三、源碼解讀

Android中的智能指針的主要代碼是:RefBase.h和RefBase.cpp這兩個文件,他們分別位於:

RefBase.h:Android源碼目錄\frameworks\native\include\utils\RefBase.h

RefBase.cpp:Android源碼目錄\frameworks\native\libs\utils\RefBase.cpp

我們在RefBase.h中可以看到智能指針的定義。智能指針分為兩種,一個是sp,一個是wp,顧名思義,sp=strong pointer

wp=weak pointer,他們兩各有用途,後面會詳細介紹,首先來看一下他們的定義:

\

我們看到這個使用到了C++中的模板技術來定義的,關於模板大家如果不熟悉的話,可以認為是Java中的泛型機制,簡化代碼,增加通用性和安全性的。

從定義來看,我們知道sp和wp其實是一個類,並非真正意義上的指針,這就解決了我們的第一個困惑,智能指針不是真正意義上的指針。

我們看到sp和wp類中都有一個RefBase類型的變量,那麼我們在看看RefBase是個什麼東東:

\

這裡我們看到,本身RefBase有兩個方法:incStrong和decStrong,這個一看應該就知道這兩個方法就是用來操作強指針的,在RefBase內部定義了weakref_type類,這個類有兩個方法:incWeak和decWeak,這個一看應該就知道這兩個方法就是用來操作弱指針的。我們在去RefBase.cpp內部看看:

\

RefBase內部的一個weakref_impl內部類,繼承weakref_type類,他內部有四個變量:

1、mStrong:是記錄強指針的引用計數

2、mWeak:是記錄弱指針的引用計數

3、mBase:是RefBase類型的變量

4、mFlags:智能指針采用什麼樣的生命管理周期,默認是0,就是采用強生命周期來管理對象。

接著看到weakref_impl的初始化,看到:

mStrong變量初始化的值是:0x10000000

\

為什麼這裡不用0作為初始化的值,因為後面需要判斷一個狀態就是強引用計數有沒有被使用過,如果用0的話,那麼就不能區分0值代表是沒使用過,還是使用過了清零了,所以這裡不用0來做初始化值

mWeak變量的初始化的值是:0

mFlags變量的初始化的值是:0,這裡的0表示就是用強生命管理方式去管理對象的生命周期,當然還有其他的方式,看一下定義:

\

還有一個是弱管理方式,為什麼有這兩種方式,後面會說到。

 

分析到這裡,我們需要整理一下關系:

1、sp和wp是一個類,他們用來管理一個對象的生命周期,但是他們都是模本類,所有管理的對象都必須繼承RefBase類,這個RefBase類叫做真實對象,就是管理對象本身。

2、RefBase類是最核心的類,他內部的一個weakref_impl內部類,繼承weakref_type類,這個類我們一般叫做影子對象,和RefBase是相對應的。weakref_impl中有幾個變量,mStrong用來記錄對象的強引用計數,mWeak用來記錄對象的弱引用計數,mBase是RefBase類型的,也就是需要管理對象本身。mFlag是采用哪種方式去管理對象的生命周期。

3、從2中我們可以看出來,RefBase和weakref_impl這兩個類是相互依賴的,而且他們也是相互對應的,一個是真實的對象,一個是影子對象。

 

說完了關系之後,我們在看一下sp到底如何工作的:

假設現在有一個類MyClass,如果要使用智能指針來引用這個類的對象,那麼這個類需滿足下列兩個前提條件:
1)這個類是基類RefBase的子類或間接子類;
2)這個類必須定義虛構造函數,即它的構造函數需要這樣定義:
virtual ~MyClass();
滿足了上述條件的類就可以定義智能指針了,定義方法和普通指針類似。比如普通指針是這樣定義:
MyClass* p_obj;
智能指針是這樣定義:
sp p_obj;
注意不要定義成 sp* p_obj。初學者容易犯這種錯誤,這樣實際上相當於定義了一個指針的指針。盡管在語法上沒有問題,但是最好永遠不要使用這樣的定義。
定義了一個智能指針的變量,就可以象普通指針那樣使用它,包括賦值、訪問對象成員、作為函數的返回值、作為函數的參數等。比如:

 

p_obj = new MyClass(); // 注意不要寫成 p_obj = new sp  
sp p_obj2 = p_obj;  
p_obj->func();  
p_obj = create_obj();  
some_func(p_obj);  

 

注意不要試圖delete一個智能指針,即 delete p_obj。不要擔心對象的銷毀問題,智能指針的最大作用就是自動銷毀不再使用的對象。不需要再使用一個對象後,直接將指針賦值為NULL即可:
p_obj = NULL;

上面說的都是強指針,弱指針的定義方法和強指針類似,但是不能通過弱指針來訪問對象的成員。下面是弱指針的示例:

 

wp wp_obj = new MyClass();  
p_obj = wp_obj.promote(); // 升級為強指針。不過這裡要用.而不是->,真是有負其指針之名啊  
wp_obj = NULL;  
智能指針用起來是很方便,在一般情況下最好使用智能指針來代替普通指針。但是需要知道一個智能指針其實是一個對象,而不是一個真正的指針,因此其運行效率是遠遠比不上普通指針的。所以在對運行效率敏感的地方,最好還是不要使用智能指針為好。

 

 

下面我們來具體分析RefBase內部的實現,主要看incStrong,decStrong,incWeak,decWeak這幾個方法:

1、incWeak方法:

\

這裡的incWeak方法是增加弱應用計數,采用的是android_atomic_inc方法,進行的原子操作,需要注意的是,這個方法的返回值代表是增加前的值。

 

2、incStrong方法:

\

這個方法就是增加強引用計數,我們看到首先會調用weakref_impl類型變量中的incWeak方法,增加弱引用計數,然後調用:

const int32_t c = android_atomic_dec(&refs->mStrong);

來增加強引用計數,這裡是一個原子操作,返回值注意是操作前的值。接著往下看,有一個判斷:

如果強引用計數在操作前是初始化值(INITIAL_STRONG_VALUE),那麼這裡我們會將強應用計數在減去一個INITIAL_STRONG_VALUE值,那麼這時候mStrong的值就是1了,符合預期,然後在調用一下onFirstRef方法,用於初始化的操作。

 

3、decWeak方法:

\

這裡首先對弱引用計數做減一操作,如果弱引用計數為0了,那麼需要做如下判斷:

1、如果采用的是強生命周期管理方式:

1)如果沒有用過強引用計數這個變量,那麼直接刪除真實對象

2)如果用過了,那麼直接刪除影子對象

2、如果采用的是弱什麼周期管理方式,直接刪除真實對象

這裡需要注意的是,因為OBJECT_LIFETIME_MASK和OBJECT_LIFETIME_WEAK的值是一樣的都是1,這裡的兩個判斷生命管理方式的代碼也就一樣的,只是感覺第一個判斷有點傻逼了,應該是寫源碼的時候出差了。

 

4、decStrong方法:

\

這個方法中,首先對強引用計數做減一操作,如果強引用計數為0了,那麼在判斷對象的管理方式是不是強管理方式,如果是直接刪除真實對象,最後在調用一次decWeak方法,減少一次弱引用計數,因為在之前的incStrong方法中也是調用了一次incWeak方法,這裡也是對應起來了。

 

5、RefBase的析構方法:

\

我們在分析上面的decStrong方法中,沒有發現刪除影子對象的代碼,那麼影子對象什麼時候刪除呢?其實是在真實對象的析構方法中做了操作。

 

我們分析完了上面的幾個方法,下面就來整理一下:

1、incStrong和incWeak方法比較簡單,就是增加一下強引用和弱引用計數,需要注意的是,incStrong方法中會調用incWeak方法

2、decWeak方法有點復雜,要做的事有兩件:是否要釋放真實對象,是否要釋放影子對象:

1)、如果生命周期是弱引用來控制,那麼在這裡就需要做判斷,弱應用的計數是否為0,是否要釋放真實對象和影子對象
2)、如果生命周期是強引用來控制的,那麼這裡也要判斷一下,如果強引用計數為0的話,需要釋放真實對象,弱引用計數是 否為0,是否要釋放影子對象

從這裡我們可以看到:
弱引用計數是關系影子對象的,如果弱引用計數為0,那麼影子對象一定要釋放,但是真實對象不一定要釋放
強引用計數是關系真實對象的,如果強引用計數為0,那麼真實對象一定要釋放的,但是影子對象不一定釋放

3、decStrong方法主要做:就是看看是否要釋放真實對象,因為強引用和真實對象關聯的

4、RefBase的析構方法:真實對象被銷毀的時候,需要做一個工作就是釋放影子對象

釋放影子對象有兩個場景:
1)、如果強引用計數根本沒有被使用過,那麼直接釋放
2)、如果強引用計數使用過,但是采用的是非強生命周期管理方式,也是釋放

 

四、案例測試

上面其實就是介紹完了Android中的智能指針的概念和用法了,但是可能還是不太理解,所以為了更好的理解,我們需要用我們自己最熟悉的代碼去實踐一下,從寫一下,從寫的話不難,定義一個RefBase類和weakref_impl類即可,這裡我用Java來實現了,具體代碼這裡就不粘貼了,後面會給出項目的下載地址,感興趣的同學可以去看一下:

RefBase.java的定義如下:

\

這裡有一點需要注意的是,Java中的對象是自動管理的,沒有類似於C++中的析構方法,所以這裡就定義一個dealloc來模擬析構方法:

\

這裡的dealloc方法是其實是抄襲OC中的,哈哈~~

 

weakref_impl.java定義如下:

\

 

在定義一個測試類TestA.java:

\

這裡需要繼承RefBase類

 

下面就用測試代碼進行測試:

\

用兩個對象來做測試:

第一個對象用來測試:強管理生命周期方式

\

這裡我們可以看到運行結果,符合預期:

如果是采用強生命周期管理對象的話,只有當強引用計數為0的時候,才會刪除真實對象,當弱引用計數為0的時候,去刪除影子對象。

 

第二對象用來測試:弱生命周期方式

\

這裡我們可以看到運行結果,符合預期:

如果采用弱生命周期管理對象的話,只有當強引用計數和弱引用計數都為0的時候,才會刪除真實對象。

比如,這裡我們注釋一行代碼:

\

我們在運行結果:

\

發現,真實對象沒有刪除,影子對象也沒有刪除。符合預期

 

說道這裡了,還想在多說一句,就是在Android4.4之前,除了這兩種管理方式,還有一種就是:OBJECT_LIFETIME_FOREVER,看字面意思是,永久的,也就是說如果,強引用計數和弱引用計數都為0的時候,這個對象也刪除不了,只有手動的調用delete才可以銷毀對象,不過這個在Android4.4之後就廢棄了,既然說到這裡了,就在看一下吧:

在Android4.4之前的RefBase源碼中的decWeak方法定義是這樣的:

\

這裡看到了,用的是OBJECT_LIFETIME_FOREVER,因為這個值是3=0x11,OBJECT_LIFETIME_WEAK這個值是1=0x01,那麼

OBJECT_LIFETIME_FOREVER其實是包含了OBJECT_LIFETIME_WEAK這個情況的,下面我們把Java代碼改一下:

\

運行結果:

\

無論我們怎麼調用dec的各種方法,都是沒有刪除真實對象和影子對象的,只能手動的調用真實對象的dealloc方法了。不過這個管理方式已經被廢棄了,所以我們可以不用在意了。

 

五、知識梳理

到這裡我們就介紹完了Android中的智能指針的相關知識了,下面來整理一下:

1、Android中的智能指針不是真正意義上的指針,他是sp和wp的類對象,用來管理對象的生命周期的中間類。

2、Android中為什麼要采用智能指針?因為在Android系統層都是用C/C++實現的,不能自動管理對象的生命周期,所以就開發了一套可以自動管理對象的生命周期機制:智能指針

3、關於智能指針的三種生命周期管理方式:

1). 如果對象的標志位被設置為0,那麼只要發現對象的強引用計數值為0,那就會自動delete掉這個對象;
2). 如果對象的標志位被設置為OBJECT_LIFETIME_WEAK,那麼只有當對象的強引用計數和弱引用計數都為0的時候,才會 自動delete掉這個對象;
3). 如果對象的標志位被設置為OBJECT_LIFETIME_FOREVER,那麼對象就永遠不會自動被delete掉,誰new出來的對象誰 來delete掉。

 

六、補充知識點

前面說到的知識點漏掉兩個,但是個人感覺和本篇文章沒什麼關系,這裡就簡單說一下:

1、Android中的智能指針其實分為輕量級和重量級,重量級就是我們上面提到的,也是最復雜的,輕量級指針很簡單:

\

這裡直接就用一個mCount變量來控制對象的引用次數。

2、上面說到了一個sp和wp兩個類,我們知道sp是真實對象的一個指針,可以直接使用真實對象中的方法,wp是影子對象,他只是真實對象的一個引用,不能直接使用真實對象中的方法,我們從他們兩的用法既可以看出來,sp用的都是點語法,wp用的都是->語法,這個在C/C++中,點語法就是指針,->語法就是引用。

所以需要將wp升級到sp才能使用真實對象,那麼這裡需要注意的是,如果真實對象已經delete了,那麼wp升級sp之後的對象是為NULL的。

最後來看一下RefBase,weakref_impl,sp,wp他們之間的關系:

\

 

 

七、我們為什麼要介紹智能指針

開始的時候已經說了,Android系統層為了解決對象的自動管理就引入了智能指針機制,所以智能指針是我們後續文章的基礎,後面再分析系統模塊的時候,會發現很多類都用到了RefBase,比如Binder機制:

\

這個也是我們後面需要分析的一個模塊,看到了他就用到了RefBase。

 

八、總結

這一篇就介紹完了Android中的系統篇中的基礎,智能指針的知識點,當然這裡可能說的不是那麼全面,但是我們可以看懂了,自己寫一個例子來深入了解一下智能指針。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved