Parcel,翻譯過來是「打包」的意思。打包乾什麼呢?是爲了序列化。 html
若是要在進程之間傳遞一個整數,很簡單,直接傳就是行了;若是要傳一個字符串,就稍微複雜了點:需先分配一塊能夠容納字符串的 內存,而後將字符串複製到內存中,再傳遞(新手可能問:爲啥不直接把字符串的引用傳過去呢?學過C/C++的地球人都知道:進程有本身的內存地址空間,一 個進程中的1000地址可能在另外一個進程中是100000,java對象的引用跟本上仍是內存地址);再若是要傳遞一個類的實例呢?也是先爲類分配內存, 而後複製一份再傳遞能夠嗎?我認爲不能夠,我至少能夠找到一個理由:類中成員除了屬性還有方法,即便屬性能完整傳過去,但還有方法呢?方法是獨立於類對象 存在的,因此到另外一個進程中再引用同一個方法就要出錯了,仍是由於獨立地址空間的緣由。 java
Android開發中,很常常在各activity之間傳遞數據,而跟據Android的設計架構,即便同一個程序中的Activity都不必定運行在同 一個進程中,因此處理數據傳遞時你不能老假設兩個activity都運行於同一進程,那麼只能按進程間傳遞數據來處理,使之具備最普遍的適應性。 android
那麼到底如何在進程之間傳遞類對象呢?簡單來講能夠這樣作:在進程A中把類中的非默認值的屬性和類的惟一標誌打成包(這就叫序列化),把這個包傳遞到進 程B,進程B接收到包後,跟據類的惟一標誌把類建立出來,而後把傳來的屬性更新到類對象中,這樣進程A和進程B中就包含了兩個徹底同樣的類對象。 數組
轉自:http://blog.csdn.net/caowenbin/article/details/6532217 (做者:曹文斌) 安全
一.先從Serialize提及 性能優化
咱們都知道JAVA中的Serialize機制,譯成串行化、序列化……,其做用是能將數據對象存入字節流當中,在須要時從新生成對象。主要應用是利用外部存儲設備保存對象狀態,以及經過網絡傳輸對象等。 網絡
二.Android中的新的序列化機制 架構
在Android系統中,定位爲針對內存受限的設備,所以對性能要求更高,另外系統中採用了新的IPC(進程間通訊)機制,必然 要求使用性能更出色的對象傳輸方式。在這樣的環境下,Parcel被設計出來,其定位就是輕量級的高效的對象序列化和反序列化機制。 app
三.Parcel類的背後 ide
在Framework中有parcel類,源碼路徑是:
Frameworks/base/core/java/android/os/Parcel.java
典型的源碼片段以下:
從中咱們看到,從這個源程序文件中咱們看不到真正的功能是如何實現的,必須透過JNI往下走了。因而,Frameworks/base/core/jni/android_util_Binder.cpp中找到了線索
從這裏咱們能夠獲得的信息是函數的實現依賴於Parcel指針,所以還須要找到Parcel的類定義,注意,這裏的類已是用C++語言實現的了。
找到Frameworks/base/include/binder/parcel.h和Frameworks/base/libs/binder/parcel.cpp。終於找到了最終的實現代碼了。
有興趣的朋友能夠本身讀一下,不難理解,這裏把基本的思路總結一下:
1. 整個讀寫全是在內存中進行,主要是經過malloc()、realloc()、memcpy()等內存操做進行,因此效率比JAVA序列化中使用外部存儲器會高不少;
2. 讀寫時是4字節對齊的,能夠看到#define PAD_SIZE(s) (((s)+3)&~3)這句宏定義就是在作這件事情;
3. 若是預分配的空間不夠時newSize = ((mDataSize+len)*3)/2;會一次多分配50%;
4. 對於普通數據,使用的是mData內存地址,對於IBinder類型的數據以及FileDescriptor使用的是 mObjects內存地址。後者是經過flatten_binder()和unflatten_binder()實現的,目的是反序列化時讀出的對象就是 原對象而不用從新new一個新對象。
上一篇中咱們透過源碼看到了Parcel背後的機制,本質上把它當成一個Serialize就能夠了,只是它是在內存中完成的序列化和反序列化,利用的是連續的內存空間,所以會更加高效。
咱們接下來要說的是Parcel類如何應用。就應用程序而言,最多見使用Parcel類的場景就是在Activity間傳遞數據。沒錯,在Activity間使用Intent傳遞數據的時候,能夠經過Parcelable機制傳遞複雜的對象。
在下面的程序中,MyColor用於保存一個顏色值,MainActivity在用戶點擊屏幕時將MyColor對象設成紅色, 傳遞到SubActivity中,此時SubActivity的TextView顯示爲紅色的背景;當點擊SubActivity時,將顏色值改成綠色, 返回MainActivity,指望的是MainActivity的TextView顯示綠色背景。
來看一下MyColor類的實現代碼:
該類實現了Parcelable接口,提供了默認的構造函數,同時也提供了可從Parcel對象開始的構造函數,另外還實現了一個static的構造器用於構造對象和數組。代碼很簡單,不一一解釋了。
再看MainActivity的代碼:
下面是SubActivity的代碼:
下面是main.xml的代碼:
注意的是在MainActivity的onActivityResult()中,有一句 color=data.getParcelableExtra("MyColor"),這說明的是反序列化後是一個新的MyColor對象,所以要想使用 這個對象,咱們作了這個賦值語句。
記得在上一篇《探索Android中的Parcel機制(上)》 中提到,若是數據自己是IBinder類型,那麼反序列化的結果就是原對象,而不是新建的對象,很顯然,若是是這樣的話,在反序列化後在 MainActivity中就再也不須要color=data.getParcelableExtra("MyColor")這句了。所以,換一種 MyColor的實現方法,令其中的int color成員變量使用IBinder類型的成員變量來表示。
新建一個BinderData類繼承自Binder,代碼以下:
修改MyColor的代碼以下:
去掉MainActivity的onActivityResult()中的color=data.getParcelableExtra("MyColor")一句,變成:
再次運行程序,結果符合預期。
以上就是Parcel在應用程序中的使用方法,與Serialize仍是挺類似的,詳細的資料固然仍是要參考Android SDK的開發文檔了。
android 中Parcel 的使用,他是一個存儲基本數據類型和引用數據類型的容器,在andorid 中經過IBinder來綁定數據在進程間傳遞數據。
Parcel parcel = Parcel.obtain();// 獲取一個Parcel 對象
下面就能夠對其進行方法進行操做了,createXXX(),wirteXXX(),readXXX(),
其中 dataPosition(),返回當前Parcel 當前對象存儲數據的偏移量,而setDataPosition(),設置當前Parcel 對象的偏移量,方便讀取parcel 中的數據,可問題就出在我讀取出來的數據要麼是空(null),要麼永遠是第一個偏移量處的值,存儲和讀取數據的。Parcel採用什麼機制實現的,是以 什麼形式的存儲的,而後我才能任意對其操做,讀取目標數據。
基本數據類型的取值範圍,
boolean 1bit
short 16bit
int 32bit
long 64bit
float 32bit
double 64bit
char 16bit
byte 8bit
由此我 能夠猜測,Parcel 32bit 做爲基本單位存儲寫入的變量,4byte*8=32bit,在內存中的引用地址變量是採用16進制進行編碼,且做爲偏移量,即偏移量是4的倍 數,0,4,8,12,16,20,24,28,32,36,40,44,48......4*N,
f(x) = 4*y{y>=0&y是天然數}
我想絕對不會出現向偏移量是3,6,9這樣的數據。。。
由此咱們能夠推斷出,不管他存儲的是基本數據類型或引用數據類型的變量,都是以32bit基本單位做爲偏移量,
parcel.writeInt(1);
parcel.writeInt(2);
parcel.writeInt(3);
parcel.writeInt(4);
parcel.writeInt(5);
parcel.writeInt(6);
parcel.writeInt(7);
parcel.writeInt(81011111);
parcel.writeFloat(1f);
parcel.writeFloat(1000000000000000000000000000000000000f);
parcel.writeXXX(), 每寫一次數據,在32bit的空間裏可以存儲要放入的變量,怎只佔一個偏移量,也就之一動4個位置,而當存儲的數據如 parcel.writeFloat(1000000000000000000000000000000000000f);他就自動日後移動,
parcel.writeString("a");
parcel.writeString("b");
parcel.writeString("d");
parcel.writeString("c");
和
parcel.writeString("abcd"); 的區別。有此可見,他的內存的分配原來是這樣的。
那我怎樣才能把我存進去的書據依次的去出來呢?setDataPosition(),設置parcel 的偏移量,在readXXX(),讀取數據
int size = parcel.dataSize();
int i = 0;
while (i <= size ) {
parcel.setDataPosition(i);
int curr_int = parcel.readInt();
i+=4;
int j = 0;
j++;
}
由此可 見parcel 寫入數據是按照32bit 爲基本的容器,依次存儲寫入的數據,基本和引用(其實引用的也是有多個基本數據類型組合而成OBJECTS-屬性|方法),讀取的時候咱們就能夠按照這種 規律根據目標數據的偏移量的位置(curr_position),以及偏移量的大小(size),,取出已經存進去的數據了
int i = curr_position;
while (i <= size ) {
parcel.setDataPosition(i);
int curr_int = parcel.readXXXt();
i+=4;
int j = 0;
j++;
}
這樣就ok 了
他的createXXX()方法如今沒用,用了在說吧!
總結一句話,java 中 基本數據類型的取值範圍,引用類型的數據,至關於c中的指針,以及各進制之間的相互轉換和靈活的引用,以及定製本身想要的任意進制數據類型。
轉自:http://blog.csdn.net/nkmnkm/article/details/6453391
上回書解釋了IBinder,這回詳細解釋一下Parcel,如下是對android sdk 文檔的翻議:
Parcel是一個容器,它主要用於存儲序列化數據,而後能夠經過Binder在進程間傳遞這些數據(要了解爲何要序列化,請參考:http://blog.csdn.net/nkmnkm/archive/2011/05/28/6451699.aspx)。Parcel能夠包含原始數據類型(用各類對應的方法寫入,好比writeInt(),writeFloat()等),能夠包含Parcelable對象,它還包含了一個活動的IBinder對象的引用,這個引用致使另外一端接收到一個指向這個IBinder的代理IBinder。
注:Parcel不是通常目的的序列化機制。這個類被設計用於高性能的IPC傳輸。所以不適合把Parcel寫入永久化存儲中,由於Parcel中的數據類型的實現的改變會致使舊版的數據不可讀。
Parcel的一坨一坨的API用於解決不一樣類型數據的讀寫。這些函數們主要有六種類型。
1原始類
這類方法們主要讀寫原始數據類型。它們是:writeByte(byte), readByte(), writeDouble(double), readDouble(), writeFloat(float), readFloat(), writeInt(int), readInt(), writeLong(long), readLong(), writeString(String), readString(). 大多數其它數據的操做都是基於這些方法。
2原始數組類
這類方法用於讀寫原始數據組成的數組。在向數組寫數據時先寫入數組的長度再寫入數據。讀數組的方法能夠將數據讀到已存在的數組中,也能夠建立並返回一個新數組。它們是:
3 Parcelable類
Parcelable爲對象從Parcel中讀寫本身提供了極其高效的協議。你可使用直接的方法 writeParcelable(Parcelable, int) 和 readParcelable(ClassLoader) 或 writeParcelableArray(T[], int) and readParcelableArray(ClassLoader) 進行讀寫。這些方法們把類的信息和數據都寫入Parcel,以使未來能使用合適的類裝載器從新構造類的實例。
還有一些方法提供了更高效的操做Parcelable們的途徑,它們是:writeTypedArray(T[], int), writeTypedList(List), readTypedArray(T[], Parcelable.Creator) and readTypedList(List, Parcelable.Creator)。這些方法不會寫入類的信息,取而代之的是:讀取時必須能知道數據屬於哪一個類並傳入正確的 Parcelable.Creator來建立對象而不是直接構造新對象。(更加高效的讀寫單個Parcelable對象的方法是:直接調用 Parcelable.writeToParcel()和Parcelable.Creator.createFromParcel())
4 Bundles類
Bundles是一種類型安全的Map型容器,可用於存儲任何不一樣類型的數據。它具備不少對讀寫數據的性能優化,而且它的類型安全機制避免了當把它的數據 封送到Parcel中時因爲類型錯誤引發的BUG的調試的麻煩,可使用的方法爲: writeBundle(Bundle), readBundle(), and readBundle(ClassLoader)。
5 活動對象類
Parcel的一個非同尋常的特性是讀寫活對象的能力。對於活動對象,它們的內容實際上並無寫入,而是僅寫入了一個令牌來引用這個對象。當從Parcel中讀取這個對象時,你不會獲取一個新的對象實例,而是直接獲得那個寫入的對象。有兩種活動對象可操做:
Binder對象。它是 Android跨進程通信的基礎。這種對象可被寫入Parcel,並在讀取時你將獲得原始的對象或一個代理對象(能夠想象:在進程內時獲得原始的對象,在 進程間時獲得代理對象)。可使用的方法們是: writeStrongBinder(IBinder), writeStrongInterface(IInterface), readStrongBinder(), writeBinderArray(IBinder[]), readBinderArray(IBinder[]), createBinderArray(), writeBinderList(List), readBinderList(List), createBinderArrayList()。
FileDescriptor對象。 它表明了原始的Linux文件描述符,它能夠被寫入Parcel並在讀取時返回一個ParcelFileDescriptor對象用於操做原始的文件描述 符。ParcelFileDescriptor是原始描述符的一個複製:對象和fd不一樣,可是都操做於同一文件流,使用同一個文件位置指針,等等。可使 用的方法是:writeFileDescriptor(FileDescriptor), readFileDescriptor()。
6無類型容器類
一類final方法,用於讀寫標準的java容器類。這些方法們是:writeArray(Object[]), readArray(ClassLoader), writeList(List), readList(List, ClassLoader), readArrayList(ClassLoader), writeMap(Map), readMap(Map, ClassLoader), writeSparseArray(SparseArray), readSparseArray(ClassLoader)。