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中数据的存储结构的确正如之前猜测的那样,它是一个数据串,有一个位置指针标志着它当前的读写位置。写入数据的时候可以遵从某种约定,按照某种顺序把数据依此写进去,读的时候再按照同样的顺序依此把数据读出来。估计应该也可以通过设置指针位置的函数跳过某些数据进行读取或写入,但我这里没有做实验。
另外,如果写完之后再读,那么读之前记得要把位置指针重新置为要读的数据开始的地方,因为之前写的时候数据指针已经移动过了。
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中数据的存储结构的确正如之前猜测的那样,它是一个数据串,有一个位置指针标志着它当前的读写位置。写入数据的时候可以遵从某种约定,按照某种顺序把数据依此写进去,读的时候再按照同样的顺序依此把数据读出来。估计应该也可以通过设置指针位置的函数跳过某些数据进行读取或写入,但我这里没有做实验。
另外,如果写完之后再读,那么读之前记得要把位置指针重新置为要读的数据开始的地方,因为之前写的时候数据指针已经移动过了。