解锁Android高效数据传输的秘钥 - Parcelable剖析


作为Android开发者,我们经常需要在不同的组件(Activity、Service等)之间传输数据。这里的"传输"往往不仅仅是简单的数据复制,还可能涉及跨进程的内存复制操作。当传输的数据量较大时,这种操作可能会带来严重的性能问题。而Android系统为我们提供了Parcelable这一高效的序列化传输机制,很好地解决了这一痛点。今天,就让我们一起来探讪Parcelable的神奇之处。


一、Parcelable架构与原理


Parcelable是Android中一种高效的序列化机制,用于实现进程间通信(IPC)中的对象传递。

Parcelable相对于Serializable的使用相对复杂一些,但Parcelable的效率相对Serializable也高很多,这一直是Google工程师引以为傲的,Parcelable和Serializable的效率对比Parcelable vs Serializable号称快10倍的效率。

与Serializable接口不同,Parcelable采用的是手工编码的方式,序列化后的数据更为紧凑。系统将数据打包到一个全局内存区域中,可供不同线程/进程共享访问。


1、Parcelable的设计理念

Parcelable的设计理念是在保证一定性能的前提下,尽可能节省内存和CPU开销。

从架构上来看,Parcelable涉及到了Binder驱动、Parcel容器和IPCThreadState等几个关键组件,共同构成了高效的序列化通道:


(1)、Binder驱动

Binder驱动是Android的核心组件之一,负责进程间的数据传输。它在内核层为每个进程维护了一块受保护的共享内存区域,用于在进程间传递Parcelable对象。


(2)、Parcel容器

Parcel对象是存储序列化数据的临时载体。开发者需要先将对象写入Parcel中,然后由Binder驱动完成Parcel在进程间的拷贝和传递。


(3)、IPCThreadState

IPCThreadState是一个线程私有数据结构,负责在进程间管理请求和应答的Parcel对象数据。每个线程在与其他进程通信时,都会使用自己的IPCThreadState实例。


(4)、Parcelable接口

Parcelable接口定义了将对象写入和从Parcel容器读取的抽象协议,开发者需要手动实现这两个序列化方法。系统会按此协议完成对象的编码/解码操作。


序列化的基本流程如下:

  1. 当一个进程需要向另一个进程传输数据时,会先初始化一个Parcel容器对象;
  2. 将要传递的Parcelable对象通过writeToParcel()方法写入Parcel容器;
  3. Binder驱动从发送方进程拷贝这个Parcel容器到内核共享内存区域;
  4. 接收方进程从共享内存区读取Parcel数据,并通过Parcelable.Creator反序列化出原始对象;
  5. 接收进程的目标组件(如Activity)即可使用这个反序列化出的对象数据。

整个过程无需经过Java层的序列化操作,因此效率极高。Parcel容器采用面向流的编码格式存储数据,格式紧凑,内存占用小。

此外,Parcelable的实现细节还包括:

  • 支持平台默认Java数据类型的高效编解码;
  • 使用标志位压缩编码,节省空间;
  • 引入Parcel窗口缓存,加快读写效率;
  • 等等一系列优化手段。

从架构和流程上看,Parcelable不仅拥有简单的接口定义,而且在系统层得到了全方位的优化支持,使其在Android世界中成为高效低耗的序列化标准。当然,与之对应的是开发者必须自行编写序列化方法的工作量。但从性能的角度来看,这一点工作量是完全值得的。


二、Parcelable接口的使用


要使用Parcelable,需要自己实现这两个接口方法。

// 定义一个数据类MyData,实现Parcelable接口
public class MyData implements Parcelable {private int id;private String name;private boolean isAdult;// 构造函数public MyData(int id, String name, boolean isAdult) {this.id = id;this.name = name;this.isAdult = isAdult;}// 从Parcel反序列化时使用的特殊构造函数protected MyData(Parcel in) {id = in.readInt();name = in.readString();isAdult = in.readByte() != 0;}// writeToParcel方法,将数据写入Parcel@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(id);dest.writeString(name);dest.writeByte((byte) (isAdult ? 1 : 0));}// 生成用于反序列的CREATOR对象public static final Creator<MyData> CREATOR = new Creator<MyData>() {@Overridepublic MyData createFromParcel(Parcel in) {return new MyData(in);}@Overridepublic MyData[] newArray(int size) {return new MyData[size];}};// describeContents是一个内部接口标志@Overridepublic int describeContents() {return 0;}// getter/setter ...
}

