首页 > 编程知识 正文

android什么意思,android版本

时间:2023-05-06 05:26:31 阅读:154367 作者:4139

背景是在开发插入式App时,AIDL为了实现进程间通信而使用的。 AIDL要传递对象类型的数据,必须序列化对象。

在Android开发中,经常需要序列化和反序列化对象。

最常见的是,通过Intent传输数据时,Intent只能传输基本数据类型、字符串类型、可序列化和可反序列化对象类型

要通过internet传递对象类型,该对象类型必须支持序列化和反序列化。

Android为完成序列化和反序列化过程提供了两种方法。

一种是可串行化方式,另一种是可并行化方式; 本文尽可能详细地介绍了两种方法的序列化。

首先,让我们理解序列化和反序列化。

序列化和反序列化是什么? 非基本数据类型(如对象、文件和图像)都有自己的数据格式,很难批量传输和存储。

基本数据类型提供了一种转换为byte[]的方法,因此可以将数据统一到字节流中。

为了使对象、文件和图像数据也可以在计算机上传输和存储,可以将这些数据格式转换为字节流。

特定语言类别中为实例对象编码成字节流序列化; 将一个字节流中读出一个对象实例称为反序列化

PS :基本数据类型

简要介绍四种整数类型byte、short、int、long的浮点数型float、double的单字型char的单布尔型boolean的上述字节和字符。

字节流和字符流的字节流是由字节组成的流,也有相关的字符流。 这是一个由字符组成的流。

这里的流动可以看作水流,水就是数据,流动就是流入和流出。

那么,字节和文字是什么?

字节和字符字节(byte ) :计算机的计量单位,表示数据量的多寡。 通常1byte=8bit字符(Character ) :用于在计算机中表示字符、数字、字和符号。 一般(ASCII代码)在英语状态下一个字符或字符占用一个字节,一个汉字用两个字节表示。

之所以有代码表,是因为计算机在世界各地的各个国家和地区都有使用,支持不同国家的字符在计算机上统一使用

因此,它提供了一个将字符批量转换为字节的编码表。

典型的编码表字符和字节的对应关系如下:

在ASCII代码中,一个字母(无论大小写)是一个字节,一个中文汉字是两个字节。 GBK码中,一个英文字母(无论大小写)为一个字节,一个中文汉字为两个字节。 在UTF-8编码中,一个英文字符是一个字节,一个中文字符是三个字节。 在Unicode代码中,一个英语是一个字节,一个中文是两个字节。 符号:英语标点符号是一个字节,中文标点符号是两个字节。 例如,英语句号。 占一个字节的大小,中文句号。 占2字节的大小。 那么,可以明确序列化的目的。 即使是为了向计算机传递统一格式的数据。

计算机传输的本质是字节,所以本质上是将数据转换为byte[]数据格式并通过计算机进行传输。

Android中常见的序列化场景文件的读取/写入是通过字节流完成的,序列化对象后可以将其保存到文件中。 网络数据传输也通过字节流进行,因此可以将对象序列化并在网络之间传输。 进程之间的通信需要序列化,例如使用AIDL传递对象时。 只能在internet之间传递基本数据类型,如果需要传递复杂的对象,则需要序列化。 安卓实现串行化的两种方法串行化able:Java提供的串行化方案parcelable :安卓提供的串行化方案串行化接口是Java提供的

要序列化对象,只需实现java.io.Serializable接口。

实现可序列化的类对象的所有属性都必须是可序列化的。 如果存在不需要可序列化的属性,请使用transient关键字进行限定。

例如,将对象保存到文本文件中并读取。

对文件的写入通过ObjectOutputStream的writeobject(objectobj )方法实现,文件的读取通过ObjectInputStream的readObject )实现。

ObjectOutputStream:是一种对象输出流,用于将对象转换为字节数据,并将其输出到文件中进行存储。 ObjectInputStream:是一个对象输入流,用于将文件中的字节数据转换为对象。 让我们看一下使用案例。

