【Android】Serializable和Parcelable序列化对象:传递自定义类数据
三三要成为安卓糕手
一:Serializable序列化
1:提出问题 && 解决思路
提问:我们现在想传输的数据是一个类怎么办
思路:putExtra方法形参中不能传递自定义类,
①改造我们自己创建的类
②序列化
2:User类
实现Serializable接口:只用简单实现这个接口就可以了,不用重写什么方法,类在底层被转化成一种字节流的东西,从而达到数据传输的效果
public class User implements Serializable {private String name;//用户名private String age;private int mobile;public User(String name , String age , int mobile){this.name = name;this.age = age;this.mobile = mobile;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public int getMobile() {return mobile;}public void setMobile(int mobile) {this.mobile = mobile;}}
3:数据传输
else if (id == R.id.btn_second6) {Intent intent = new Intent(this, SerialActivity.class);User user = new User("三三", "18", 123456);intent.putExtra("key_user", user);startActivity(intent);}
4:数据接收
//接收类数据Intent intent = getIntent();User user = (User) intent.getSerializableExtra("key_user");if (user != null) {String name = user.getName();String age = user.getAge();int mobile = user.getMobile();Log.i(TAG, "onCreate: name = " + name);Log.i(TAG, "onCreate: age = " + age);Log.i(TAG, "onCreate: mobile = " + mobile);} else {Log.e(TAG, "onCreate: user is null");}
(1)getSerializableExtra
双参数的,需要版本在29以上,为了兼顾老版本,优先使用单参数方式
getSerializableExtra(String name, Class<T> clazz)
:这是 Android API 29 引入的方法,它在获取数据的同时,通过传入目标类型的 Class
对象,直接返回指定类型的数据;比如这段代码
Intent intent = getIntent();MySerializableClass myObj = intent.getSerializableExtra("key", MySerializableClass.class);if (myObj != null) {// 处理 myObj
}
getSerializableExtra(String name)
:返回一个 Serializable
类型的对象。获取到数据后,需要强制将其转换为实际需要的类型。
二:Parcelable打包数据
想要让数据传输中性能更好,Android中专门诞生了一个接口Parcelable(包是安卓下的)
顾名思义parcelable 翻译为 “可打包的” (用于跨进程传输)
1:实现Parcelable接口重写方法
public class Student implements Parcelable {private static final String TAG = "Student";private String name;//用户名private String age;//年龄private int mobile;//手机号public Student(String name, String age, int mobile) {this.name = name;this.age = age;this.mobile = mobile;}public Student(Parcel parcel){name = parcel.readString();age = parcel.readString();mobile = parcel.readInt();}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public int getMobile() {return mobile;}public void setMobile(int mobile) {this.mobile = mobile;}@Overridepublic int describeContents() {return 0;}/*** 打包操作*/@Overridepublic void writeToParcel(@NonNull Parcel dest, int flags) {dest.writeString(name);dest.writeString(age);dest.writeInt(mobile);Log.i(TAG, "writeToParcel: ");}/*** 拆包操作*/public static final Creator<Student> CREATOR = new Creator<Student>() {@Overridepublic Student createFromParcel(Parcel source) {Log.i(TAG, "createFromParcel: source" + source);return new Student(source);}@Overridepublic Student[] newArray(int size) {return new Student[0];}};}
2:describeContents
- 主要用于描述当前对象中包含的特殊对象类型(如文件描述符等)——这里了解即可,用的不多
- 通常情况下返回 0 即可,只有当对象中包含需要特殊处理的数据时才返回非 0 值
3:writeToParcel-打包
对象序列化的核心方法,将当前对象的所有成员变量写入到 Parcel
对象中,后续可以通过对应的 CREATOR
从 Parcel
中恢复对象
(1)@NonNull Parcel dest
参数dest
是 destination 的缩写,意为 “目的地”。传递数据的时候做一个打包的工作
/*** 打包操作*/@Overridepublic void writeToParcel(@NonNull Parcel dest, int flags) {dest.writeString(name);dest.writeString(age);dest.writeInt(mobile);Log.i(TAG, "writeToParcel: ");}
4:Creator CREATOR-拆包
(1)拆包思路一
返回的是一个Student对象,那就先读取三个变量,在作为构造方法里的参数进行传参,但是不行因为这是静态方法
CREATOR
是 静态内部实现(public static final Creator<Student> CREATOR
),方法内部只能访问静态成员变量
(2)Parcelable
的 “约定玩法”
官方 / 社区习惯上,会给 Parcelable
类专门写一个 protected
或 public
的构造函数,参数是 Parcel
,专门用来接收数据,用于反序列化,为对象的成员变量赋值。
public Student(Parcel parcel){name = parcel.readString();age = parcel.readString();mobile = parcel.readInt();}
public static final Creator<Student> CREATOR = new Creator<Student>() {@Overridepublic Student createFromParcel(Parcel source) {Log.i(TAG, "createFromParcel: source" + source);return new Student(source);}@Overridepublic Student[] newArray(int size) {return new Student[0];}};
5:createFromParcel(Parcel source)
方法
作用:从序列化后的 Parcel
对象中读取数据
参数 source
:是之前通过 writeToParcel()
写入数据的 Parcel
对象(可以理解为 “数据源”)。
6:newArray(int size)
方法
Student
数组,用于批量反序列化时使用(例如从 Parcel
中读取多个 Student
对象时)。
参数 size
:需要创建的数组长度。返回值写成 new Student[size]
更合适
三:Parcelable数据传输和接收
1:数据传输
else if (id == R.id.btn_second7) {try {Intent intent = new Intent(this, ParcelActivity.class);Student sansan = new Student("Sansan", "18", 12345);intent.putExtra("key_student", sansan);startActivity(intent);} catch (Exception e) {Log.e(TAG, "跳转ParcelActivity失败: " + e.getMessage(), e);}}
2:数据接收
public class ParcelActivity extends AppCompatActivity {private static final String TAG = "ParcelActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_parcel);Intent intent = getIntent();//注意这段代码,不是new Intent();Student student = intent.getParcelableExtra("key_student");String name = student.getName();String age = student.getAge();int mobile = student.getMobile();Log.i(TAG, "onCreate: name = " + name);Log.i(TAG, "onCreate: age = " + age);Log.i(TAG, "onCreate: mobile" + mobile);}
}
在某个版本之前也是被限定的,用兼容性更好的单个参数即可
3:结果
通过日志可以看见通过parclable传过来的数据
四:插件简化Parcelable开发
Parcelable中很多代码是规范化和统一化的,所以使用一个插件能减少我们的工作量,直接生成模版
五:总结
两种传递类数据的方式,前者silizeable简单,后者难但是性能好