接着就可以通过Intent、Binder等方式传输这个Parcelable对象了。系统在底层会自动完成序列化和反序列化的工作。

// 使用示例:传递Parcelable对象
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 创建MyData对象MyData data = new MyData(1, "Jack", true);// 使用Intent传递MyDataIntent intent = new Intent(this, SecondActivity.class);intent.putExtra("data", data);startActivity(intent);}
}// 接收Parcelable对象
public class SecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);// 从Intent中取出MyData对象MyData data = getIntent().getParcelableExtra("data");if (data != null) {int id = data.getId();String name = data.getName();boolean isAdult = data.isAdult();// 处理data...}}
}

在这个例子中:

  1. 我们定义了一个MyData类,实现了Parcelable接口。
  2. 在MyData类中,我们提供了一个用于Parcel反序列化的特殊构造函数,以及writeToParcel和describeContents方法。
  3. 同时生成了一个CREATOR对象,用于从Parcel重构MyData实例。
  4. 在MainActivity中,我们创建了一个MyData对象,并通过Intent将它传递给SecondActivity。
  5. 在SecondActivity中,我们从Intent中取出了MyData对象,可以使用其中的数据。

通过这个案例,你可以看到使用Parcelable传递一个自定义数据对象是非常简单的。只需要完成几个基本方法的实现,就可以实现对象的高效序列化和反序列化。

与手动实现序列化和Binder传递相比,使用Parcelable的代码更加简洁、安全,并得到了系统级的性能优化。这就是Parcelable作为Android高效IPC解决方案的魅力所在。


三、Parcelable的使用场景


以下是Parcelable主要用于的数据传输场景,以及结合案例代码演示和使用Parcelable的优点分析。

1、Activity间传递数据


当从一个Activity导航到另一个Activity时,可以使用Intent携带数据。如果数据对象实现了Parcelable接口,可以直接在Intent中使用。

// 创建Parcelable对象
MyData myData = new MyData("Hello", 123);// 通过Intent传递数据
Intent intent = new Intent(CurrentActivity.this, NextActivity.class);
intent.putExtra("MY_DATA_KEY", myData);
startActivity(intent);

在接收的Activity中:

// 接收Parcelable数据
Intent intent = getIntent();
MyData myData = intent.getParcelableExtra("MY_DATA_KEY");

2、Activity与Service传递数据


Parcelable也可以用于ActivityService之间的数据传输。可以通过Intent发送数据到Service,或者Service返回结果给Activity

// Activity发送数据到Service
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("MY_DATA_KEY", myData);
startService(serviceIntent);

3、通过Binder传输数据


在使用AIDL(Android Interface Definition Language)定义服务时,Parcelable可以用来在客户端和服务器之间传递数据。

// 在AIDL接口定义中
parcelable MyData;

4、将对象保存在Bundle或保存实例状态


Activity的生命周期中,可以在onSaveInstanceState方法中使用Bundle保存Parcelable对象。

@Override
public void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);outState.putParcelable("MY_DATA_KEY", myData);
}@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {super.onRestoreInstanceState(savedInstanceState);myData = savedInstanceState.getParcelable("MY_DATA_KEY");
}

四、Parcelable与Serializable的比较


与Java中的Serializable相比,Parcelable有以下优势:

1、性能更优
Parcelable序列化后的码流要比Serializable小得多,内存开销和CPU损耗也更低。

2、使用成本低
Parcelable无需使用反射,只需手写几个方法即可。

3、没有安全隐患
Parcelable不会自动完成数据的深复制,避免了Serializable可能带来的安全隐患。

当然,其缺点是需要手动编码实现序列化逻辑,并维护代码与类结构的同步,工作量较高。而Serializable则可以自动完成序列化。


五、Parcelable的性能优化建议


尽管Parcelable已经相当高效,但我们在实际使用时仍可以通过一些优化手段达到更佳的性能表现:

1、尽量使用标量类型
标量类型(int/long)可以直接通过writeInt/writeLong方法进行序列化,性能较高。

2、减少自动装箱操作
避免对装箱对象进行序列化,如Integer等。应直接使用基本类型。

3、编写高效的Parcelable方法
在writeToParcel方法中应先写入有效数据,而不是创建临时对象。

4、启用Parcelable代码生成器
Parceler等工具可以自动生成Parcelable代码,提高开发效率。


