探索Android中的Parcel

1、Android中的Parcel是什麼

    Parcel,翻譯過來是「打包」的意思。打包乾什麼呢?是爲了序列化。 html

    若是要在進程之間傳遞一個整數,很簡單,直接傳就是行了;若是要傳一個字符串,就稍微複雜了點:需先分配一塊能夠容納字符串的 內存,而後將字符串複製到內存中,再傳遞(新手可能問:爲啥不直接把字符串的引用傳過去呢?學過C/C++的地球人都知道:進程有本身的內存地址空間,一 個進程中的1000地址可能在另外一個進程中是100000,java對象的引用跟本上仍是內存地址);再若是要傳遞一個類的實例呢?也是先爲類分配內存, 而後複製一份再傳遞能夠嗎?我認爲不能夠,我至少能夠找到一個理由:類中成員除了屬性還有方法,即便屬性能完整傳過去,但還有方法呢?方法是獨立於類對象 存在的,因此到另外一個進程中再引用同一個方法就要出錯了,仍是由於獨立地址空間的緣由。 java

    Android開發中,很常常在各activity之間傳遞數據,而跟據Android的設計架構,即便同一個程序中的Activity都不必定運行在同 一個進程中,因此處理數據傳遞時你不能老假設兩個activity都運行於同一進程,那麼只能按進程間傳遞數據來處理,使之具備最普遍的適應性。 android

   那麼到底如何在進程之間傳遞類對象呢?簡單來講能夠這樣作:在進程A中把類中的非默認值的屬性和類的惟一標誌打成包(這就叫序列化),把這個包傳遞到進 程B,進程B接收到包後,跟據類的惟一標誌把類建立出來,而後把傳來的屬性更新到類對象中,這樣進程A和進程B中就包含了兩個徹底同樣的類對象。 數組

2、 探索Android中的Parcel機制(上)

轉自: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

典型的源碼片段以下:

  1. /**   
  2.  * Write an integer value into the parcel at the current dataPosition(),   
  3.  * growing dataCapacity() if needed.   
  4.  */    
  5. public final native void writeInt(int val);    
  6.     
  7. /**   
  8.  * Write a long integer value into the parcel at the current dataPosition(),   
  9.  * growing dataCapacity() if needed.   
  10.  */    
  11. public final native void writeLong(long val);   
 

         從中咱們看到,從這個源程序文件中咱們看不到真正的功能是如何實現的,必須透過JNI往下走了。因而,Frameworks/base/core/jni/android_util_Binder.cpp中找到了線索

  1. static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val)    
  2. {    
  3.     Parcel* parcel = parcelForJavaObject(env, clazz);    
  4.     if (parcel != NULL) {    
  5.         const status_t err = parcel->writeInt32(val);    
  6.         if (err != NO_ERROR) {    
  7.             jniThrowException(env, "java/lang/OutOfMemoryError", NULL);    
  8.         }    
  9.     }    
  10. }    
  11.     
  12. static void android_os_Parcel_writeLong(JNIEnv* env, jobject clazz, jlong val)    
  13. {    
  14.     Parcel* parcel = parcelForJavaObject(env, clazz);    
  15.     if (parcel != NULL) {    
  16.         const status_t err = parcel->writeInt64(val);    
  17.         if (err != NO_ERROR) {    
  18.             jniThrowException(env, "java/lang/OutOfMemoryError", NULL);    
  19.         }    
  20.     }    
  21. }    

         從這裏咱們能夠獲得的信息是函數的實現依賴於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一個新對象。

