【09.03.30】Android中使用C++程式讀寫Parcel的簡單例子
Android中的Parcel類困惑了我好一段時間(當然現在也沒把Parcel完全弄明白),查了一些資料也不知道它裡面是以一種什麼格式儲存資料的。因為它對外提供的讀寫函式都沒有提到具體一個讀寫函式到底是讀寫Parcel中的哪一塊地址裡的資料。比如網上有這麼兩段程式碼:
public void writeToParcel(Parcel out) { //當前資料寫入到Parcel中
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) { //從Parcel中讀取資料
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
其中無論是讀函式writeInt(),還是寫函式readInt(),引數中都沒有提及是往Parcel中的具體哪塊地方寫入left變數,又是從Parcel的哪塊地方讀出數值賦給right變數。後來看了一些原始碼,包括Parcel.h和Parcel.cpp。感覺Parcel可能類似一個一維的資料串,各種變數都可以往裡面寫(通過Parcel提供的針對各種變數的具體函式),有一個位置指標指向這個一維的資料串中的某個位置,這個位置就是預設的讀寫的位置。也就是說,如果現在呼叫讀寫函式,就是讀寫當前位置指標處的資料,讀寫結束後,把位置指標向後移動一塊空間(跨越的長度正好是你上次呼叫讀寫函式讀過或者寫過資料的長度),繼續準備對下一部分空間進行讀寫操作。
為了驗證這個想法,編寫了一段對Parcel進行讀寫的C++程式。由於對C++較為生疏,編寫這個小程式花了一下午的時間,不過最後總算是通過了。現在把程式碼貼在下面:
/********************** 以下是 Parcel_test.cpp 程式 ****************************/
/*這裡的副檔名應該是.cpp,也就是c++檔案,如果此處用c寫程式,副檔名為.c的話,加入#include <utils/Parcel.h>這句後可能將出現錯誤*/
#include <utils/Parcel.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
using namespace android;/*這一行一開始沒有加上,結果總是出錯,提示Parcel、status_t、NO_ERROR都沒有定義,鬱悶了好久,心想在include中加入了utils/Parcel.h呀,而且在Android.mk中也加入了Parcel.h所在的庫libutils。後來看了Android原始碼才推測出了可能要加入這句*/
status_t status;
size_t parcel_position;
int intp=987654;
char charp='g';
float floatp=3.14159;
double doublep=12345.6789012;
long longp=1234567890;
char *stringp="this is my parcel test!";
Parcel p;
parcel_position = p.dataPosition();/*備份位置*/
printf("當前Parcel的讀寫位置是:%d\n",parcel_position);
/*************寫int型別***************/
status=p.writeInt32(intp);
if (status==NO_ERROR)
printf("write int type success!\n");
else
printf("write int type fail,the errno=%d\n",errno);
/*************寫char型別***************/
status=p.writeInt32(charp);
if (status==NO_ERROR)
printf("write char type success!\n");
else
printf("write char type fail,the errno=%d\n",errno);
/*************寫Float型別***************/
status=p.writeFloat(floatp);
if (status==NO_ERROR)
printf("write Float type success!\n");
else
printf("write Float type fail,the errno=%d\n",errno);
/*************寫Double型別***************/
status=p.writeDouble(doublep);
if (status==NO_ERROR)
printf("write Double type success!\n");
else
printf("write Double type fail,the errno=%d\n",errno);
/*************寫long型別***************/
status=p.writeInt64(longp);
if (status==NO_ERROR)
printf("write long type success!\n");
else
printf("write long type fail,the errno=%d\n",errno);
/*************寫String型別***************/
status=p.writeCString(stringp);
if (status==NO_ERROR)
printf("write String type success!\n");
else
printf("write String type fail,the errno=%d\n",errno);
/*************將parcel讀寫位置置回原位***************/
p.setDataPosition(parcel_position);
/*************讀出變數***************/
printf("讀出的int型別變數為:%d\n",p.readInt32());
printf("讀出的char型別變數為:%c\n",(char)p.readInt32());
printf("讀出的Float型別變數為:%f\n",(float)p.readFloat());
printf("讀出的Double型別變數為:%f\n",(double)p.readDouble());
printf("讀出的long型別變數為:%ld\n",(long)p.readInt64());
printf("讀出的字串為:%s\n",p.readCString());
}
/********************** 以上是 Parcel_test.cpp 程式 ****************************/
/***************下面是對應的makefile檔案******************/
/********************** 以下是Android.mk檔案 ****************************/
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
parcel_test.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils
LOCAL_CFLAGS :=
LOCAL_MODULE:= parcel_test
include $(BUILD_EXECUTABLE)
/********************** 以上是Android.mk檔案 ****************************/
把這兩個檔案放在Android原始碼目錄下的development目錄下的parcel_test資料夾中(dl資料夾是新建的),然後在終端中使用root許可權進入到Android原始碼目錄下,執行 make parcel_test。成功後將會在android原始碼目錄/out/target/product/generic/system/bin/中生成parcel_test可執行檔案。
使用以下命令將它們放入Android模擬器,注意要先啟動emulator
adb push Android原始碼目錄/out/target/product/generic/system/bin/parcel_test /data
進入data資料夾執行
adb shell
# cd data
# ./parcel_test
////////////////////////// 以下是程式執行結果 //////////////////////////////
當前Parcel的讀寫位置是:0
write int type success!
write char type success!
write Float type success!
write Double type success!
write long type success!
write String type success!
讀出的int型別變數為:987654
讀出的char型別變數為:g
讀出的Float型別變數為:3.141590
讀出的Double型別變數為:12345.678901
讀出的long型別變數為:1234567890
讀出的字串為:this is my parcel test!
==============================
由此可知,Parcel中資料的儲存結構的確正如之前猜測的那樣,它是一個數據串,有一個位置指標標誌著它當前的讀寫位置。寫入資料的時候可以遵從某種約定,按照某種順序把資料依此寫進去,讀的時候再按照同樣的順序依此把資料讀出來。估計應該也可以通過設定指標位置的函式跳過某些資料進行讀取或寫入,但我這裡沒有做實驗。
另外,如果寫完之後再讀,那麼讀之前記得要把位置指標重新置為要讀的資料開始的地方,因為之前寫的時候資料指標已經移動過了。