六、不得不提及的Bundler


Parcelable的强大远不止于上述简单用法。在Android 10开始,Google引入了Bundler框架,可以将任意的应用程序数据自动打包成一个Parcelable的Bundle,从而实现高效的跨进程通信。Bundler极大地简化了使用Parcelable的难度。

这一强大功能曾广受期待。令人惋惜的是,Bundler目前的可用性和成熟度似乎还有待提高。但不可否认,Google为我们展现了Parcelable在未来更大的应用前景。

无论是Android系统的Binder,还是Chrome浏览器的IPC数据传输,Parcelable都扮演着举足轻重的角色。它使得Android应用能够高效、安全地在进程间传输数据。面向未来,或许Parcelable的序列化能力会不断增强,甚至取代JVM的Serializable成为跨平台的数据序列化标准。让我们拭目以待吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/14595.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

web自动化之PO模式

PO模式 1、为什么需要PO思想&#xff1f; 首先我们观察和思考一下&#xff0c;目前我们写的作业脚本的问题&#xff1a; 元素定位和操作动 作写到一起了&#xff0c;这就就会用导致一个问题&#xff1a; UI的页面元素比较容易变化的&#xff0c;所以元素定位和脚本操作写到一…

如何将照片从 iPhone 传输到闪存驱动器【无质量损坏】

概括 人们喜欢用 iPhone 拍照&#xff0c;因为照片通常都很漂亮&#xff0c;这都要归功于 iPhone 令人惊叹的技术。但照片更新后会占用更多空间&#xff0c;并且您可能会开始收到没有存储空间的通知。因此&#xff0c;您可以将照片传输到 USB 驱动器&#xff0c;然后从 iPhone…

Spring Boot构建mvc项目

好的,以下是一个简单的Java MVC(Model-View-Controller)项目示例,使用Spring Boot框架和MySQL数据库。这个项目包括基本的CRUD操作。 项目结构 src/ └── main/├── java/│ └── com/│ └── example/│ └── demo/│ ├──…

springboot-阿里羚羊 服务端埋点

官方文档 集成Java SDK 手动引入jar包「quickaplus-log-collector-java-sdk-1.0.1-SNAPSHOT.jar」 <dependency><groupId>com.alibaba.lingyang</groupId><artifactId>quickaplus-log-collector-java-sdk</artifactId><version>1.0.1&l…

应用案例 | 如何实时监测和管理冷链仓库温湿度?

一、项目背景 冷链仓库温湿度管理的重要性在于确保仓库内产品的质量和安全。通过遵循相关法规和标准&#xff0c;满足客户对产品质量的需求&#xff0c;同时实施有效的温湿度管理措施&#xff0c;可以降低成本并提高仓库作业效率。该项目的实施旨在帮助客户保证产品的新鲜度&a…

Java - AbstractQueuedSynchronizer

AQS简介 AQS全称AbstractQueuedSynchronizer&#xff0c;抽象队列同步器&#xff0c;是一个实现同步组件的基础框架。AQS使用一个int类型的成员变量state维护同步状态&#xff0c;通过内置的同步队列&#xff08;CLH锁、FIFO&#xff09;完成线程的排队工作&#xff0c;底层主…

echarts 散点图修改散点图中图形形状颜色大小

话不多说&#xff0c;直接上代码 let option {color:[xxx, xxx, xxx, xxx], //直接设置color可修改图形颜色title: {text: 散点图图形,},tooltip: {trigger: axis,axisPointer: {type: cross}},legend: {top: 2,right:2,itemWidth: 10,itemHeight: 10,textStyle:{fontSize:14}…

shell脚本条件语句和循环语句

文章目录 一、条件语句测试比较整数数值字符串比较逻辑运算双中括号&#xff08; &#xff09;{ }if语句结构case语句 二、循环语句基础知识for循环whileuntil双重循环及跳出循环 一、条件语句 测试 条件测试&#xff1a;判断某需求是否满足&#xff0c;需要由测试机制来实现…

视频分类——C3D使用

整体比较分散&#xff0c;可能很多源码都需要修改&#xff0c;需要有耐心。 一、数据准备 PS 调研后&#xff0c;上手容易代码比较简洁的是&#xff1a;https://github.com/Niki173/C3D/tree/main 因为源码很多参数都写死到了源码中&#xff0c;没有解耦&#xff0c;并且默…

CCF-CSP认证 2024年3月 4.化学方程式配平