import java.io.Serializable; publicclassuserimplementsserializable { privatestaticfinallongserialversionuid=1L; private String name=' '; //transient表示不序列化此属性private transient int age=0。 //Child对象也必须实现序列化。 否则就必须加tran

sient 禁止该属性序列化 private Child child = null; // 省略 get set 等方法}import java.io.Serializable;public class Child implements Serializable { private static final long serialVersionUID = 12L; private int age = 0; // 省略 get set 等方法}// 写入文件private static void writeUser(){ //序列化到本地 User user=new User("crdxhd",20, new Child(2)); try { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:\user.txt")); out.writeObject(user); out.close(); } catch (IOException e) { e.printStackTrace(); }}// 从文件读取private static void readUser(){ try { ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\user.txt")); User user=(User)in.readObject(); System.out.println("user :"+user.toString()); // 输出 // user :User{name='crdxhd', age=0, child=Child{age=2}} // 解释 // 由于我们在 User 对象的 age 属性设置了 transient 关键字,所以 age 不会被序列化。 // 也就是写入的 age=20 保存不了,所以读取出来的数据是默认的数字 0 in.close(); } catch (Exception e) { e.printStackTrace(); }}

如果对象没实现Serializable接口就直接调用ObjectOutputStream、ObjectInputStream会报错java.io.NotSerializableException。
而且User对象中的Child对象如果没实现Serializable接口也会报该错,进一步说明了对象中所有要序列化的对象都要实现Serializable接口。
我们在User对象的age属性设置了transient,在写入User数据age=20之后但是读取数据时age=0也说明了transient可以忽略属性的序列化。

关于serialVersionUID最好是用private显式声明,而且必须是static final long类型,不然在反序列化时可能会报错。

这个serialVersionUID是用来辅助序列化和反序列化的。
如不显式声明,编译器会自动去计算出一个值并赋予它。

那么Serialable是如何实现序列化的呢?

Serializable 实现原理

我们在写入文件时调用了writeObject()方法,那么我们就从该方法入手。

参考小缘大佬的源码解读:地址-https://www.wanandroid.com/wenda/show/9002

借助ObjectStreamClass记录目标对象的类型,类名等信息,这个类里面还有个ObjectStreamField数组,用来记录目标对象的内部变量;在defaultWriteFields方法中,会先通过ObjectStreamClass的getPrimFieldValues方法,把基本数据类型的值都复制到一个叫primVals的byte数组上;接着通过getPrimFieldValues方法来获取所有成员变量的值,出乎意料的是:这两个获取值的方法,里面都不是我们常规的反射操作(Field.get),而是通过操作Unsafe类来完成的;遍历剩下不是基本数据类型的成员变量,然后递归调用writeObject方法(也就是一层层地剥开目标对象,直到找到基本数据类型为止)

UnSafe类的相关的内容可自行搜索,这里就简短描述了,毕竟没用过。

Unsafe类是在sun.misc包下,不属于Java标准。但是很多Java的基础类库,使用Unsafe可用来直接访问系统内存资源并进行自主管理。Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。Unsafe可认为是Java中留下的后门,提供了一些低层次操作,如直接内存访问、线程调度等。但是官方并不建议使用Unsafe。
来源:https://www.jb51.net/article/140726.htm

小结:
Serializable实现原理本质是利用UnSafe类去获取对象的数据。为什么不是用反射呢?
其实反射获取数据最后也是通过UnSafe类去获取。

例如:我们通过反射获取上面User中的age属性,getInt的实现如下:

public void getUserAgeByReflection() throws Exception { //获取student类的字节码对象 Class clazz = Class.forName("model.User"); //用反射创建一个对象 Object user = clazz.newInstance(); //获取字段 Field ageField = clazz.getDeclaredField("age"); ageField.getInt(user);}

ageField.getInt(user); 的实现在 UnsafeCharacterFieldAccessorImpl 类,我们可以看出反射底层还是调用的 UnSafe 类

public int getInt(Object var1) throws IllegalArgumentException { this.ensureObj(var1); return unsafe.getInt(var1, this.fieldOffset);}

具体信息可查看源码。

由于Serializable是通过I/O读写存储在磁盘上的数据, 并且使用了UnSafe类去获取数据。
我们知道反射会有性能问题,而反射底层实现就是UnSafe类,所以使用Serializable序列化会有性能问题。
Android的卡顿问题绝对是用户最头疼的问题,因此在Android上通过Parcelable来优化序列化导致的性能问题。

Parcelable

Parcelable 是 Android 提供的序列化接口。

这里举个例子:两个页面之间传递对象

先定义对象并实现 Parcelable class UserParcelable implements Parcelable { private String name = ""; private int age = 0; public UserParcelable(String name, int age) { this.name = name; this.age = age; } protected UserParcelable(Parcel in) { name = in.readString(); age = in.readInt(); } /** * 反序列化 */ public static final Creator<UserParcelable> CREATOR = new Creator<UserParcelable>() { @Override public UserParcelable createFromParcel(Parcel in) { return new UserParcelable(in); } @Override public UserParcelable[] newArray(int size) { return new UserParcelable[size]; } }; @Override public int describeContents() { return 0; } /** * 序列化 * @param dest * @param flags */ @Override public void writeToParcel(Parcel dest, int flags) { // 写入数据 dest.writeString(name); dest.writeInt(age); } @Override public String toString() { return "UserParcelable{" + "name='" + name + ''' + ", age=" + age + '}'; }}

页面FirstAc通过Intent传递给SecondAc

// 发送数据Intent intent = new Intent(FirstAc.this, SecondAc.class);intent.putExtra("UserParcelable", new UserParcelable("kldrjb", 18));startActivity(intent);// 接收数据Intent intent = getIntent();UserParcelable userParcelable = intent.getParcelableExtra("UserParcelable");Log.d("SecondAc", userParcelable.toString()); Parcelable 实现原理

还是参考小缘大佬的源码解读:地址-https://www.wanandroid.com/wenda/show/9002

它的各种writeXXX方法,在native层都是会调用Parcel.cpp的write方法,它是通过memcpy函数直接复制内存地址由一个叫mData的uint8_t来保存。

read方法同理,它也是通过 memcpy函数来把mData上的某部分数据复制出来。

二者的区别 区别SerializableParcelable所属APIJAVA APIAndroid SDK API原理序列化和反序列化过程需要在磁盘上进行大量的I/O操作,且通过反射实现有性能问题序列化和反序列化过程直接在native端操作内存开销开销大开销小效率低很高使用场景序列化到本地或者通过网络传输本地内存序列化总结

序列化的作用统一数据格式便于数据传输和保存。

由于计算机中数据是以字节流传递,因此大部分的编程语言实现序列化的做法都是转为byte[]
而全球众多文字转为字节是通过特定的编码方式实现,例如GBK编码中,一个英文字符表示一个字节,一个中文字符表示两个字节。

Android中有两种方式实现序列化,一个是实现Serializable,另一个是实现Parcelable。
Serializable是Java提供的序列化方案,
Parcelable是Android为了解决Serializable序列化导致的性能问题而提供的方案。

Serializable实现简单,但是会有性能问题,原因是它数据的读写是通过I/O在磁盘上操作,而且获取数据使用的是和反射底层用的是一个类:UnSafe类,该类可以直接操作内存,官方建议不熟悉该类最好别使用。
Parcelable实现略微复杂,通过writeXXX,readXXX方法实现数据的读写,最终通过native的方法去内存中读取数据。

Serializable序列化后的数据可以进行网络传输本地存储
Parcelable是基于Android的,只能在内存间传输。

参考 Java:字节流和字符流(输入流和输出流)Android中两种序列化方式的比较Serializable和Parcelable一直在用,但Serializable到底是怎么一回事?每日一问 Parcelable 为什么效率高于 Serializable ?Java中Unsafe类详解

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。