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;

下面,给出一张浅显的Parcel的存放空间图,希望大家在理解的同时,更能体味其中滋味。有点简单,求谅解。

相信通过前面的介绍,你一定很了解了了Parcel的存储机制,下面给定一应用程序来实践。

   1、布局文件如下:

[html]

  1. <?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、配置文件如下:

[html]

  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、程序主文件如下:

[html]

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

由于取值时,可能存在类型的转换,因此点击按钮时,可能不会产生预期结果。因此,得保证偏移量对应数值的正确性。

原文地址:https://www.cnblogs.com/ldq2016/p/8469323.html

时间: 2024-10-12 02:50:15

Android中Parcel的分析以及使用的相关文章

PopupWindow在android中的使用分析

PopupWindow在android中的使用分析 PopupWindow是应用开发中经常用到的组建,使用它可以在当前屏幕的上层显示一个弹窗,同时也可以指定弹窗的位置以及背景色等特性,大大提高用户体验,那么这里我就以下几点介绍它的使用: 1 从指定的位置弹出这个窗口(淡入淡出动画) 2 从屏幕底部弹出这个窗口(带有透明度背景,自定义触摸其他位置自动关闭弹窗) 我的效果图如下: 下面直接上代码,具体如下所示(按开发顺序排列) 1 自定义一个继承自PopupWindow的类 publicclassP

【转载+整理】Android中TouchEvent事件分析

原文地址:http://mobile.51cto.com/abased-374715.htm 一.知识回顾 一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP 二.问题提出 当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是 View

Android 中图片压缩分析(上)

作者: shawnzhao,QQ音乐技术团队一员 一.前言 在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种:其一是质量压缩,其二是下采样压缩. 前者是在不改变图片尺寸的情况下,改变图片的存储体积,而后者则是降低图像尺寸,达到相同目的. 由于本文的篇幅问题,分为上下两篇发布. 二.Android 质量压缩逻辑 在Android中,对图片进行质量压缩,通常我们的实现方式如下所示: ByteArrayOutputStream outputStream = new Byte

android中SELINUX规则分析和语法简介

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 1. SELINUX是可以理解为一种android上面的安全机制,是有美国国家安全局和一些公司设计的一个针对linux的安全加强系统 我们可以通过配置SELINUX的相关policy,来定制自己的手机的一些权限,比如,我们可以完全让root用户没有任何的权限和user一样 2.

Android 中LayoutInflater原理分析

概述 在Android开发中LayoutInflater的应用非常普遍,可以将res/layout/下的xml布局文件,实例化为一个View或者ViewGroup的控件.与findViewById的作用类似,但是findViewById在xml布局文件中查找具体的控件,两者并不完全相同. 应用场景: 1.在一个没有载入或者想要动态载入的界面中,需要使用layoutInflater.inflate()来载入布局文件: 2.对于一个已经载入的界面,就可以使用findViewById方法来获得其中的界

如何使用Android中TraceView性能分析工具

现来看一下整个界面的图,整个界面包括上下两部分,上面是你测试的进程中每个线程的执行情况,每个线程占一行:下面是每个方法执行的各个指标的值 上面一部分是你测试进程的中每个线程运行的时间线,下图中可以可以看到,主要只有一个main线程在执行,因为我滑动了一下列表,main线程(UI线程)正在进行绘制View呢~ 附相关视频教程Android应用开发视频教程 然后我点击了序号为133的一个方法io.bxbxbai.android.examples.activity.ExpandableLayoutMa

Android中应用安装分析

#1 安装方式 1 安装系统APK和预制APK时,通过PMS的构造函数中安装,即第一次开机时安装应用,没有安装界面. 2 网络下载安装,通过应用商店等,即调用PackageManager.installPackages(),有安装界面. 3 通过adb工具安装,没有安装界面,它通过启动pm脚本的形式,然后调用com.android.commands.pm.Pm类,之后调用到PMS.installStage()完成安装. 4 安装本地apk,有安装界面,由PackageInstaller系统应用安

Android Binder机制分析(4) Parcel类分析

引言 在上一篇Blog中,在分析服务注册过程时,往data(Parcel对象)变量写入数据时,有这样的调用路径: BpServiceManager::addService()–>Parcel::writeStrongBinder()–>flatten_binder()–>finish_flatten_binder() 由于finish_flatten_binder()方法中涉及到的东西太多,在上一篇博客就没有展开来讲.这篇博客将详细分析数据是如何写入到data中的. 下面是Parcel类

从源码角度分析Android中的Binder机制的前因后果

前面我也讲述过一篇文章<带你从零学习linux下的socket编程>,主要是从进程通信的角度开篇然后延伸到linux中的socket的开发.本篇文章依然是从进程通信的角度去分析下Android中的进程通信机制. 为什么在Android中使用binder通信机制? 众所周知linux中的进程通信有很多种方式,比如说管道.消息队列.socket机制等.socket我们再熟悉不过了,然而其作为一款通用的接口,通信开销大,数据传输效率低,主要用在跨网络间的进程间通信以及在本地的低速通信.消息队列和管道