Parcelable是专门为Android设计的序列化接口。它是基于内存,底层是通过指针挪动实现。因此,执行效率比Java的Serializable要高的多,号称快10倍。

一个简单的使用Parcelable进行序列化的例子:

public class ParcelObj implements Parcelable {private String strField;private int intField;// 需要提供一个接收 Parcel类型参数的构造函数,执行反序列化protected ParcelObj(Parcel in) {// 反序列化参数的顺序必须和序列化时保持一致this.strField = in.readString();this.intField = in.readInt();}@Overridepublic int describeContents() {return 0;}// 将一个对象转换成 Parcel对象,进行序列化@Overridepublic void writeToParcel(Parcel dest, int flags) {// 序列化参数的顺序必须和反序列化时保持一致dest.writeString(this.strField);dest.writeInt(this.intField);}// 实现类必须有一个CREATOR属性,用于反序列化,将Parcel对象转换为 Pacelablepublic static final Parcelable.Creator<ParcelObj> CREATOR = new             Parcelable.Creator<ParcelObj>() {// 反序列化的方法,将Parcel还原成Java对象@Overridepublic ParcelObj createFromParcel(Parcel parcel) {return new ParcelObj(parcel);}@Overridepublic ParcelObj[] newArray(int size) {return new ParcelObj[0];}};
}

Parcelable序列化过程是通过Parcel来实现的。Parcel底层基于共享内存实现。 

C++共享内存原理

Android NDK部分是用C/C++来实现(这里用C++来说明)。C++运行时内存分为栈区、堆区、全局区、代码区。其中堆区是C++存放对象的主要区域,在该区域分配的对象,使用完后必须要手动进行释放。否则,会造成内存泄漏。系统不会主动进行释放。这一块区域不在jvm运行时内存管理的范围。因此,所有用户进程都可以访问。

C++提供了四种类型转换:const_cast、static_cast、dynamic_cast、reinterpret_cast。其中reinterpret_cast功能很强大。可以将对象类型指针转换为long类型的数。并且可以再次通过将该数值转换回对象指针,并可以访问对象。

// 在堆区分配对象
KPlayer * kPlayer = new KPlayer();
// 将对象指针转换为long类型数值
long playerValue = reinterpret_cast<long>(kPlayer); // (在另一个位置) 将刚才的数值,转换回对象指针(要求次对象没有被手动释放过)
KPlayer * kPlayer = reinterpret_cast<KPlayer *>(playerValue );
// 调用对象的方法
kellyPlayer->show();

由此,共享内存的思路,即在Native层新建对象,并将指向对象的指针转换成数值返回Java层。Java层不同进程拿到的是相同的long类型数值,则可以访问Native层共同的对象。进而实现共享内存。

Parcel序列化的原理,既是将java数据写入共享内存的对象,在另一个位置(或进程),读取数据并反序列化成对象。

Parcel源码分析

初始化

、        通过调用Parcel.obtain()方法,获取一个Parcel对象。当前没有缓存的Parcel对象则会新建一个。mNativePtr既是在上面提到的,在Native层对象指针返回给java层的数值。这个指针指向的是Native层的Parcel对象。

package android.os;public final class Parcel {// 用于访问共享内存对象的long类型数值private long mNativePtr; // used by native code/*** 用于初始化 Parcel对象的函数* Retrieve a new Parcel object from the pool.*/public static Parcel obtain() {...return new Parcel(0);...}
}

new Parcel(0) 会调用到nativeCreate()函数。

private static native long nativeCreate();private Parcel(long nativePtr) {if (DEBUG_RECYCLE) {mStack = new RuntimeException();}//Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);init(nativePtr);
}private void init(long nativePtr) {if (nativePtr != 0) {mNativePtr = nativePtr;mOwnsNativeParcelObject = false;} else {mNativePtr = nativeCreate();mOwnsNativeParcelObject = true;}
}

nativeCreate()是一个本地方法。最终会调用到 android_os_Parcel.cpp的 nativeCreate。

可以看到android_os_Parcel_create实际上就是创建一个对象,使用reinterpret_cast转换成long类型数值,返回。

// 这里是JNI的动态注册
static const JNINativeMethod gParcelMethods[] = {...{"nativeCreate",   "()J", (void*)android_os_Parcel_create},...
}static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{// 实例化一个Parcel对象Parcel* parcel = new Parcel();// 用reinterpret_cast讲Parcel转换jlong类型,并返回return reinterpret_cast<jlong>(parcel);
}

在new Parcel()中会分配内存,初始化大小等。到此完成了Parcel的初始化。

写数据

以writeInt方法为例,writeInt也是调用native方法实现。

private static native void nativeWriteInt(long nativePtr, int val);public final void writeInt(int val) {nativeWriteInt(mNativePtr, val);
}

nativeWriteInt既是讲该int类型的值写入内存中,并将指针向后移动相应大小的位置。

// android_os_Parcel.cpp:
...
{"nativeWriteInt",            "(JI)V", (void*)android_os_Parcel_writeInt},
...    static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {// 通过在Java层保存的,C++对象首地址,来查找到C++对象 Parcel* parcelParcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {// 把内容写入进去const status_t err = parcel->writeInt32(val); if (err != NO_ERROR) {signalExceptionForError(env, clazz, err);}}
}// Parcel.cpp:
status_t Parcel::writeInt32(int32_t val)
{return writeAligned(val);
}template<class T> status_t Parcel::writeAligned(T val) {COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));// 判断是否超出共享内存大小if ((mDataPos+sizeof(val)) <= mDataCapacity) {// 填入数据restart_write: *reinterpret_cast<T*>(mData+mDataPos) = val;return finishWrite(sizeof(val));}...
}status_t Parcel::finishWrite(size_t len)
{mDataPos += len; // 根据数据大小,挪动指针if (mDataPos > mDataSize) {mDataSize = mDataPos;}return NO_ERROR;
}