3、探索Android中的Parcel機制(下)

       上一篇中咱們透過源碼看到了Parcel背後的機制,本質上把它當成一個Serialize就能夠了,只是它是在內存中完成的序列化和反序列化,利用的是連續的內存空間,所以會更加高效。

         咱們接下來要說的是Parcel類如何應用。就應用程序而言,最多見使用Parcel類的場景就是在Activity間傳遞數據。沒錯,在Activity間使用Intent傳遞數據的時候,能夠經過Parcelable機制傳遞複雜的對象。

         在下面的程序中,MyColor用於保存一個顏色值,MainActivity在用戶點擊屏幕時將MyColor對象設成紅色, 傳遞到SubActivity中,此時SubActivity的TextView顯示爲紅色的背景;當點擊SubActivity時,將顏色值改成綠色, 返回MainActivity,指望的是MainActivity的TextView顯示綠色背景。

         來看一下MyColor類的實現代碼:

  1. package com.wenbin.test;    
  2.     
  3. import android.graphics.Color;    
  4. import android.os.Parcel;    
  5. import android.os.Parcelable;    
  6.     
  7. /**   
  8.  * @author 曹文斌   
  9.  * http://blog.csdn.net/caowenbin   
  10.  *   
  11.  */    
  12. public class MyColor implements Parcelable {    
  13.     private int color=Color.BLACK;    
  14.         
  15.     MyColor(){    
  16.         color=Color.BLACK;    
  17.     }    
  18.         
  19.     MyColor(Parcel in){    
  20.         color=in.readInt();    
  21.     }    
  22.         
  23.     public int getColor(){    
  24.         return color;    
  25.     }    
  26.         
  27.     public void setColor(int color){    
  28.         this.color=color;    
  29.     }    
  30.         
  31.     @Override    
  32.     public int describeContents() {    
  33.         return 0;    
  34.     }    
  35.     
  36.     @Override    
  37.     public void writeToParcel(Parcel dest, int flags) {    
  38.         dest.writeInt(color);    
  39.     }    
  40.     
  41.     public static final Parcelable.Creator<MyColor> CREATOR    
  42.         = new Parcelable.Creator<MyColor>() {    
  43.         public MyColor createFromParcel(Parcel in) {    
  44.             return new MyColor(in);    
  45.         }    
  46.             
  47.         public MyColor[] newArray(int size) {    
  48.             return new MyColor[size];    
  49.         }    
  50.     };    
  51. }    


         該類實現了Parcelable接口,提供了默認的構造函數,同時也提供了可從Parcel對象開始的構造函數,另外還實現了一個static的構造器用於構造對象和數組。代碼很簡單,不一一解釋了。

         再看MainActivity的代碼:

 

  1. package com.wenbin.test;    
  2.     
  3. import android.app.Activity;    
  4. import android.content.Intent;    
  5. import android.graphics.Color;    
  6. import android.os.Bundle;    
  7. import android.view.MotionEvent;    
  8.     
  9. /**   
  10.  * @author 曹文斌   
  11.  * http://blog.csdn.net/caowenbin   
  12.  *   
  13.  */    
  14. public class MainActivity extends Activity {    
  15.     private final int SUB_ACTIVITY=0;    
  16.     private MyColor color=new MyColor();    
  17.         
  18.     @Override    
  19.     public void onCreate(Bundle savedInstanceState) {    
  20.         super.onCreate(savedInstanceState);    
  21.         setContentView(R.layout.main);    
  22.     }    
  23.     
  24.     @Override    
  25.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {    
  26.         super.onActivityResult(requestCode, resultCode, data);    
  27.         if (requestCode==SUB_ACTIVITY){    
  28.             if (resultCode==RESULT_OK){    
  29.                 if (data.hasExtra("MyColor")){    
  30.                     color=data.getParcelableExtra("MyColor");  //Notice    
  31.                     findViewById(R.id.text).setBackgroundColor(color.getColor());    
  32.                 }    
  33.             }    
  34.         }    
  35.     }    
  36.     
  37.     @Override    
  38.     public boolean onTouchEvent(MotionEvent event){    
  39.         if (event.getAction()==MotionEvent.ACTION_UP){    
  40.             Intent intent=new Intent();    
  41.             intent.setClass(this, SubActivity.class);    
  42.             color.setColor(Color.RED);    
  43.             intent.putExtra("MyColor", color);    
  44.             startActivityForResult(intent,SUB_ACTIVITY);        
  45.         }    
  46.         return super.onTouchEvent(event);    
  47.     }    
  48.     
  49. }    

        下面是SubActivity的代碼:

 

  1. package com.wenbin.test;    
  2.     
  3. import android.app.Activity;    
  4. import android.content.Intent;    
  5. import android.graphics.Color;    
  6. import android.os.Bundle;    
  7. import android.view.MotionEvent;    
  8. import android.widget.TextView;    
  9.     
  10. /**   
  11.  * @author 曹文斌   
  12.  * http://blog.csdn.net/caowenbin   
  13.  *   
  14.  */    
  15. public class SubActivity extends Activity {    
  16.     private MyColor color;    
  17.         
  18.     @Override    
  19.     public void onCreate(Bundle savedInstanceState) {    
  20.         super.onCreate(savedInstanceState);    
  21.         setContentView(R.layout.main);    
  22.         ((TextView)findViewById(R.id.text)).setText("SubActivity");    
  23.         Intent intent=getIntent();    
  24.         if (intent!=null){    
  25.             if (intent.hasExtra("MyColor")){    
  26.                 color=intent.getParcelableExtra("MyColor");    
  27.                 findViewById(R.id.text).setBackgroundColor(color.getColor());    
  28.             }    
  29.         }    
  30.     }    
  31.         
  32.     @Override    
  33.     public boolean onTouchEvent(MotionEvent event){    
  34.         if (event.getAction()==MotionEvent.ACTION_UP){    
  35.             Intent intent=new Intent();    
  36.             if (color!=null){    
  37.                 color.setColor(Color.GREEN);    
  38.                 intent.putExtra("MyColor", color);    
  39.             }    
  40.             setResult(RESULT_OK,intent);    
  41.             finish();    
  42.         }    
  43.         return super.onTouchEvent(event);    
  44.     }    
  45. }    

        下面是main.xml的代碼:

 

  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
  3.     android:orientation="vertical"    
  4.     android:layout_width="fill_parent"    
  5.     android:layout_height="fill_parent"    
  6.     >    
  7. <TextView      
  8.     android:layout_width="fill_parent"     
  9.     android:layout_height="wrap_content"     
  10.     android:text="@string/hello"    
  11.     android:id="@+id/text"    
  12.     />    
  13. </LinearLayout>    

        注意的是在MainActivity的onActivityResult()中,有一句 color=data.getParcelableExtra("MyColor"),這說明的是反序列化後是一個新的MyColor對象,所以要想使用 這個對象,咱們作了這個賦值語句。

         記得在上一篇《探索Android中的Parcel機制(上)》 中提到,若是數據自己是IBinder類型,那麼反序列化的結果就是原對象,而不是新建的對象,很顯然,若是是這樣的話,在反序列化後在 MainActivity中就再也不須要color=data.getParcelableExtra("MyColor")這句了。所以,換一種 MyColor的實現方法,令其中的int color成員變量使用IBinder類型的成員變量來表示。

         新建一個BinderData類繼承自Binder,代碼以下:

 

  1. package com.wenbin.test;    
  2.     
  3. import android.os.Binder;    
  4.     
  5. /**   
  6.  * @author 曹文斌   
  7.  * http://blog.csdn.net/caowenbin   
  8.  *   
  9.  */    
  10. public class BinderData extends Binder {    
  11.     public int color;    
  12. }    

  

       修改MyColor的代碼以下:

 

  1. package com.wenbin.test;    
  2.     
  3. import android.graphics.Color;    
  4. import android.os.Parcel;    
  5. import android.os.Parcelable;    
  6.     
  7. /**   
  8.  * @author 曹文斌   
  9.  * http://blog.csdn.net/caowenbin   
  10.  *   
  11.  */    
  12. public class MyColor implements Parcelable {    
  13.     private BinderData data=new BinderData();    
  14.         
  15.     MyColor(){    
  16.         data.color=Color.BLACK;    
  17.     }    
  18.         
  19.     MyColor(Parcel in){    
  20.         data=(BinderData) in.readValue(BinderData.class.getClassLoader());    
  21.     }    
  22.         
  23.     public int getColor(){    
  24.         return data.color;    
  25.     }    
  26.         
  27.     public void setColor(int color){    
  28.         data.color=color;    
  29.     }    
  30.         
  31.     @Override    
  32.     public int describeContents() {    
  33.         return 0;    
  34.     }    
  35.     
  36.     @Override    
  37.     public void writeToParcel(Parcel dest, int flags) {    
  38.         dest.writeValue(data);    
  39.     }    
  40.     
  41.     public static final Parcelable.Creator<MyColor> CREATOR    
  42.         = new Parcelable.Creator<MyColor>() {    
  43.         public MyColor createFromParcel(Parcel in) {    
  44.             return new MyColor(in);    
  45.         }    
  46.             
  47.         public MyColor[] newArray(int size) {    
  48.             return new MyColor[size];    
  49.         }    
  50.     };    
  51. }    

         去掉MainActivity的onActivityResult()中的color=data.getParcelableExtra("MyColor")一句,變成:

 

  1. @Override    
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {    
  3.     super.onActivityResult(requestCode, resultCode, data);    
  4.     if (requestCode==SUB_ACTIVITY){    
  5.         if (resultCode==RESULT_OK){    
  6.             if (data.hasExtra("MyColor")){    
  7.                 findViewById(R.id.text).setBackgroundColor(color.getColor());    
  8.             }    
  9.         }    
  10.     }    
  11. }    

 再次運行程序,結果符合預期。

         以上就是Parcel在應用程序中的使用方法,與Serialize仍是挺類似的,詳細的資料固然仍是要參考Android SDK的開發文檔了。



3、Android Parcel理解

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中的指針,以及各進制之間的相互轉換和靈活的引用,以及定製本身想要的任意進制數據類型。


4、 Android開發:什麼是Parcel(2)

轉自: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原始數組類

這類方法用於讀寫原始數據組成的數組。在向數組寫數據時先寫入數組的長度再寫入數據。讀數組的方法能夠將數據讀到已存在的數組中,也能夠建立並返回一個新數組。它們是:

  • writeBooleanArray(boolean[]), readBooleanArray(boolean[]), createBooleanArray()
    writeByteArray(byte[]), writeByteArray(byte[], int, int), readByteArray(byte[]), createByteArray() 
    writeCharArray(char[]), readCharArray(char[]), createCharArray() 
    writeDoubleArray(double[]), readDoubleArray(double[]), createDoubleArray() 
    writeFloatArray(float[]), readFloatArray(float[]), createFloatArray() 
    writeIntArray(int[]), readIntArray(int[]), createIntArray() 
    writeLongArray(long[]), readLongArray(long[]), createLongArray() 
    writeStringArray(String[]), readStringArray(String[]), createStringArray(). 
    writeSparseBooleanArray(SparseBooleanArray), readSparseBooleanArray().

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)。

相關文章
相關標籤/搜索