通过给四大组件指定android:process属性,我们可以开启多线程模式
一般来说,使用多进程会造成如下几个方面的问题:
不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁的不是同一个对象
SharedPreferences不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失,这时因为SharedPreferences底层是通过读写XML文件来实现的,并发写显然是可能出问题的,甚至并发读写都有可能发生问题
运行在同一个进程中的组件是属于同一个虚拟机和同一个Application的。同理,运行在不同进程中的组件是属于两个不同的虚拟机和Application的。
是Java所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作。使用Serializable来实现序列化相当简单,只需要在类的声明中指定一个类似下面的标识即可自动实现默认的序列化过程。
private static final long serialVersionUID = 8711368828010083044L
通过Serializable方来实现对象的序列化,如下代码:
//序列化过程
User user = new User(0, "jake", true);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();
//反序列化过程
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User newUser = (User)in.readObject();
in.close();
原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同时才能够正常的被反序列化。serialVersionUID的详细工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中(也可能是其他的中介),当反序列化的时候系统会去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化,否则就说明当前类和序列化的类相比发生了某些变换
给serialVersionUID制定为1L或者采用Eclipse根据当前类结构去生成的hash值,这两者并没有本质区别。
Parcelable也是一个接口,只要实现这个接口,一个类的对象就可以实现序列化并可以通过Intent的Binder传递
Parcelable的方法说明:
方法 | 功能 | 标记位 |
---|---|---|
createFromParcel(Parcel in) | 从序列化的对象中创建原始对象 | |
newArray[int size] | 创建指定长度的原始对象数组 | |
User(Parcel in) | 从序列化的对象中创建原始对象 | |
write ToParcel(Parcel out, int flags) | 将当前对象写入序列化结构中,其中flags标识有两种值0或1(参见右侧标记位)。为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0 | PARCELABLE_WRITE_RETURN_VALUE |
describeContents | 返回当前对象的内容描述。如果含有文件描述符,返回1(参见右侧标记位),否则返回0,几乎所有的情况都返回0 | CONTENTS_FILE_DESCRIPTOR |
Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化需要大量I/O操作。而Parceleble是Android中的序列化方式,因此更适合在Android平台上,缺点是麻烦,但是效率高,这是Android推荐的序列化方式,所以我们要首选Parcelable。Parcelable主要用在内存序列化上,通过Parcelable将对象序列化到存储设备中或者将对象序列化之后通过网络传输,但是过程稍显复杂,因此在这两种情况下建议大家使用Serializable。
aidl工具根据aidl文件自动生成的java接口的解析:首先,它声明了几个接口方法,同时还声明了几个整型的id用于标识这些方法,id用于标识在transact过程中客户端所请求的到底是哪个方法;接着,它声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,而当两者位于不同进程时,方法调用需要走transact过程,这个逻辑由Stub内部的代理类Proxy来完成。
所以,这个接口的核心就是它的内部类Stub和Stub内部的代理类Proxy。 下面分析其中的方法:
1、声明一个DeathRecipient对象、DeathRecipient是一个接口,其内部只有一个方法bindDied,实现这个方法就可以在Binder死亡的时候收到通知了。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mRemoteBookManager == null) return;
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
// TODO:这里重新绑定远程Service
}
};
2、在客户端绑定远程服务成功之后,给binder设置死亡代理
mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
1、 使用Bundle
Bundle实现了Parcelable接口,Activity、Service和Receiver都支持在Intent中传递Bundle数据
2、 使用文件共享
这种方式简单,适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读写的问题,SharedPreferences是一个特例,虽然它也是文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式下、系统对它的读写就变的不可靠,当面对高并发读写访问的时候,有很大几率会丢失,因此,不建议在进程间通信中使用SharedPreferences。
3、 使用Messenger
Messenger是一种轻量级的IPC方案,它的底层实现就是AIDL。Messenger是以串行的方式处理请求的,即服务端只能一个个处理,不存在并发执行的情形。
4、 使用AIDL
大致流程:首先建一个Service和一个AIDL接口,接着创建一个类继承自AIDL接口中的Stub类中的抽象方法,在Service的onBind方法中返回这个类的对象,然后客户端就可以绑定服务端Service,建立连接后就可以访问远程服务端的方法了。
1.AIDL支持的数据类型:基本数据类型、String和CharSequence、ArrayList、HashMap、Parcelable以及AIDL;
2.某些类即使和AIDL文件在同一个包中也要显式import进来;
3.AIDL中除了基本数据类,其他类型的参数都要标上方向:in、out或者inout;
4.AIDL接口中支持方法,不支持声明静态变量;
5.为了方便AIDL的开发,建议把所有和AIDL相关的类和文件全部放入同一个包中,这样做的好处是,当客户端是另一个应用的时候,可以直接把整个包复制到客户端工程中。
6.RemoteCallbackList是系统专门提供的用于删除跨进程Listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口,因为所有的AIDL接口都继承自IInterface接口。
5、使用ContentProvider
1.ContentProvider主要以表格的形式来组织数据,并且可以包含多个表;
2.ContentProvider还支持文件数据,比如图片、视频等,系统提供的MediaStore就是文件类型的ContentProvider;
3.ContentProvider对底层的数据存储方式没有任何要求,可以是SQLite、文件,甚至是内存中的一个对象都行;
4.要观察ContentProvider中的数据变化情况,可以通过ContentResolver的registerContentObserver方法来注册观察者;
6、使用Socket
套接字,分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层中TCP和UDP协议。