读数据过程与写数据一致,既是按照写的顺序和类型,一个一个的读出来。这也是为什么序列化写数据要和反序列化读数据的顺序要保持一致的原因。

Android 序列化 ---- Parcelable原理分析相关推荐

  1. 可能是最详细的Android图片压缩原理分析(二)—— 鲁班压缩算法解析

    本篇文章已授权微信公众号guolin_blog(郭霖)独家发布 稀土掘金链接 前言 通过上一篇,我们了解了一些关于图片压缩的基础知识,这篇文章我们主要讲解一下鲁班压缩的算法逻辑,很多博客都是从Gith ...

  2. 可能是最详细的Android图片压缩原理分析(一)—— Android图片压缩必备基础知识

    本篇文章已授权微信公众号guolin_blog(郭霖)独家发布 稀土掘金链接 前言: 最近在研究图片压缩原理,看了大量资料,从上层尺寸压缩.质量压缩原理到下层的哈夫曼压缩,走成华大道,然后去二仙桥,全 ...

  3. Android App加固原理分析

    Android App加固原理分析 对App进行加固,可以有效防止移动应用被破解.盗版.二次打包.注入.反编译等,保障程序的安全性.稳定性.对于金融类App,尤其重要. 对App dex进行加固的基本 ...

  4. Android Retrofit实现原理分析

    retrofit有几个关键的地方. 1.用户自定义的接口和接口方法.(由动态代理创建对象.) 2.converter转换器.(把response转换为一个具体的对象) 3.注解的使用. 让我们跟随Ap ...

  5. linux qt getpid,[QTA] Android 动态注入原理分析

    一.前言 Android 的 UI 自动化测试可以通过注入式和非注入式分别实现,通过注入式可以更加方便地与应用进行交互.QTA 团队提供的 Android UI 自动化测试框架QT4A, 是通过动态注 ...

  6. Android Animation动画原理分析

    动画在Android应用中是很常用的,而系统提供的Animation相关动画机制由于简单实用,经常是我们实现动画的首要选择.本文旨在分析Animation相关类实现动画的原理,目的是了解动画实现的主要 ...

  7. Android 换肤原理分析

    当了解了一些知识,应该用文字记录它,再抽个时间再看它,永远记住它 Android 换肤的理论知识和文章已经很多了,这里记录一下自己对这块的理解.本文效果如下: 工程:一键换肤的快乐 一.换肤的由来 首 ...

  8. 安卓获取APP对应的Android id的原理分析

    android_id 的生成原理是由系统生成的随机数,并与应用 app 签名,经过 HmacSHA256 算法生成的: 从 android 8 以后开始就是随机的了,每个应用获取到的简要步骤: 获取的 ...

  9. 包含c语言的序列化字符,Android Serializable与Parcelable原理与区别

    一.序列化.反序列化是什么? (1) 名词解释 对象的序列化 : 把Java对象转换为字节序列并存储至一个储存媒介的过程. 对象的反序列化:把字节序列恢复为Java对象的过程. (2) 序列化详细解释 ...

最新文章

  1. Struts2利用iText导出word文档(包含表格)
  2. c/c++左值和右值
  3. moment格式换时间_不一样的日期、时间转换(moment.js)
  4. 杭电1411 校庆神秘建筑
  5. C++ 出现异常“.... \debug_heap.cpp Line:980 Expression:__acrt_first_block==header“
  6. WIZnet推出串口转以太网模块WIZ550S2E
  7. Threading模块
  8. SpringBoot+Vue项目个性化音乐推荐系统
  9. Kotlin教程(一)基础
  10. java ckfinder_java 使用ckfinder
  11. 别在直接背3500个英语单词了,支你一招,看过来
  12. 弧齿锥齿轮零件图_弧齿锥齿轮画法类别
  13. 爬取B站直播流 - http+flv的相关研究
  14. 怎么制作gif动图?你学会制作了吗?
  15. 二部六层电梯程序西门子1200二部六层电梯程序
  16. Ts-Map 类的使用
  17. Java API文档
  18. JMeter性能测试流程及性能指标关注点
  19. 打牌博弈 dfs深度优先遍历搜索 排课表 拓扑排序 升序字符串 动态规划 剑指offer编程题整理 leetcode每日算法题
  20. 虚幻引擎(23)-游戏背景音乐

热门文章

  1. Day 22 学习分享 - 泛型和学生管理系统项目进阶
  2. Python-魔法函数
  3. matlab 中的三次样条插值函数spline使用说明
  4. 一篇文章搞懂屈梁生院士说了什么(二维全息谱的理解)
  5. 纯净Windows镜像安装方法(无网卡和usb驱动)
  6. linux 下 gtest 的安装
  7. bs架构 mysql_bs架构是什么意思?
  8. CentOS7设置阿里镜像源
  9. freedns mysql_freedns.afraid.org免费多子域名支持dns解析
  10. IBM公司DB2数据库的ODBC驱动的下载地址及相关资料