您的位置:首页 > 移动开发 > Android开发

android-----IPC机制学习总结系列(一)

2016-07-06 20:23 447 查看
        接下来开始了IPC机制的学习,自我感觉这是android中较难的一部分了,打算好好做个学习总结;

        首先为什么会出现IPC机制,因为不同进程之间是不共享内存的,那么他们之间就不能通过直接为对象设置属性或者获取属性来共享对象了,所以也就出现了序列化与反序列化过程了,这也是学习IPC机制的基础,说白了IPC是为了进程间通信的,那么通信就意味着进程间会进行数据交互,也就是假如进程1想要使用进程2中某个对象的某个属性值做些操作,这时候进程2无法直接将对象传递给进程1,那他会先序列化将对象以及对象属性、属性值固化下来,而进程1就可以通过反序列化的过程获得对象以及对象中的属性值;

        java中提供的序列化接口是:Serializable

        android新增了序列化接口: Parcelable

        我们先从官方文档看看各自怎么用,接着给出使用实例,最后来比较两者的区别:

        先来看Serializable:

* Marks classes that can be serialized by ObjectOutputStream and deserialized by ObjectInputStream.

* You can take control of your serialized form by implementing these two
* methods with these exact signatures in your serializable classes:
*
*   private void writeObject(java.io.ObjectOutputStream out)
*       throws IOException {
*     // write 'this' to 'out'...
*   }
*
*   private void readObject(java.io.ObjectInputStream in)
*       throws IOException, ClassNotFoundException {
*     // populate the fields of 'this' from the data in 'in'...
*   }
*
public interface Serializable {

}
        从官方注释中可以看出来,Serializable是空接口,我们想要对某一个类进行序列化操作时需要实现这个接口,可以通过ObjectOutputStream以及ObjectInputStream对象的writeObject以及readObject来进行序列化与反序列化,同时,如果我们想要控制序列化形式的话,也可以通过在需要序列化的类中覆写writeObject来实现,当然ObjectInputStream中的反序列化方法readObject也要进行重写并且必须要和writeObject的顺序一致,这点可以用到保存数据的安全领域,比如只有你知道序列化的顺序,那当然也只有你知道该怎么反序列化了;

        下面用实例说明下Serializable的使用:

        首先定义一个可序列化的类Student.java:

public class Student implements Serializable{
private static final long serialVersionUID = -8281321707609748022L;
/**
*
*/
public String name;
public int age;
public String address;
public Student() {
}

public Student(String name,int age,String address)
{
this.name = name;
this.age = age;
this.address = address;
}

private void writeObject(ObjectOutputStream oos)throws IOException
{
oos.defaultWriteObject();
//oos.writeInt(age);
}

private void readObject(ObjectInputStream in)
{
try {
in.defaultReadObject();
//age = in.readInt();
}catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
        我这里重写了writeObject和readObject方法,并且在里面调用的是defaultWriteObject和defaultReadObject,也就是默认情况的序列化,即序列化除static以及transient修饰的属性值;

        接着定义一个序列化测试类,封装序列化与反序列化方法:

public class SerializableTest {
/**
* 序列化
* @param student
*/
public static void toSerializable(Student student,String path)
{
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File(path)));
objectOutputStream.writeObject(student);
objectOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 反序列化
* @param path
* @return
*/
public static Student fromSerializable(String path)
{
Student student = null;
ObjectInputStream objectInputStream;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(new File(path)));
student = (Student) objectInputStream.readObject();
objectInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return student;
}

public static void main(String[] args) {
Student student = new Student("huzhiwei",24,"shanxi");
Student result = null;
toSerializable(student, "e:/test.txt");
result = fromSerializable("e:/test.txt");
System.out.println("name:  "+result.name);
System.out.println("age:  "+result.age);
System.out.println("address:  "+result.address);
System.out.println("student==result:  "+(student==result));
}
}

        通过第9--11行实现序列化过程,第29--31行实现反序列化过程,随后我们查看输出:

name:  huzhiwei
age:  24
address:  shanxi
student==result:  false
        可以看到,我们通过反序列化的方式得到了序列化对象的属性值,这里的值包括了name、age、address;

        如果我们把age前面添加transient关键字,继续查看输出:

name:  huzhiwei
age:  0
address:  shanxi
student==result:  false
        可以发现此时age的值变成了0,说明transient关键字修饰的变量不会进行序列化;

        那么我们把name前面添加关键字static,查看输出情况:

name:  huzhiwei
age:  24
address:  shanxi
student==result:  false


        咦?这和我在别的地方看到的说明不一样啊,不是说static变量属于类,不参与序列化过程么?是的,you are right,但是这里name为什么还是有值呢?原因在于你的序列化和反序列化是在同一个main函数中进行的,也就是说你在同一个机器(而且是同一个进程),因为你的jvm已经把name加载进来了,所以能够获取到name的值,而序列化一般的应用场景是网络或者远程调用,假如你在另一台机子上进行反序列化肯定不会有name值,同样你也可以重新建一个类,在另一个类的main方法中进行反序列化来进行测试,结果如下:

