在本文中,我们将为您详细介绍Android:如何对我的对象实施Parcelable?的相关知识,并且为您解答关于android对象池的疑问,此外,我们还会提供一些关于(android)Parcelab
在本文中,我们将为您详细介绍Android:如何对我的对象实施Parcelable?的相关知识,并且为您解答关于android 对象池的疑问,此外,我们还会提供一些关于(android) Parcelable 接口的使用、Android Activity 传递Parcelable对象、Android Parcelable、Android Parcelable 和 Serializable 的区别的有用信息。
本文目录一览:- Android:如何对我的对象实施Parcelable?(android 对象池)
- (android) Parcelable 接口的使用
- Android Activity 传递Parcelable对象
- Android Parcelable
- Android Parcelable 和 Serializable 的区别
Android:如何对我的对象实施Parcelable?(android 对象池)
我有一个ConstantData类,该类保存我的JSON索引,我需要在活动之间附加传递它们。但在此之前,我必须首先对此类的对象实现Parcelable。
我的问题是,如何在这里的类中声明对象并将每个对象放入变量中?
我是新手,现在我一无所有。谢谢。请随意在下面修改我的代码。
ConstantData.java
public class ConstantData{ public static String project_title = "project title"; public static String organization_title = "organization title"; public static String keyword = "keyword"; public static String short_code = "short code"; public static String project_description = "description"; public static String smallImageUrl = "smallImageUrl"; public static String bigImageUrl = "bigImageUrl"; public static String price= "price"; public static String country= "country"; public static ArrayList<Project> projectsList = new ArrayList<Project>(); public int describeContents() { return 0; } public void writeToParcel(Parcel out, int flags) { out.writeString(project_title); out.writeString(organization_title); out.writeString(keyword); out.writeString(short_code); out.writeString(project_description); out.writeString(smallImageUrl); out.writeString(bigImageUrl); out.writeString(price); out.writeString(country); } public static final Parcelable.Creator<ConstantData> CREATOR = new Parcelable.Creator<ConstantData>() { public ConstantData createFromParcel(Parcel in) { return new ConstantData(in); } public ConstantData[] newArray(int size) { return new ConstantData[size]; } }; private ConstantData(Parcel in) { project_title = in.readString(); organization_title = in.readString(); keyword = in.readString(); short_code = in.readString(); project_description = in.readString(); smallImageUrl = in.readString(); bigImageUrl = in.readString(); price = in.readString(); country = in.readString(); } }
如果我的问题不够清楚,您可以查找以下问题:如何使用Intent将对象从一个Android Activity发送到另一个Android
Activity?
他myParcelableObject
在那儿写道,我只是不知道如何制作可包裹的物体。
编辑 Project.java
public class Project { public String project_title; public String organization_title; public String keyword; public String short_code; public String project_description; public String smallImageUrl; public String bigImageUrl; public String price; public String country; }
答案1
小编典典您需要实现Parcelable
接口
public class ConstantData implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(project_title); .... out.writeString(country); out.writeList(projectsList); // <<-- } private ConstantData(Parcel in) { project_title = in.readString(); .... country = in.readString(); projectsList = in.readList();
我认为为了写作的projectsList
工作,Project
班级也需要实施Parcelable
。
例如,请参见此类。
(android) Parcelable 接口的使用
什么是 Parcelable ?
Parcelable, 定义了将数据写入 Parcel,和从 Parcel 中读出的接口。一个实体(用类来表示),如果需要封装到消息中去,就必须实现这一接口,实现了这一接口,该实体就成为 “可打包的” 了。
Parcelable 传递对象
Android 序列化对象主要有两种方法:
1. 实现 Serializable 接口,实现 Serializable 接口是 JavaSE 本身就支持的;
2. 实现 Parcelable 接口,Parcelable 是 Android 特有的功能,效率比实现 Serializable 接口高,像用于 Intent 数据传递也都支持,而且还可以用在进程间通信 (IPC),
除了基本类型外,只有实现了 Parcelable 接口的类才能被放入 Parcel 中。
Parcelable 接口定义
public interface Parcelable {
//内容描述接口,基本不用管
public int describeContents();
//写入接口函数,打包
public void writeToParcel(Parcel dest, int flags);
//读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板参数传入。
//为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数分别返回单个和多个继承类实例。
public interface Creator<T> {
public T createFromParcel(Parcel source);
public T[] newArray(int size);
}
怎么实现 Parcelable 接口?
从 parcelable 接口定义中,我们可以看到,实现 parcelable 接口,需要我们实现下面几个方法:
1.describeContents 方法。内容接口描述,默认返回 0 就可以;
2.writeToParcel 方法。该方法将类的数据写入外部提供的 Parcel 中。即打包需要传递的数据到 Parcel 容器保存,以便从 parcel 容器获取数据,该方法声明如下:
writeToParcel (Parcel dest, int flags) 具体参数含义见 javadoc
3. 静态的 Parcelable.Creator 接口,本接口有两个方法:
createFromParcel (Parcel in) 从 Parcel 容器中读取传递数据值,封装成 Parcelable 对象返回逻辑层。
newArray (int size) 创建一个类型为 T,长度为 size 的数组,仅一句话(return new T [size]) 即可。方法是供外部类反序列化本类数组使用。
代码实现
封装数据,把实现 parcelable 接口的 Person 对象传递到 TwoActivity 里;
public class DemoActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 封装数据
Person p = new Person();
p.setId(1);
p.setName("xiaoming");
// 用Intent传递Person对象
Intent i = new Intent(this, TwoActivity.class);
i.putExtra("Person", p);
startActivity(i);
}
}
2.TwoActivity 获取数据,从 DemoActivity 传递的 Person 对象给解析,并打印;
public class TwoActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Person p = (Person)getIntent().getParcelableExtra("Person");
System.out.println("p_id"+p.getId());
System.out.println("p_name"+p.getName());
}
}
3.parcelable 接口的实现
public class Person implements Parcelable{
// 成员变量
private int id;
private String name;
// 1.必须实现Parcelable.Creator接口,否则在获取Person数据的时候,会报错,如下:
// android.os.BadParcelableException:
// Parcelable protocol requires a Parcelable.Creator object called CREATOR on class com.um.demo.Person
// 2.这个接口实现了从Percel容器读取Person数据,并返回Person对象给逻辑层使用
// 3.实现Parcelable.Creator接口对象名必须为CREATOR,不如同样会报错上面所提到的错;
// 4.在读取Parcel容器里的数据事,必须按成员变量声明的顺序读取数据,不然会出现获取数据出错
// 5.反序列化对象
public static final Parcelable.Creator<Person> CREATOR = new Creator(){
@Override
public Person createFromParcel(Parcel source) {
// TODO Auto-generated method stub
// 必须按成员变量声明的顺序读取数据,不然会出现获取数据出错
Person p = new Person();
p.setId(source.readInt());
p.setName(source.readString());
return p;
}
@Override
public Person[] newArray(int size) {
// TODO Auto-generated method stub
return new Person[size];
}
};
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
// 1.必须按成员变量声明的顺序封装数据,不然会出现获取数据出错
// 2.序列化对象
dest.writeInt(id);
dest.writeString(name);
}
}
Android Activity 传递Parcelable对象
前面我们知道了启动activity的时候可以传递一些参数。Activity的跳转时可以传递Parcelable对象。
Parcelable对象和Serializable不一样。实现了Parcelable接口的类并不会被系统序列化。
接下来我们用一个例子看看如何使用这一接口。
使用例子
先准备数据,然后传送Parcelable对象。
数据准备
先设计一个类实现Parcelable接口。前面我们使用Serializable的时候,类只要实现Serializable接口即可,不需要额外的操作。但用Parcelable接口会需要开发者多做一些工作。
Parcelable接口在android.os
包里,与Serializable不同。我们必须明确认识这一点。
官方给出的一个使用例子。
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
可以看到,强制使用了一个Parcelable.Creator
对象。里面的方法我们暂时不管也不修改。
重点关注私有构造器MyParcelable(Parcel in
和writeToParcel
方法。
官方例子中,私有构造器接收一个Parcel对象,然后从中读出一个int。而writeToParcel
方法中,把mData写入Parcel对象中。
这写入和读出操作就是我们开发者需要特别关心的地方。
之前使用intent.putExtra方法的时候会传入一个String类型的key(键),用于标示传入的数据。
但在官方的例子中,我们只看到了一个int。而writeInt方法也没有指定key。系统如何区分出各个参数呢?
我们自定义一个类DataParcel
,实现Parcelable
接口。as自动在里面生成了CREATOR。
import android.os.Parcel;
import android.os.Parcelable;
public class DataParcel implements Parcelable {
private int number;
private String str1;
private String str2;
private String noSave = "[不传送的字符串]";
// getter setter ...
public String info() {
return "number: "+number+", str1: "+str1+", str2: "+str2+", noSave: "+noSave;
}
protected DataParcel(Parcel in) {
number = in.readInt();
str1 = in.readString();
str2 = in.readString();
}
public DataParcel(int number, String str1, String str2, String noSave) {
this.number = number;
this.str1 = str1;
this.str2 = str2;
this.noSave = noSave;
}
public static final Creator<DataParcel> CREATOR = new Creator<DataParcel>() {
@Override
public DataParcel createFromParcel(Parcel in) {
return new DataParcel(in);
}
@Override
public DataParcel[] newArray(int size) {
return new DataParcel[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(number);
dest.writeString(str1);
dest.writeString(str2);
}
}
可以看到我们有1个int和3个String。公开构造器需要传入这4个属性。
writeToParcel
方法中,按顺序写入了int和2个String。私有构造器中,按顺序读出了int和2个String。
noSave
并没有被写入和读出。拿来做对比。info()
方法是拿来打印信息的。
传送Parcelable对象
现在我们的类已经设计好了,传送对象试试。
把DataParcel对象交给intent。
DataParcel dataParcel = new DataParcel(100, "s1", "s2", "改变这个字符串看看能否被传递");
intent.putExtra(SendParamsDemo.K_PARCEL, dataParcel);
被打开的Activity接收传入的对象。
DataParcel dataParcel = intent.getParcelableExtra(K_PARCEL);
log中打印出发送和传入的对象信息。
D/rustAppMainActivity: goSendParamsDemo: parcel obj: com.rustfisher.tutorial2020.act.DataParcel@d8ce985
D/rustAppMainActivity: goSendParamsDemo: parcel obj: number: 100, str1: s1, str2: s2, noSave: 改变这个字符串看看能否被传递
D/rustAppSendParamsDemo: gotInput: parcel obj: com.rustfisher.tutorial2020.act.DataParcel@d90a3a6
D/rustAppSendParamsDemo: gotInput: number: 100, str1: s1, str2: s2, noSave: [不传送的字符串]
从log中我们可以看出,发送的对象和接收到的对象并不是同一个对象。但我们指定的那3个属性是相同的。
总结
至此,我们了解了如何使用Parcelable这个接口。
Parcel和Parcelable是Android IPC中使用到的容器和工具。大家可以了解一下Binder机制
一般认为,普通情况下Parcelable性能上会优于Serializable。
Serializable涉及到序列化,系统会通过反射的方法来获取信息。相对而言比较耗资源。
Parcel并不涉及序列化机制。它是为了高性能IPC传输设计的。因此,Parcel并不适合用来永久化存储数据。
实际工作中,我们可以根据业务需要,综合开发时间成本和应用性能要求,来选择使用Parcelable或者Serializable。
- 示例代码
- Android Activity 传递Parcelable对象
Android Parcelable
Parcelable(SDK)
Interface for classes whose instances can be written to and restored from a Parcel
.
Classes implementing the Parcelable interface must also have a static field called CREATOR
, which is an object implementing the Parcelable.Creator
interface.
Passing data between activities is quite easy.
You would normally do that using the Bundle packed into an intent. But sometimes you need to pass complex objects from one activity to another.
One workaround would be to keep a static instance of the object int your Activity and access it from you new Activity. This might help, but it''s definitely not a good way to do this.
To pass such objects directly through the Bundle, your class would need to implement the Parcelable interface.
Example:
MainActivity, SubActivity, Person
MainActivity
- package com.learn;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- public class MainActivity extends Activity implements OnClickListener {
- private static final String TAG = "Parcelable";
- public static final String KEY = "key";
- private Button btn;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Log.d(TAG, "MainActivity");
- btn = (Button)findViewById(R.id.btn);
- btn.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- Log.d(TAG, "onClick");
- if(R.id.btn == v.getId()){
- Person mPerson = new Person();
- mPerson.setName("Bill");
- mPerson.setAge(22);
- Bundle bundle = new Bundle();
- bundle.putParcelable(KEY, mPerson); // mPerson -> Parcelable
- Log.d(TAG, "mPerson=" + mPerson);
- Intent intent = new Intent(this, SubActivity.class);
- intent.putExtras(bundle);
- startActivity(intent);
- Log.d(TAG, "startActivity");
- }
- }
- }
package com.learn; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity implements OnClickListener { private static final String TAG = "Parcelable"; public static final String KEY = "key"; private Button btn; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.d(TAG, "MainActivity"); btn = (Button)findViewById(R.id.btn); btn.setOnClickListener(this); } @Override public void onClick(View v) { Log.d(TAG, "onClick"); if(R.id.btn == v.getId()){ Person mPerson = new Person(); mPerson.setName("Bill"); mPerson.setAge(22); Bundle bundle = new Bundle(); bundle.putParcelable(KEY, mPerson); // mPerson -> Parcelable Log.d(TAG, "mPerson=" + mPerson); Intent intent = new Intent(this, SubActivity.class); intent.putExtras(bundle); startActivity(intent); Log.d(TAG, "startActivity"); } } }
SubActivity
- package com.learn;
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.Parcelable;
- import android.util.Log;
- import android.widget.TextView;
- public class SubActivity extends Activity {
- private static final String TAG = "Parcelable";
- private TextView tv;
- @Override
- public void onCreate(Bundle savedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Log.d(TAG, "SubActivity");
- Parcelable parcelable = getIntent().getParcelableExtra(MainActivity.KEY);
- Person mPerson = (Person)parcelable; // Parcelable -> Person
- Log.d(TAG, "parcelable=" + parcelable + "; mPerson=" + mPerson);
- tv = (TextView)findViewById(R.id.tv);
- tv.setText("name=" + mPerson.getName() + "; age=" + mPerson.getAge());
- }
- }
package com.learn; import android.app.Activity; import android.os.Bundle; import android.os.Parcelable; import android.util.Log; import android.widget.TextView; public class SubActivity extends Activity { private static final String TAG = "Parcelable"; private TextView tv; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.d(TAG, "SubActivity"); Parcelable parcelable = getIntent().getParcelableExtra(MainActivity.KEY); Person mPerson = (Person)parcelable; // Parcelable -> Person Log.d(TAG, "parcelable=" + parcelable + "; mPerson=" + mPerson); tv = (TextView)findViewById(R.id.tv); tv.setText("name=" + mPerson.getName() + "; age=" + mPerson.getAge()); } }
Person
- package com.learn;
- import android.os.Parcel;
- import android.os.Parcelable;
- import android.util.Log;
- public class Person implements Parcelable{
- private static final String TAG = "Parcelable";
- private String name;
- private int age;
- public Person(){
- }
- public void setName(String name){
- this.name = name;
- }
- public String getName(){
- return this.name;
- }
- public void setAge(int age){
- this.age = age;
- }
- public int getAge(){
- return this.age;
- }
- public static final Parcelable.Creator<Person> CREATOR = new Creator<Person>() {
- @Override
- public Person createFromParcel(Parcel source) {
- Log.d(TAG, "createFromParcel(Parcel source)");
- Person mPerson = new Person();
- mPerson.name = source.readString();
- mPerson.age = source.readInt();
- return mPerson;
- }
- @Override
- public Person[] newArray(int size) {
- Log.d(TAG, "newArray(int size)");
- return new Person[size];
- }
- };
- @Override
- public int describeContents() {
- Log.d(TAG, "describeContents()");
- return 0;
- }
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- Log.d(TAG, "writeToParcel(Parcel dest, int flags)");
- dest.writeString(name);
- dest.writeInt(age);
- }
- }
package com.learn; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; public class Person implements Parcelable{ private static final String TAG = "Parcelable"; private String name; private int age; public Person(){ } public void setName(String name){ this.name = name; } public String getName(){ return this.name; } public void setAge(int age){ this.age = age; } public int getAge(){ return this.age; } public static final Parcelable.Creator<Person> CREATOR = new Creator<Person>() { @Override public Person createFromParcel(Parcel source) { Log.d(TAG, "createFromParcel(Parcel source)"); Person mPerson = new Person(); mPerson.name = source.readString(); mPerson.age = source.readInt(); return mPerson; } @Override public Person[] newArray(int size) { Log.d(TAG, "newArray(int size)"); return new Person[size]; } }; @Override public int describeContents() { Log.d(TAG, "describeContents()"); return 0; } @Override public void writeToParcel(Parcel dest, int flags) { Log.d(TAG, "writeToParcel(Parcel dest, int flags)"); dest.writeString(name); dest.writeInt(age); } }
main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <TextView
- android:id="@+id/tv"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/hello" />
- <Button android:id="@+id/btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Parcelable" />
- </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/tv" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Parcelable" /> </LinearLayout>
Running Result
click, then
Log
- D/Parcelable( 487): MainActivity
- D/Parcelable( 487): onClick
- D/Parcelable( 487): mPerson=com.learn.Person@40520b98
- D/Parcelable( 487): writeToParcel(Parcel dest, int flags)
- D/Parcelable( 487): startActivity
- D/Parcelable( 487): SubActivity
- D/Parcelable( 487): createFromParcel(Parcel source)
- D/Parcelable( 487): parcelable=com.learn.Person@405269f8; mPerson=com.learn.Person@405269f8
D/Parcelable( 487): MainActivity D/Parcelable( 487): onClick D/Parcelable( 487): mPerson=com.learn.Person@40520b98 D/Parcelable( 487): writeToParcel(Parcel dest, int flags) D/Parcelable( 487): startActivity D/Parcelable( 487): SubActivity D/Parcelable( 487): createFromParcel(Parcel source) D/Parcelable( 487): parcelable=com.learn.Person@405269f8; mPerson=com.learn.Person@405269f8
Analyse
writeToParcel(Parcel dest, int flags) ---- bundle.putParcelable(KEY, mPerson)
createFromParcel(Parcel source) ---- (Person)getIntent().getParcelableExtra(MainActivity.KEY)
source ---- dest
Download
Parcelable - How to do that in Android
Writing Parcelable classes for Android
原文链接: http://blog.csdn.net/sunboy_2050/article/details/6949809
Android Parcelable 和 Serializable 的区别
学习内容:
1. 序列化的目的
2.Android 中序列化的两种方式
3.Parcelable 与 Serializable 的性能比较
4.Android 中如何使用 Parcelable 进行序列化操作
5.Parcelable 的工作原理
6. 相关实例
1. 序列化的目的
(1). 永久的保存对象数据 (将对象数据保存在文件当中,或者是磁盘中
(2). 通过序列化操作将对象数据在网络上进行传输 (由于网络传输是以字节流的方式对数据进行传输的。因此序列化的目的是将对象数据转换成字节流的形式)
(3). 将对象数据在进程之间进行传递 (Activity 之间传递对象数据时,需要在当前的 Activity 中对对象数据进行序列化操作。在另一个 Activity 中需要进行反序列化操作讲数据取出)
(4).Java 平台允许我们在内存中创建可复用的 Java 对象,但一般情况下,只有当 JVM 处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比 JVM 的生命周期更长(即每个对象都在 JVM 中)但在现实应用中,就可能要停止 JVM 运行,但有要保存某些指定的对象,并在将来重新读取被保存的对象。这是 Java 对象序列化就能够实现该功能。(可选择入数据库、或文件的形式保存)
(5). 序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化.
(6). 在 Intent 之间,基本的数据类型直接进行相关传递即可,但是一旦数据类型比较复杂的时候,就需要进行序列化操作了.
2.Android 中实现序列化的两种方式
(1).Implements Serializable 接口 (声明一下即可)
Serializable 的简单实例:
public class Person implements Serializable{ private static final long serialVersionUID = -7060210544600464481L; private String name; private int age; public String getName(){ return name; } public void setName(String name){ this.name = name; } public int getAge(){ return age; } public void setAge(int age){ this.age = age; } }
(2).Implements Parcelable 接口 (不仅仅需要声明,还需要实现内部的相应方法)
Parcelable 的简单实例:
注:写入数据的顺序和读出数据的顺序必须是相同的.
public class Book implements Parcelable{ private String bookName; private String author; private int publishDate; public Book(){ } public String getBookName(){ return bookName; } public void setBookName(String bookName){ this.bookName = bookName; } public String getAuthor(){ return author; } public void setAuthor(String author){ this.author = author; } public int getPublishDate(){ return publishDate; } public void setPublishDate(int publishDate){ this.publishDate = publishDate; } @Override public int describeContents(){ return 0; } @Override public void writeToParcel(Parcel out, int flags){ out.writeString(bookName); out.writeString(author); out.writeInt(publishDate); } public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){ @Override public Book[] newArray(int size){ return new Book[size]; } @Override public Book createFromParcel(Parcel in){ return new Book(in); } }; public Book(Parcel in){ //如果元素数据是list类型的时候需要: lits = new ArrayList<?> in.readList(list); //否则会出现空指针异常.并且读出和写入的数据类型必须相同.如果不想对部分关键字进行序列化,可以使用transient关键字来修饰以及static修饰. bookName = in.readString(); author = in.readString(); publishDate = in.readInt(); } }
我们知道在 Java 应用程序当中对类进行序列化操作只需要实现 Serializable 接口就可以,由系统来完成序列化和反序列化操作,但是在 Android 中序列化操作有另外一种方式来完成,那就是实现 Parcelable 接口。也是 Android 中特有的接口来实现类的序列化操作。原因是 Parcelable 的性能要强于 Serializable. 因此在绝大多数的情况下,Android 还是推荐使用 Parcelable 来完成对类的序列化操作的.
3.Parcelable 与 Serializable 的性能比较
首先 Parcelable 的性能要强于 Serializable 的原因我需要简单的阐述一下
1). 在内存的使用中,前者在性能方面要强于后者
2). 后者在序列化操作的时候会产生大量的临时变量,(原因是使用了反射机制) 从而导致 GC 的频繁调用,因此在性能上会稍微逊色
3). Parcelable 是以 Ibinder 作为信息载体的。在内存上的开销比较小,因此在内存之间进行数据传递的时候,Android 推荐使用 Parcelable, 既然是内存方面比价有优势,那么自然就要优先选择.
4). 在读写数据的时候,Parcelable 是在内存中直接进行读写,而 Serializable 是通过使用 IO 流的形式将数据读写入在硬盘上.
但是:虽然 Parcelable 的性能要强于 Serializable, 但是仍然有特殊的情况需要使用 Serializable, 而不去使用 Parcelable, 因为 Parcelable 无法将数据进行持久化,因此在将数据保存在磁盘的时候,仍然需要使用后者,因为前者无法很好的将数据进行持久化.(原因是在不同的 Android 版本当中,Parcelable 可能会不同,因此数据的持久化方面仍然是使用 Serializable)
速度测试:
测试方法:
1)、通过将一个对象放到一个 bundle 里面然后调用 Bundle#writeToParcel (Parcel, int) 方法来模拟传递对象给一个 activity 的过程,然后再把这个对象取出来。
2)、在一个循环里面运行 1000 次。
3)、两种方法分别运行 10 次来减少内存整理,cpu 被其他应用占用等情况的干扰。
4)、参与测试的对象就是上面的相关代码
5)、在多种 Android 软硬件环境上进行测试
- LG Nexus 4 – Android 4.2.2
- Samsung Nexus 10 – Android 4.2.2
- HTC Desire Z – Android 2.3.3
结果如图:
性能差异:
Nexus 10
Serializable: 1.0004ms, Parcelable: 0.0850ms – 提升 10.16 倍。
Nexus 4
Serializable: 1.8539ms – Parcelable: 0.1824ms – 提升 11.80 倍。
Desire Z
Serializable: 5.1224ms – Parcelable: 0.2938ms – 提升 17.36 倍。
由此可以得出: Parcelable 比 Serializable 快了 10 多倍。
从相对的比较我们可以看出,Parcelable 的性能要比 Serializable 要优秀的多,因此在 Android 中进行序列化操作的时候,我们需要尽可能的选择前者,需要花上大量的时间去实现 Parcelable 接口中的内部方法.
4.Android 中如何使用 Parcelable 进行序列化操作
说了这么多,我们还是来看看 Android 中如何去使用 Parcelable 实现类的序列化操作吧.
Implements Parcelable 的时候需要实现内部的方法:
1).writeToParcel 将对象数据序列化成一个 Parcel 对象 (序列化之后成为 Parcel 对象。以便 Parcel 容器取出数据)
2). 重写 describeContents 方法,默认值为 0
3).Public static final Parcelable.Creator<T>CREATOR (将 Parcel 容器中的数据转换成对象数据) 同时需要实现两个方法:
3.1 CreateFromParcel (从 Parcel 容器中取出数据并进行转换.)
3.2 newArray (int size) 返回对象数据的大小
因此,很明显实现 Parcelable 并不容易。实现 Parcelable 接口需要写大量的模板代码,这使得对象代码变得难以阅读和维护。具体的实例就是上面 Parcelable 的实例代码。就不进行列举了.(有兴趣的可以去看看 Android 中 NetWorkInfo 的源代码,是关于网络连接额外信息的一个相关类,内部就实现了序列化操作。大家可以去看看)
5.Parcelable 的工作原理
无论是对数据的读还是写都需要使用 Parcel 作为中间层将数据进行传递.Parcel 涉及到的东西就是与 C++ 底层有关了。都是使用 JNI. 在 Java 应用层是先创建 Parcel (Java) 对象,然后再调用相关的读写操作的时候。就拿读写 32 为 Int 数据来说吧:
static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz){ Parcel* parcel = parcelForJavaObject(env, clazz); if (parcel != NULL) { return parcel->readInt32(); } return 0; }
调用的方法就是这个过程,首先是将 Parcel (Java) 对象转换成 Parcel (C++) 对象,然后被封装在 Parcel 中的相关数据由 C++ 底层来完成数据的序列化操作.
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(sizeof(T)) == sizeof(T)); if ((mDataPos+sizeof(val)) <= mDataCapacity) { restart_write: *reinterpret_cast<t*>(mData+mDataPos) = val; return finishWrite(sizeof(val)); } status_t err = growData(sizeof(val)); if (err == NO_ERROR) goto restart_write; return err; } 真正的读写过程是由下面的源代码来完成的. status_t Parcel::continueWrite(size_t desired) { // If shrinking, first adjust for any objects that appear // after the new data size. size_t objectsSize = mObjectsSize; if (desired < mDataSize) { if (desired == 0) { objectsSize = 0; } else { while (objectsSize > 0) { if (mObjects[objectsSize-1] < desired) break; objectsSize--; } } } if (mOwner) { // If the size is going to zero, just release the owner''s data. if (desired == 0) { freeData(); return NO_ERROR; } // If there is a different owner, we need to take // posession. uint8_t* data = (uint8_t*)malloc(desired); if (!data) { mError = NO_MEMORY; return NO_MEMORY; } size_t* objects = NULL; if (objectsSize) { objects = (size_t*)malloc(objectsSize*sizeof(size_t)); if (!objects) { mError = NO_MEMORY; return NO_MEMORY; } // Little hack to only acquire references on objects // we will be keeping. size_t oldObjectsSize = mObjectsSize; mObjectsSize = objectsSize; acquireObjects(); mObjectsSize = oldObjectsSize; } if (mData) { memcpy(data, mData, mDataSize < desired ? mDataSize : desired); } if (objects && mObjects) { memcpy(objects, mObjects, objectsSize*sizeof(size_t)); } //ALOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); mOwner = NULL; mData = data; mObjects = objects; mDataSize = (mDataSize < desired) ? mDataSize : desired; ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); mDataCapacity = desired; mObjectsSize = mObjectsCapacity = objectsSize; mNextObjectHint = 0; } else if (mData) { if (objectsSize < mObjectsSize) { // Need to release refs on any objects we are dropping. const sp<ProcessState> proc(ProcessState::self()); for (size_t i=objectsSize; i<mObjectsSize; i++) { const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]); if (flat->type == BINDER_TYPE_FD) { // will need to rescan because we may have lopped off the only FDs mFdsKnown = false; } release_object(proc, *flat, this); } size_t* objects = (size_t*)realloc(mObjects, objectsSize*sizeof(size_t)); if (objects) { mObjects = objects; } mObjectsSize = objectsSize; mNextObjectHint = 0; } // We own the data, so we can just do a realloc(). if (desired > mDataCapacity) { uint8_t* data = (uint8_t*)realloc(mData, desired); if (data) { mData = data; mDataCapacity = desired; } else if (desired > mDataCapacity) { mError = NO_MEMORY; return NO_MEMORY; } } else { if (mDataSize > desired) { mDataSize = desired; ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); } if (mDataPos > desired) { mDataPos = desired; ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); } } } else { // This is the first data. Easy! uint8_t* data = (uint8_t*)malloc(desired); if (!data) { mError = NO_MEMORY; return NO_MEMORY; } if(!(mDataCapacity == 0 && mObjects == NULL && mObjectsCapacity == 0)) { ALOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired); } mData = data; mDataSize = mDataPos = 0; ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); mDataCapacity = desired; } return NO_ERROR; }
1). 整个读写全是在内存中进行,主要是通过 malloc ()、realloc ()、memcpy () 等内存操作进行,所以效率比 JAVA 序列化中使用外部存储器会高很多
2). 读写时是 4 字节对齐的,可以看到 #define PAD_SIZE (s) (((s)+3)&~3) 这句宏定义就是在做这件事情
3). 如果预分配的空间不够时 newSize = ((mDataSize+len)*3)/2; 会一次多分配 50%
4). 对于普通数据,使用的是 mData 内存地址,对于 IBinder 类型的数据以及 FileDescriptor 使用的是 mObjects 内存地址。后者是通过 flatten_binder () 和 unflatten_binder () 实现的,目的是反序列化时读出的对象就是原对象而不用重新 new 一个新对象。
6. 相关实例
最后上一个例子..
首先是序列化的类 Book.class
public class Book implements Parcelable{ private String bookName; private String author; private int publishDate; public Book(){ } public String getBookName(){ return bookName; } public void setBookName(String bookName){ this.bookName = bookName; } public String getAuthor(){ return author; } public void setAuthor(String author){ this.author = author; } public int getPublishDate(){ return publishDate; } public void setPublishDate(int publishDate){ this.publishDate = publishDate; } @Override public int describeContents(){ return 0; } @Override public void writeToParcel(Parcel out, int flags){ out.writeString(bookName); out.writeString(author); out.writeInt(publishDate); } public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){ @Override public Book[] newArray(int size){ return new Book[size]; } @Override public Book createFromParcel(Parcel in){ return new Book(in); } }; public Book(Parcel in){ //如果元素数据是list类型的时候需要: lits = new ArrayList<?> in.readList(list); 否则会出现空指针异常.并且读出和写入的数据类型必须相同.如果不想对部分关键字进行序列化,可以使用transient关键字来修饰以及static修饰. bookName = in.readString(); author = in.readString(); publishDate = in.readInt(); } }
第一个 Activity,MainActivity
Book book = new Book(); book.setBookname("Darker"); book.setBookauthor("me"); book.setPublishDate(20); Bundle bundle = new Bundle(); bundle.putParcelable("book", book); Intent intent = new Intent(MainActivity.this,AnotherActivity.class); intent.putExtras(bundle);
第二个 Activity,AnotherActivity
Intent intent = getIntent(); Bundle bun = intent.getExtras(); Book book = bun.getParcelable("book"); System.out.println(book);
总结:Java 应用程序中有 Serializable 来实现序列化操作,Android 中有 Parcelable 来实现序列化操作,相关的性能也作出了比较,因此在 Android 中除了对数据持久化的时候需要使用到 Serializable 来实现序列化操作,其他的时候我们仍然需要使用 Parcelable 来实现序列化操作,因为在 Android 中效率并不是最重要的,而是内存,通过比较 Parcelable 在效率和内存上都要优秀与 Serializable, 尽管 Parcelable 实现起来比较复杂,但是如果我们想要成为一名优秀的 Android 软件工程师,那么我们就需要勤快一些去实现 Parcelable, 而不是偷懒与实现 Serializable. 当然实现后者也不是不行,关键在于我们头脑中的那一份思想。
今天关于Android:如何对我的对象实施Parcelable?和android 对象池的分享就到这里,希望大家有所收获,若想了解更多关于(android) Parcelable 接口的使用、Android Activity 传递Parcelable对象、Android Parcelable、Android Parcelable 和 Serializable 的区别等相关知识,可以在本站进行查询。
本文标签: