最近在拜读任主席的Android开发艺术探索,现在看了一半,再回头看前面的,感觉跟没有看一样,所以还是把知识点总结一下吧,这一节咱们来讲一下IPC,即进程间通信
开启多进程模式
在Android中使用多进程只有一种方法:给四大组件(Activity、Service、Receiver、ContentProvider)在AndroidManifest中指定android:process属性,除此之外没有其他办法(除去通过JNI在native层去fork):
- android:process=”:remote”,这种方式的标记的进程名为包名:remote,“:“的含义是指要在当前的进程名前附加包名,其次,进程名以“:”开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。
- android:process=”com.example.wpp.remote”,这种声明方式是完整的命名方式,不会附件包名信息,属于全局进程,其他应用通过ShareUID方式可以和它跑在同一个进程中
Android系统会为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据,而两个应用通过ShareUID跑在同一个进程需要这两个应用有相同的ShareUID并且签名相同才可以,在这种情况下,它们可以互相 访问对方的私有数据,比如data目录、组件信息等,不管它们是否泡在同一个进程中。当然如果它们跑在同一个进程中,那么除了能共享data目录、组件信息,还可以共享内存数据
使用多进程会造成如下几方面的问题:
1. 静态成员和单例模式完全失效
2. 线程同步机制完全失效。
3. SharedPreferences的可靠性下降
4. Application会多次创建。
IPC中的一些基本概念
本节主要介绍IPC中的一些基本概念:Serializable接口、Parcelable接口以及Binder
Serializable接口
Serializable是java所提供的一个序列化接口, 是一个空接口,为对象提供标准的序列化和反序列化操作,使用时需要声明实现Serializable接口,并指定一个类似下面的标识即可自动实现默认的序列化过程。
private static final long serialVersionUID = 8711368828010083048L
这个serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够正常的被反序列化。反序列化的时候会从待反序列的文件中读取这个值和目标类中的值比对,如果不一致就会反序列化失败。这个值可以手动指定也可以让IDE通过当前类的结构自动去生成它的hash值,如果通过IDE自动生成,反序列化时当前类有所改变,那么系统会重新计算serialVersionUID的值,于是反序列化失败。当我们手动指定了它以后,可以在很大程度上避免反序列化过程的失败。
注意:如果类结构发生了非常规性改变,比如修改了类名,修改成员变量类型,尽管serialVersionUID相同,此时反序列化还是会失败,程序crash
另外,系统的默认序列化过程也是可以改变的,通过重写系统writeObject和readObject方法即可。
Parcelable接口
Parcelable接口是Android提供的新的序列化方式,Parcelable也是一个接口,下面的示例是一个典型的用法。
public class User implements Parcelable{
public int userId;
public String userName;
public boolean isMale;
public Book book;
public User(int userId, String userName, boolean isMale){
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
public int describeContents(){
return 0;
}
public void writeToParcel(Parcel out, int flags){
out.writeInt(userId);
out.wirteString(userName);
out.wirteInt(isMale ? 1 : 0);
out.writeParcelable(book, 0);
}
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>(){
public User createFromParcel(Parcel in){
return new User(in);
}
public User[] new Array(int size){
return new User[size];
}
};
private User(Parcel in){
userId = in.readInt();
userName = in.readString();
isMale = in.readInt()==1;
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
}
Parcelable方法说明
方法 | 功能 | 标记位 |
---|---|---|
createFromParcel(Parcel in) | 从序列化后的对象中创建原对象 | |
new Array(int size) | 创建指定长度的原始对象数组 | |
User(Parcel in) | 从序列化后的对象中创建原始对象 | |
writeToParcel(Parcel out, int flag) | 将当前对象写入序列化结构中,其中flag标识有两种值:0或者1.为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0 | PARCELABLE_WRITE_RETURN_VALUE |
describeContents | 返回当前对象的内容描述,如果还有文件描述符,返回1,否则返回0,几乎所有情况都返回0 |
系统已经为我们提供了许多实现了Parcelable接口的类,它们都是可以直接序列化的,比如Intent、Bundle、Bitmap等,同时List和Map也可以序列化,前提是它们里面每个元素都是可序列化的。
Parcelable和Serializable区别:
Serializable是java中的序列化接口,其使用起来简单但是开销大,序列化和反序列化需要大量I/O操作。而Parcelable是Android中的序列化方式,因此更适合用在Android平台上,缺点就是用起来稍微麻烦点,但是它的效率高,这是Android推荐的序列化方式。