Android 序列化 ---- Parcelable原理分析

2023-05-16

        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();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    // 将一个对象转换成 Parcel对象,进行序列化
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 序列化参数的顺序必须和反序列化时保持一致
        dest.writeString(this.strField);
        dest.writeInt(this.intField);
    }

    // 实现类必须有一个CREATOR属性,用于反序列化,将Parcel对象转换为 Pacelable
    public static final Parcelable.Creator<ParcelObj> CREATOR = new             
                Parcelable.Creator<ParcelObj>() {
        // 反序列化的方法,将Parcel还原成Java对象
        @Override
        public ParcelObj createFromParcel(Parcel parcel) {
            return new ParcelObj(parcel);
        }

        @Override
        public 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* parcel
    Parcel* 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;
}

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 序列化 ---- Parcelable原理分析 的相关文章

  • 文章五:Python 网络爬虫实战:使用 Beautiful Soup 和 Requests 抓取网页数据

    一 简介 本篇文章将介绍如何使用 Python 编写一个简单的网络爬虫 xff0c 从网页中提取有用的数据 我们将通过以下几个部分展开本文的内容 xff1a 网络爬虫的基本概念Beautiful Soup 和 Requests 库简介选择一
  • 无人机目标检测:使用YOLOv4在VisDrone数据集上进行目标检测任务

    在本篇博客中 我们将探讨如何使用YOLOv4在VisDrone数据集上进行无人机目标检测任务 目标检测是计算机视觉中的一个重要任务 可以用于自动驾驶汽车 无人机监测和视频分析等多种应用 YOLOv4是一种实时目标检测算法 以其速度和准确性而
  • rospy的publisher和init_node

    文章目录 1 xff0c class Publiser xff08 发布者 xff09 2 rospy init node 初始化节点 1 xff0c class Publiser xff08 发布者 xff09 废话不多说 xff0c 先
  • ros功能包

    使用ROS的功能包使用以下常见的机器视觉应用 1 xff09 摄像头标定 xff1a 摄像头本身存在光学畸变 xff0c 可以使用camera calibration功能包实现双目和单目摄像头的标定 2 xff09 基于opencv的人脸识
  • 如何彻底关闭Win10自动更新,Win10永久关闭自动更新的方法

    如何彻底关闭Win10自动更新 xff1f Win10自动更新的问题是很多用户都遇到的问题 xff0c 很多时候我们关闭了自动更新 xff0c 过一段时间系统又自动更新了 xff0c 由于win10自动更新非常顽固 xff0c 所以我们要从
  • 函数模板及库函数

    函数模板 xff08 function template xff09 是一个独立于类型的函数 xff0c 可作为一种模式 xff0c 产生函数的特定类型版本 使用函数模板可以设计通用型的函数 xff0c 这些函数与类型无关并且只在需要时自动
  • Vmware虚拟机Ubuntu的ssh远程登陆--笔记

    SSH远程登录 apt更新网路更新ssh配置了解的部分 SecureCRTPortable登陆 apt更新 版本号 xff1a Ubuntu 16 04 环境 xff1a Vmware 17 2 网路 首先 xff0c 先检查网络是否畅通
  • Docker的常用命令

    一 Docker中几个重要的概念 镜像 和容器 是docker中两个非常重要的 概念 镜像 xff08 Image xff09 xff1a Docker 将应用程序及其所需的依赖 函数库 环境 配置等文件打包在一起 xff0c 称为镜像 容
  • Linux-C语言编写-UDP服务器客户端通信流程简介(代码)

    目录 一 xff0c 服务器 1 创建数据报套接字 2 填充结构体 3 绑定服务器的ip和端口 4 接收来自客户端的消息 recvfrom 5 关闭套接字 6 详细代码 二 xff0c 客户端 1 创建数据报套接字 2 填充结构体 xff0
  • C++三阶贝塞尔曲线

    文章目录 1 贝塞尔曲线2 示意图3 c 43 43 代码实现 1 贝塞尔曲线 贝塞尔曲线阶数等于控制点个数n 1将控制点首尾相连并且取每段连线上一点P xff0c 再将每个线段上的P点连接设第一个控制点为P1 xff0c 第二个为P2 x
  • Ubuntu 图达通激光雷达可视化/获取点云

    文章目录 0 ILA 平台网页预览1 Ubuntu的安装2 安装Ubuntu对应版本ros3 激光雷达接线4 解压SDK文件5 启动ros可视化点云6 录制点云7 播放录制文件8 rosbag文件 gt pcd文件 0 ILA 平台网页预览
  • Python的while循环

    目录 一 计数器 二 while循环使用 三 不同循环的使用环境判断 xff1a 四 while循环使用break和continue 五 while的嵌套使用 一 计数器 计数器 xff0c 是一个叫法 xff0c 代表的是一个功能 用于记
  • 字符串结束符

    在C语言中 xff0c 存储一个字符串通常用一个char 数组 在C语言中 xff0c 为了方便存储 xff0c 要求在最后一个字符的后面存储一个0 xff08 一个字节 xff09 这个0称为 字符串结束符 xff0c 常用 0 表示 在
  • 一起自学SLAM算法:1.1 ROS简介

    连载文章 xff0c 长期更新 xff0c 欢迎关注 xff1a 写在前面 第1章 ROS入门必备知识 1 1 ROS简介 1 2 ROS开发环境搭建 1 3 ROS系统架构 1 4 ROS调试工具 1 5 ROS节点通信 1 6 ROS其
  • 一起自学SLAM算法:1.2 ROS开发环境搭建

    连载文章 xff0c 长期更新 xff0c 欢迎关注 xff1a 写在前面 第1章 ROS入门必备知识 1 1 ROS简介 1 2 ROS开发环境搭建 1 3 ROS系统架构 1 4 ROS调试工具 1 5 ROS节点通信 1 6 ROS其
  • 戴尔电脑恢复系统后,D盘被加密Bitlocker,要求输入48位密钥,才能打开D盘---解决过程

    一 前言 今天DELL电脑恢复系统后 xff0c D盘被加密 xff08 D盘图标上有一把黄色的锁 xff09 xff0c 鼠标双击准备打开D盘 xff0c 提示了一个密钥ID xff0c 让输入48位码解密 xff0c 被microsof
  • 一起自学SLAM算法:1.4 ROS调试工具

    连载文章 xff0c 长期更新 xff0c 欢迎关注 xff1a 写在前面 第1章 ROS入门必备知识 1 1 ROS简介 1 2 ROS开发环境搭建 1 3 ROS系统架构 1 4 ROS调试工具 1 5 ROS节点通信 1 6 ROS其
  • 一起自学SLAM算法:1.5 ROS节点通信

    连载文章 xff0c 长期更新 xff0c 欢迎关注 xff1a 写在前面 第1章 ROS入门必备知识 1 1 ROS简介 1 2 ROS开发环境搭建 1 3 ROS系统架构 1 4 ROS调试工具 1 5 ROS节点通信 1 6 ROS其
  • 一起自学SLAM算法:3.4 图像特征点提取

    连载文章 xff0c 长期更新 xff0c 欢迎关注 xff1a 写在前面 第1章 ROS入门必备知识 第2章 C 43 43 编程范式 第3章 OpenCV图像处理 3 1 认识图像数据 3 2 图像滤波 3 3 图像变换 3 4 图像特
  • 一起自学SLAM算法:第4章-机器人传感器

    连载文章 xff0c 长期更新 xff0c 欢迎关注 xff1a 写在前面 第1章 ROS入门必备知识 第2章 C 43 43 编程范式 第3章 OpenCV图像处理 第4章 机器人传感器 4 1 惯性测量单元 4 2 激光雷达 4 3 相

随机推荐