歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android中Parcel的分析以及使用

簡單點來說:Parcel就是一個存放讀取數據的容器, Android系統中的binder進程間通信(IPC)就使用了Parcel類來進行客戶端與服務端數據的交互,而且AIDL的數據也是通過Parcel來交互的。在Java空間和C++都實現了Parcel,由於它在C/C++中,直接使用了內存來讀取數據,因此,它更有效率。

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

[java]
  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的內部存儲機制是怎麼樣的?偏移量又是

  什麼情況?讓我們回憶一下基本數據類型的取值范圍:

                   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對象中只存在某一類型時,我們就可以通過這個方法來快速的讀取

所有值。具體方法如下:

 

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


    由於可能存在讀取值的偏差,一個默認的取值規范為:

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

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

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

                               對象不匹配時,返回0;  

Copyright © Linux教程網 All Rights Reserved