Android中Parcel的分析以及使用

來源:互聯網
上載者:User

簡單點來說:Parcel就是一個存放讀取資料的容器, android系統中的binder處理序間通訊(IPC)就使用了Parcel類來進行用戶端與服務端資料的互動,而且AIDL的資料也是通過Parcel來互動的。在Java空間和C++都實現了Parcel,由於它在C/C++中,直接使用了記憶體來讀取資料,因此,它更有效率。

             分析Binder機制中的用戶端與伺服器端進行實際操作ontransact()函數 :

  1. //參數說明:  
  2. // code :是請求的ID號   
  3. // data :用戶端請求發送的參數  
  4. // reply:伺服器端返回的結果  
  5. // flags:一些額外的標識,如FLAG_ONEWAY等,通常為0.  
  6. virtual status_t    onTransact( uint32_t code,  
  7.                                 const Parcel& data,  
  8.                                 Parcel* reply,  
  9.                                 uint32_t flags = 0);  

複製代碼

從中我們可以看到Parcel的重要性以及窺探它的使用方式,接下來,我主要分析它的儲存機制。  

 

    常用方法介紹:

            obtain()                          獲得一個新的parcel ,相當於new一個對象

            dataSize()                      得到當前parcel對象的實際儲存空間

            dataCapacity()               得到當前parcel對象的已指派的儲存空間, >=dataSize()值  (以空間換時間)

            dataPostion()                 獲得當前parcel對象的位移量(類似於檔案流指標的位移量)

            setDataPosition()           設定位移量

            recyle()                           清空、回收parcel對象的記憶體

            writeInt(int)                     寫入一個整數

            writeFloat(float)              寫入一個浮點數

            writeDouble(double)       寫入一個雙精確度數

            writeString(string)           寫入一個字串

         當然,還有更多的writeXXX()方法,與之對應的就是readXXX(),具體方法請參閱SDK。

          其中幾個值得注意的方法為:

             writeException()        在Parcel隊頭寫入一個異常

             writeException()        Parcel隊頭寫入“無異常“

             readException()        在Parcel隊頭讀取,若讀取值為異常,則拋出該異常;否則,程式正常運行。

一、Parcel的分析

       相信看了前面的值,對Parcel的使用該有了初步印象。那麼,Parcel的內部儲存機制是怎麼樣的?位移量又是

  什麼情況?讓我們回憶一下基礎資料型別 (Elementary Data Type)的取值範圍:

                   boolean     1bit          1位元組

                   char          16bit         2位元組

                   int             32bit        4位元組

                   long          64bit        8位元組

                   float          32bit        4位元組

                  double       64bit         8位元組

 

        如果大家對C語言熟悉的話,C語言中結構體的記憶體對齊和Parcel採用的記憶體存放機制一樣,即讀取最小位元組

為32bit,也即4個位元組。高於4個位元組的,以實際資料類型進行存放,但得為4byte的倍數。基本公式如下:

             實際存放位元組:

                       判別一:  32bit      (<=32bit)             例如:boolean,char,int

                       判別二:  實際佔用位元組(>32bit)     例如:long,float,String,數組等

        當我們使用readXXX()方法時,讀取方法也如上述:

              實際讀取位元組:

                        判別一:  32bit      (<=32bit)            例如:boolean,char,int

                        判別二:  實際位元組大小(>32bit)     例如:long,float,String,數值等

      由上可以知道,當我們寫入/讀取一個資料時,位移量至少為4byte(32bit),於是,位移量的公式如下:

                 f(x)= 4x  (x=0,1,…n)

        事實上,我們可以顯示的通過setDataPostion(int postion) 來直接操作我們欲讀取資料時的位移量。毫無疑問,

你可以設定任何位移量,但所讀取的值是類型可能有誤。因此顯示設定位移量讀取值的時候,需要小心。

 

      另外一個注意點就是我們在writeXXX()和readXXX()時,導致的位移量是共用的,例如,我們在writeInt(23)後,

此時的datapostion=4,如果我們想讀取5,簡單的通過readInt()是不行的,只能得到0。這時我們只能通過

setDataPosition(0)設定為起始位移量,從起始位置讀取四個位元組,即23。因此,在讀取某個值時,可能需要使用

setDataPostion(int postion)使位移量裝換到我們的值處。

 

      巧用setDataPosition()方法,當我們的parcel對象中只存在某一類型時,我們就可以通過這個方法來快速的讀取

所有值。具體方法如下:

 

  1. view plainprint?
  2. /**  
  3.      * 前提條件,Parcel存在多個類型相同的對象,本例子以10個float對象說明:  
  4.      */  
  5.     public void readSameType() {  
  6.         Parcel parcel =Parcel.obtain() ;  
  7.         for (int i = 0; i < 10; i++) {  
  8.             parcel.writeDouble(i);  
  9.             Log.i(TAG, "write double ----> " + getParcelInfo());  
  10.         }  
  11.         //方法一 ,顯示設定位移量   
  12.         int i = 0;  
  13.         int datasize = parcel.dataSize();  
  14.         while (i < datasize) {  
  15.             parcel.setDataPosition(i);  
  16.             double fvalue = parcel.readDouble();  
  17.             Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  
  18.             i += 8; // double佔用位元組為 8byte   
  19.         }  
  20. //      方法二,由於對象的類型一致,我們可以直接利用readXXX()讀取值會產生位移量  
  21. //      parcel.setDataPosition(0)  ;  //  
  22. //      while(parcel.dataPosition()<parcel.dataSize()){  
  23. //          double fvalue = parcel.readDouble();  
  24. //          Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  
  25. //      }  
  26.     }  

複製代碼

由於可能存在讀取值的偏差,一個預設的取值規範為:

             1、  讀取複雜物件時: 對象匹配時,返回當前位移位置的該對象;

                               對象不匹配時,返回null對象 ;

             2、  讀取簡單對象時: 對象匹配時,返回當前位移位置的該對象 ;

                               對象不匹配時,返回0;

     下面,給出一張淺顯的Parcel的存放空間圖,希望大家在理解的同時,更能體味其中滋味。有點簡單,求諒解。

 

 

相信通過前面的介紹,你一定很瞭解了了Parcel的儲存機制,下面給定一應用程式來實踐。

     1、布局檔案如下:

  1. <span ><?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical" android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent">  
  5.     <TextView android:layout_width="fill_parent"  
  6.         android:layout_height="wrap_content" android:text="@string/hello" />  
  7.     <LinearLayout android:orientation="horizontal"  
  8.         android:layout_width="fill_parent" android:layout_height="wrap_content">  
  9.         <Button android:id="@+id/btWriteByte" android:layout_width="wrap_content"  
  10.             android:layout_height="wrap_content" android:text="寫入一個byte值"></Button>  
  11.         <Button android:id="@+id/btWriteInt" android:layout_width="wrap_content"  
  12.             android:layout_height="wrap_content" android:text="寫入一個int值"></Button>  
  13.     </LinearLayout>  
  14.     <LinearLayout android:orientation="horizontal"  
  15.         android:layout_width="fill_parent" android:layout_height="wrap_content">  
  16.         <Button android:id="@+id/btWriteDouble" android:layout_width="wrap_content"  
  17.             android:layout_height="wrap_content" android:text="寫入一個double值"></Button>  
  18.         <Button android:id="@+id/btWriteString" android:layout_width="wrap_content"  
  19.             android:layout_height="wrap_content" android:text="寫入一個String值"></Button>  
  20.     </LinearLayout>  
  21.     <View android:layout_width="fill_parent" android:layout_height="2dip"  
  22.         android:background="#FF1493"></View>  
  23.     <LinearLayout android:orientation="horizontal"  
  24.         android:layout_marginTop="5dip" android:layout_width="fill_parent"  
  25.         android:layout_height="wrap_content">  
  26.         <Button android:id="@+id/btReadByte" android:layout_width="wrap_content"  
  27.             android:layout_height="wrap_content" android:text="讀取一個byte值"></Button>  
  28.         <Button android:id="@+id/btReadInt" android:layout_width="wrap_content"  
  29.             android:layout_height="wrap_content" android:text="讀取一個int值"></Button>  
  30.     </LinearLayout>  
  31.     <LinearLayout android:orientation="horizontal"  
  32.         android:layout_width="fill_parent" android:layout_height="wrap_content">  
  33.         <Button android:id="@+id/btReadDouble" android:layout_width="wrap_content"  
  34.             android:layout_height="wrap_content" android:text="讀取一個double值"></Button>  
  35.         <Button android:id="@+id/btReadString" android:layout_width="wrap_content"  
  36.             android:layout_height="wrap_content" android:text="讀取一個String值"></Button>  
  37.     </LinearLayout>  
  38.     <View android:layout_width="fill_parent" android:layout_height="2dip"  
  39.         android:background="#FF1493"></View>  
  40.     <Button android:id="@+id/btSameType" android:layout_width="wrap_content"  
  41.         android:layout_height="wrap_content" android:text="利用setDataPosition讀取多個值"></Button>  
  42. </LinearLayout>  

複製代碼

2、設定檔如下:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.       package="com.qinjuning.parcel"  
  4.       android:versionCode="1"  
  5.       android:versionName="1.0">  
  6.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
  7.         <activity android:name=".MainActivity"  android:label="@string/app_name">  
  8.             <intent-filter>  
  9.                 <action android:name="android.intent.action.MAIN" />  
  10.                 <category android:name="android.intent.category.LAUNCHER" />  
  11.             </intent-filter>  
  12.         </activity>  
  13.     </application>  
  14. </manifest>   

複製代碼