题解&#xff1a;首先完成数据的读入&#xff0c;然后高斯消元求秩按题意解即可 #pragma GCC optimize(2, 3, "Ofast", "inline") #include <bits/stdc.h> using namespace std; const int maxn 100;using matrix double[maxn][maxn]; using vect…

5.20Git

版本控制工具Git&#xff0c;其他的工具还有SVN 共享代码&#xff0c;追溯记录&#xff0c;存储.c文件 Git实现的功能&#xff1a;回溯&#xff08;以前某个时间节点的数据情况&#xff09;共享&#xff08;大家共享修改&#xff09; Git&#xff1a;80% SVN&#xff…

QT tableWidget详细分析

一.定义 QTableWidget是一个用于显示表格数据的Qt控件&#xff0c;它是一个基于Qt Model/View框架的视图组件。QTableWidget提供了一种简单的方式来展示和编辑表格数据&#xff0c;用户可以通过添加行、列和单元格来构建一个完整的数据表格。 下面是一些QTableWidget的主要特点…

The Missing Semester of Your CS Education(计算机教育中缺失的一课)

Shell 工具和脚本(Shell Tools and Scripting) 一、shell脚本 1.1、变量赋值 在bash中为变量赋值的语法是foobar&#xff0c;访问变量中存储的数值&#xff0c;其语法为 $foo。 需要注意的是&#xff0c;foo bar &#xff08;使用空格隔开&#xff09;是不能正确工作的&…

网工内推 | 香港移动,10年以上数通经验,CCIE,5W-6W

01 香港移动招聘 &#x1f537;招聘岗位&#xff1a;网络工程师 &#x1f537;岗位要求&#xff1a; 需要有10年及以上数通经验&#xff0c;有CCIE 证书&#xff0c;懂技术管理&#xff0c;沟通畅通 &#x1f537;语言要求&#xff1a; 粤语英语 &#x1f537;薪资&#xff1…

基于灰狼优化算法优化RBF(GWO-RBF)的数据回归预测(多输入多输出)

代码原理及流程 基于灰狼优化算法优化多输入多输出&#xff08;MIMO&#xff09;的RBF神经网络的数据回归预测&#xff0c;可以采取以下步骤&#xff1a; 1. 数据准备&#xff1a;准备包含多个输入特征和多个输出目标的数据集&#xff0c;确保数据已经经过预处理和归一化。 …

TCP - 半连接队列和全连接队列

目录 一、半连接队列和全连接队列的概念 二、全连接队列溢出 三、半连接队列溢出 一、半连接队列和全连接队列的概念 1. 半连接队列&#xff1a;服务端收到客户端发送的 SYN 包时&#xff0c;内核会将该连接加入半连接 SYN 队列&#xff0c;并向客户端返回响应 2. 全连接队…

CSS基础(第二天)

Emmet语法 快速生成HTML结构语法 1. 生成标签 直接输入标签名 按tab键即可 比如 div 然后tab 键&#xff0c; 就可以生成 <div></div> 2. 如果想要生成多个相同标签 加上 * 就可以了 比如 div*3 就可以快速生成3个div 3. 如果有父子级关系的标签&#xff0c;可以…

算法刷题笔记 数的范围(C++实现)(二分法重要例题)

文章目录 题目描述题目思路题目代码&#xff08;C&#xff09;题目感想 题目描述 给定一个按照升序排列的长度为n的整数数组&#xff0c;以及q个查询。对于每个查询&#xff0c;返回一个元素k的起始位置和终止位置&#xff08;位置从0开始计数&#xff09;。如果数组中不存在该…

Docker【2】iptables 错误解决

iptables 错误解决 问题说明问题分析解决步骤1. 确保 iptables 模块已加载2. 检查和重启 docker 服务3. 检查 firewalld 状态4. 重置 iptables 规则5. 查看和更新 Docker 配置 总结 问题说明 执行的 docker 命令如下&#xff0c;启动 nginx 并设置宿主机端口 (8080) 与容器端口…

学习Uni-app开发小程序Day25

这一章学习了触底加载更多阻止无效的网络请求、分类列表存入Storage在预览页面读取缓存展示、通过swiper的事件实现真正的壁纸预览及切换 触底加载更多阻止无效的网络请求、load-more样式的展现 前面已经学习了当列表触底后&#xff0c;会继续加载&#xff0c;当到最后一层后…