name:  null
age:  24
address:  shanxi
student==result:  false


        这下子正确了吧!

        接下来,我们把Student类中的21和28行注释掉,打开22和29行的注释,查看输出:

name:  null
age:  24
address:  null
student==result:  false
        你会发现只有age是有值的,因为我们进行了定制化的序列化,也就是我们刚刚打开注释的那部分了;

        如果你仔细点的话,会发现上面所有输出的最后一行都是student==result:false,这句输出验证了反序列化之后生成的对象和原来的对象不是一个对象,那么这点就违背了我们的单例模式,这个问题怎么解决呢?解决方法挺简单,就是在Student类中添加一个readResolve方法,具体大家可以查看下别的资料;

        接下来就是Parcelable接口了:

        同样我们先来看下官方文档的说明:

/**
* Interface for classes whose instances can be written to
* and restored from a {@link Parcel}.  Classes implementing the Parcelable
* interface must also have a static field called <code>CREATOR</code>, which
* is an object implementing the {@link Parcelable.Creator Parcelable.Creator}
* interface.
*
* A typical implementation of Parcelable is:
*
*
* 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();
*     }
* }
*/
public interface Parcelable {
}

        从注释看出实现Parcelable接口的类必须要有一个名字为CREATOR的实现了Parcelable.Creator接口的静态变量,同时官方文档还给了我们一个实例,先来说说里面每个方法的作用,接着通过实例来应用一下:

        这里面出现的Parcel接口是用于包装可序列化数据的,writeToParcel用于通过一系列的write方法进行序列化,反序列化的工作由

CREATOR来完成,通过createFromParcel创建序列化对象,通过newArray创建序列化数组,而创建序列化对象会调用他的有参构造函数,在这个构造函数中通过一系列的read方法读取到序列化的内容;describeContents返回当前对象的内容描述,一般情况下我们都是返回0;还有一点需要注意,就是如果我们序列化对象内部存在另一个可序列化对象的时候,他的反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到类的错误;

        我们通过实例来学习下Parcelable进行序列化:

        首先我们先定义一个实现Parcelable接口的Student类:

public class Student implements Parcelable{

public String name;
public int age;
public Course course;

public static final Parcelable.Creator<Student> CREATOR = new Creator<Student>() {

@Override
public Student[] newArray(int size) {
return new Student[size];
}

@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
};

private Student(Parcel in)
{
name = in.readString();
age = in.readInt();
course = in.readParcelable(Thread.currentThread().getContextClassLoader());
}

public Student(String name,int age,Course course)
{
this.name = name;
this.age = age;
this.course = course;
}
@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeInt(age);
out.writeParcelable(course, 0);
}
}
        可以看到在这个类中用到了可序列化对象Course,所以Course也必须实现Parcelable接口:

public class Course implements Parcelable {
public String courseName;
public String teacherName;

public static final Parcelable.Creator<Course> CREATOR = new Parcelable.Creator<Course>() {

@Override
public Course[] newArray(int size) {
return new Course[size];
}

@Override
public Course createFromParcel(Parcel in) {
return new Course(in);
}
};

private Course(Parcel in)
{
courseName = in.readString();
teacherName = in.readString();
}

public Course(String courseName,String teacherName)
{
this.courseName = courseName;
this.teacherName = teacherName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(courseName);
out.writeString(teacherName);
}
}

        上面两个类分别实现了Parcelable接口,因为Student使用了Course,在他里面使用Course对象和使用普通对象有点区别,在writeToParcel中通过writeParcelable序列化Course,接着通过Parcel对象的readParcelable方法获取到序列化之后的Course对象,传入的参数是当前线程的上下文类加载器;

        接着我们定义了两个Activity,MainActivity里面设置按钮,点击之后跳转到AnotherActivity,并且通过Intent传递一个Student对象过去,在AnotherActivity里面获取到该Student对象的属性值,显示在TextView上面,代码比较简单;

        MainActivity.java

public class MainActivity extends Activity {

public Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button)findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View arg0) {
Intent intent = new Intent(MainActivity.this, AnotherActivity.class);
Course course = new Course("zhangsan","数学");
Student student = new Student("lisi",24,course);
intent.putExtra("student",student);
startActivity(intent);
}
});
}
}
        AnotherActivity.java

public class AnotherActivity extends Activity{
public TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_another);
mTextView = (TextView)findViewById(R.id.textView);
Student student = getIntent().getParcelableExtra("student");
mTextView.setText("name:  "+student.name+"\n"+"age:  "+student.age+"\n"+"courseName:  "+student.course.courseName+"\n"+"teacherName:  "+student.course.teacherName);
}
}
        至此,关于Serializable与Parcelable的实例介绍结束了;
        在此我们总结一下:

        (1):使用Serializable或者Parcelable只是序列化对象的属性值,并不能序列化方法;

        (2):父类实现了Serializable或者Parcelable接口,那么子类不实现Serializable或者Parcelable接口也是可以序列化的,当然有个前提条件是子类中所引用到的对象必须是可以序列化的;

        (3):Serializable适用于网络传输或者硬盘固化场景的序列化,Parcelable适用于内存序列化;

    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息