3、程式主檔案如下:

  1. <span >public class MainActivity extends Activity implements OnClickListener {  
  2.   
  3.     private static String TAG = "PARCELTEST";  
  4.     // Button ID  
  5.     private static int[] btIds = new int[] { R.id.btWriteByte, R.id.btWriteInt,  
  6.             R.id.btReadDouble, R.id.btWriteString, R.id.btReadByte,  
  7.             R.id.btReadInt, R.id.btReadDouble, R.id.btReadString,  
  8.             R.id.btSameType };  
  9.     // 每種類型的當前值  
  10.     private byte cur_byte = 1; // 每次總寫入 false  
  11.     private int cur_int = 10; // 寫入值 cur_int ++ ;  
  12.     private double cur_float = 100.0d; // 寫入值 cur_float++ ;  
  13.     private String cur_str = "QinJun -->" + cur_int; // 寫入值 "QinJun -->"+cur_int  
  14.   
  15.     private Parcel parcel = null;  
  16.   
  17.     @Override  
  18.     public void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.main);  
  21.         for (int i = 0; i < btIds.length; i++) {  
  22.             Button bt = (Button) findViewById(btIds[i]);  
  23.             bt.setOnClickListener(this);  
  24.         }  
  25.         parcel = Parcel.obtain(); // 獲得一個Parcel對象 ,相當於new一個,初始大小為0  
  26.         Log.i(TAG, "The original parcel info" + getParcelInfo());  
  27.     }  
  28.   
  29.     @Override  
  30.     public void onClick(View view) {  
  31.         // TODO Auto-generated method stub  
  32.         int viewviewId = view.getId();  
  33.         switch (viewId) {  
  34.         case R.id.btWriteByte:  
  35.             parcel.setDataPosition(0);  
  36.             parcel.writeByte(cur_byte);  
  37.             Log.i(TAG, " after write byte, --->" + getParcelInfo());  
  38.             break;  
  39.         case R.id.btWriteInt:  
  40.             parcel.writeInt(cur_int);  
  41.             Log.i(TAG, " after write int, --->" + getParcelInfo());  
  42.             break;  
  43.         case R.id.btWriteDouble:  
  44.             parcel.writeDouble(cur_float);  
  45.             Log.i(TAG, " after write float, --->" + getParcelInfo());  
  46.             break;  
  47.         case R.id.btWriteString:  
  48.             parcel.writeString(cur_str);  
  49.             Log.i(TAG, " after write String, --->" + getParcelInfo());  
  50.             break;  
  51.         case R.id.btReadByte:  
  52.             byte b = parcel.readByte();  
  53.             Log.i(TAG, " read byte is=" + b + ", --->" + getParcelInfo()  
  54.                     + "String");  
  55.             break;  
  56.         case R.id.btReadInt:  
  57.             int i = parcel.readInt();  
  58.             Log.i(TAG, " read int is=" + i + ", --->" + getParcelInfo());  
  59.             break;  
  60.         case R.id.btReadDouble:  
  61.             float f = parcel.readFloat();  
  62.             readSameType();  
  63.             Log.i(TAG, " read float is=" + f + ", --->" + getParcelInfo());  
  64.             break;  
  65.         case R.id.btReadString:  
  66.             parcel.setDataPosition(0);  
  67.             String str = parcel.readString();  
  68.             Log.i(TAG, " read float is=" + str + ", --->" + getParcelInfo());  
  69.             break;  
  70.         case R.id.btSameType:  
  71.             readSameType();  
  72.             break;  
  73.         default:  
  74.             break;  
  75.         }  
  76.     }  
  77.   
  78.     private String getParcelInfo() {// 得到parcel的資訊  
  79.         return "dataSize = " + parcel.dataSize() + ", dataCapacity="  
  80.                 + parcel.dataCapacity() + ", dataPositon = "  
  81.                 + parcel.dataPosition();  
  82.     }  
  83.   
  84.     /**  
  85.      * 前提條件,Parcel存在多個類型相同的對象,本例子以10個float對象說明:  
  86.      */  
  87.     public void readSameType() {  
  88.           
  89.         for (int i = 0; i < 10; i++) {  
  90.             parcel.writeDouble(i);  
  91.             Log.i(TAG, "write double ----> " + getParcelInfo());  
  92.         }  
  93.         //方法一 ,顯示設定位移量   
  94.         int i = 0;  
  95.         int datasize = parcel.dataSize();  
  96.         while (i < datasize) {  
  97.             parcel.setDataPosition(i);  
  98.             double fvalue = parcel.readDouble();  
  99.             Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  
  100.             i += 8; // double佔用位元組為 8byte   
  101.         }  
  102. //      方法二,由於對象的類型一致,我們可以直接利用readXXX()讀取值會產生位移量  
  103. //      parcel.setDataPosition(0)  ;  //  
  104. //      while(parcel.dataPosition()<parcel.dataSize()){  
  105. //          double fvalue = parcel.readDouble();  
  106. //          Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  
  107. //      }  
  108.     }  
  109. }  

複製代碼

由於取值時,可能存在類型的轉換,因此點擊按鈕時,可能不會產生預期結果。因此,得保證位移量對應數值的正確性。

 

轉載自:http://www.eoeandroid.com/blog-129327-2029.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.