天天看點

Android中Parcel的分析以及使用

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

[java]

//參數說明:   

// code :是請求的ID号    

// data :用戶端請求發送的參數   

// reply:伺服器端傳回的結果   

// flags:一些額外的辨別,如FLAG_ONEWAY等,通常為0.   

virtual status_t    onTransact( uint32_t code,  

                                const Parcel& data,  

                                Parcel* reply,  

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

  什麼情況?讓我們回憶一下基本資料類型的取值範圍:

                   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]

/**  

     * 前提條件,Parcel存在多個類型相同的對象,本例子以10個float對象說明:  

     */  

    public void readSameType() {  

        Parcel parcel =Parcel.obtain() ;  

        for (int i = 0; i < 10; i++) {  

            parcel.writeDouble(i);  

            Log.i(TAG, "write double ----> " + getParcelInfo());  

        }  

        //方法一 ,顯示設定偏移量   

        int i = 0;  

        int datasize = parcel.dataSize();  

        while (i < datasize) {  

            parcel.setDataPosition(i);  

            double fvalue = parcel.readDouble();  

            Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  

            i += 8; // double占用位元組為 8byte   

//      方法二,由于對象的類型一緻,我們可以直接利用readXXX()讀取值會産生偏移量  

//      parcel.setDataPosition(0)  ;  //  

//      while(parcel.dataPosition()<parcel.dataSize()){  

//          double fvalue = parcel.readDouble();  

//          Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());  

//      }  

    }  

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

             1、  讀取複雜對象時: 對象比對時,傳回目前偏移位置的該對象;

                               對象不比對時,傳回null對象 ;

             2、  讀取簡單對象時: 對象比對時,傳回目前偏移位置的該對象 ;

                               對象不比對時,傳回0;  

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

Android中Parcel的分析以及使用

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

     1、布局檔案如下:

<?xml version="1.0" encoding="utf-8"?>  

    android:orientation="vertical" android:layout_width="fill_parent"  

    android:layout_height="fill_parent">  

    <TextView android:layout_width="fill_parent"  

        android:layout_height="wrap_content" android:text="@string/hello" />  

    <LinearLayout android:orientation="horizontal"  

        android:layout_width="fill_parent" android:layout_height="wrap_content">  

        <Button android:id="@+id/btWriteByte" android:layout_width="wrap_content"  

            android:layout_height="wrap_content" android:text="寫入一個byte值"></Button>  

        <Button android:id="@+id/btWriteInt" android:layout_width="wrap_content"  

            android:layout_height="wrap_content" android:text="寫入一個int值"></Button>  

    </LinearLayout>  

        <Button android:id="@+id/btWriteDouble" android:layout_width="wrap_content"  

            android:layout_height="wrap_content" android:text="寫入一個double值"></Button>  

        <Button android:id="@+id/btWriteString" android:layout_width="wrap_content"  

            android:layout_height="wrap_content" android:text="寫入一個String值"></Button>  

    <View android:layout_width="fill_parent" android:layout_height="2dip"  

        android:background="#FF1493"></View>  

        android:layout_marginTop="5dip" android:layout_width="fill_parent"  

        android:layout_height="wrap_content">  

        <Button android:id="@+id/btReadByte" android:layout_width="wrap_content"  

            android:layout_height="wrap_content" android:text="讀取一個byte值"></Button>  

        <Button android:id="@+id/btReadInt" android:layout_width="wrap_content"  

            android:layout_height="wrap_content" android:text="讀取一個int值"></Button>  

        <Button android:id="@+id/btReadDouble" android:layout_width="wrap_content"  

            android:layout_height="wrap_content" android:text="讀取一個double值"></Button>  

        <Button android:id="@+id/btReadString" android:layout_width="wrap_content"  

            android:layout_height="wrap_content" android:text="讀取一個String值"></Button>  

    <Button android:id="@+id/btSameType" android:layout_width="wrap_content"  

        android:layout_height="wrap_content" android:text="利用setDataPosition讀取多個值"></Button>  

</LinearLayout>   

 2、配置檔案如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  

      package="com.qinjuning.parcel"  

      android:versionCode="1"  

      android:versionName="1.0">  

    <application android:icon="@drawable/icon" android:label="@string/app_name">  

        <activity android:name=".MainActivity"  android:label="@string/app_name">  

            <intent-filter>  

                <action android:name="android.intent.action.MAIN" />  

                <category android:name="android.intent.category.LAUNCHER" />  

            </intent-filter>  

        </activity>  

    </application>  

</manifest>   

     3、程式主檔案如下:

public class MainActivity extends Activity implements OnClickListener {  

    private static String TAG = "PARCELTEST";  

    // Button ID  

    private static int[] btIds = new int[] { R.id.btWriteByte, R.id.btWriteInt,  

            R.id.btReadDouble, R.id.btWriteString, R.id.btReadByte,  

            R.id.btReadInt, R.id.btReadDouble, R.id.btReadString,  

            R.id.btSameType };  

    // 每種類型的目前值  

    private byte cur_byte = 1; // 每次總寫入 false  

    private int cur_int = 10; // 寫入值 cur_int ++ ;  

    private double cur_float = 100.0d; // 寫入值 cur_float++ ;  

    private String cur_str = "QinJun -->" + cur_int; // 寫入值 "QinJun -->"+cur_int  

    private Parcel parcel = null;  

    @Override  

    public void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

        setContentView(R.layout.main);  

        for (int i = 0; i < btIds.length; i++) {  

            Button bt = (Button) findViewById(btIds[i]);  

            bt.setOnClickListener(this);  

        parcel = Parcel.obtain(); // 獲得一個Parcel對象 ,相當于new一個,初始大小為0  

        Log.i(TAG, "The original parcel info" + getParcelInfo());  

    public void onClick(View view) {  

        // TODO Auto-generated method stub  

        int viewviewId = view.getId();  

        switch (viewId) {  

        case R.id.btWriteByte:  

            parcel.setDataPosition(0);  

            parcel.writeByte(cur_byte);  

            Log.i(TAG, " after write byte, --->" + getParcelInfo());  

            break;  

        case R.id.btWriteInt:  

            parcel.writeInt(cur_int);  

            Log.i(TAG, " after write int, --->" + getParcelInfo());  

        case R.id.btWriteDouble:  

            parcel.writeDouble(cur_float);  

            Log.i(TAG, " after write float, --->" + getParcelInfo());  

        case R.id.btWriteString:  

            parcel.writeString(cur_str);  

            Log.i(TAG, " after write String, --->" + getParcelInfo());  

        case R.id.btReadByte:  

            byte b = parcel.readByte();  

            Log.i(TAG, " read byte is=" + b + ", --->" + getParcelInfo()  

                    + "String");  

        case R.id.btReadInt:  

            int i = parcel.readInt();  

            Log.i(TAG, " read int is=" + i + ", --->" + getParcelInfo());  

        case R.id.btReadDouble:  

            float f = parcel.readFloat();  

            readSameType();  

            Log.i(TAG, " read float is=" + f + ", --->" + getParcelInfo());  

        case R.id.btReadString:  

            String str = parcel.readString();  

            Log.i(TAG, " read float is=" + str + ", --->" + getParcelInfo());  

        case R.id.btSameType:  

        default:  

    private String getParcelInfo() {// 得到parcel的資訊  

        return "dataSize = " + parcel.dataSize() + ", dataCapacity="  

                + parcel.dataCapacity() + ", dataPositon = "  

                + parcel.dataPosition();  

    /**  

}  

由于取值時,可能存在類型的轉換,是以點選按鈕時,可能不會産生預期結果。是以,得保證偏移量對應數值的正确性。

    本文轉自 一點點征服   部落格園部落格,原文連結:<b>http://www.cnblogs.com/ldq2016/p/8469323.html</b>,如需轉載請自行聯系原作者