IPC是Inter-Process Communication的缩写,含义为进程间通信或跨进程通信,是指两个进城之间进行数据交换的过程.
IPC方式的优缺点和适用场景
名称 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Bundle | 简单易用 | 只能传输Bundle支持的数据类型 | 四大组件之间的通信 |
文件共享 | 简单易用 | 不适合高并发场景,并且无法做到进程间的即使通信 | 无并发访问情形,交换简单的数据实时性不高的场景 |
AIDL | 功能强大,支持一对多并发通信,支持实时通信 | 使用稍复杂,需要处理好线程同步 | 一对多通信且有RPC需求 |
Messenger | 功能一般,支持一对多串行通信,支持实时通信 | 不能很好处理高并发情形,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型 | 低并发的一对多即时通信,无RPC需求,或者无需要返回结果的RPC需求 |
ContentProvider | 在数据源访问页面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作 | 可以理解为受约束的AIDL,只要提供数据源的CRUD操作 | 一对多进程间的数据共享 |
Socket | 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 | 实现细节稍微有点繁琐,不支持直接的RPC | 网络数据交换 |
开启多进程
<activity
android:name=".FirstActivity"
android:process=":remote"
/>
<activity
android:name=".SecondActivity"
android:process="com.test.bt.remote"
/>
AndroidManifest
中添加process属性即可在activity启动时新建一个进程并且运行在新进程中,process的命名有2种方式:
- 以
:
为前缀命名的activity启动的进程默认前面会加上包名,即FirstActivity
会启动在包名:remote
的进程中.此进程为本应用的私有进程,即其他应用的组件无法运行在该进程内. SecondActivity
的com.test.bt.remote
的完整的命名方式,不会附加包名信息.
对象序列化
Intent和Binder传输数据时需要序列化数据,然后将获取的数据反序列化,通过Serilizable接口和Parcelable接口可以完成序列化和反序列化
Serializable接口
public class User implements Serializable{
private static final long serialVersionUID = 11242141L;
public String name;
public int age;
...
}
所有操作系统自动完成,只需加上serialVersionUID
注:务必加上serialVersionUID ,可手动指定或系统生成,作为序列化和反序列化的识别码.一般情况下不加serialVersionUID 也可序列化成功,但是当类的结构发生改变,或者未来升级过程中新增删除了某些成员变量,反序列化仍然可以成功,可最大限度方式数据丢失.
Parcelable接口
public class User implements Parcelable{
public String name;
public int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
/*返回当前对象的内容描述
* 当前对象中存在对象描述符时返回1
* 其余情况均返回0
*/
@Override
public int describeContents() {
return 0;
}
//序列化
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
//反序列化
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>(){
//从序列化的对象中还原原始对象
@Override
public User createFromParcel(Parcel source) {
return new User(source);
}
//创建指定长度的原始对象数组
@Override
public User[] newArray(int size) {
return new User[size];
}
};
private User(Parcel in){
name = in.readString();
age = in.readInt();
}
}
主要序列化和反序列化在实现了Parcelable接口的类内实现
Parcelable和Serializable对比
Serializable是java中的序列化接口简单易用,但是在序列化和反序列化过程中需要大量I/O操作,开销很大
Parcelable是Android中的序列化接口,使用时的效率很高,但是缺点是使用起来较为麻烦.
推荐在进行文件化存储和网络传输使用Serializable,而在内存序列化上使用Parcelable
Binder
Binder是Android中的一个类,实现了IBinder的接口.
从IPC角度来说Binder是Android中的一种跨进程方式
AIDL
使用AIDL分析Binder的工作机制
User.java
文件使用上文中的代码
//User.aidl
package com.test.bt.aidl
parcelable User;
//UserManager.aidl
package com.test.bt.aidl
import com.test.bt.aidl.User;
interface UserManager{
User getUser(in int position);
}
需要在User.aidl中声明User类.
UserManager.aidl
是一个自定义的接口,尽管和User.aidl
在同一个包内,但是仍需要导入
创建AIDL方法后会在gen目录下自动生成了UserManager.java
文件,太长就不贴出来
核心方法 | 作用 |
---|---|
Stub | 内部类,其内部决定了请求是走代理还是直接返回自身的Binder对象 |
asInterface(IBinder obj) | 用于将服务端的Binder对象转化为客户端的Binder对象,如果客户端服务端处于同一个进程,返回的是服务端对象本身,否则根据proxy代理返回封装后的Stub.proxy |
asBinder | 返回当前Binder对象 |
onTransact | 根据请求的code得知请求的方法是什么,并根据传入参数进行操作并设定reply返回值,如果返回值为false则代表请求失败,可以以此做权限验证 |
Proxy | 此方法运行于客户端,创建需要的输入型对象_data ,输出型对象_reply 和返回值对象List ,然后把参数信息写入_data ,然后调用transact方法发送RPC请求,然后当前线程挂起,服务端调用onTransact方法,直到RPC返回数据,线程继续执行,并且从_relpy方法中取出数据 |
注:不要在UI线程中去进行请求
服务端
public class UserService extends Service{
private List<User> mUsers = new ArrayList<User>();
//实现UserManager.Stub()中的方法
private Binder mBinder = new UserManager.Stub(){
@Override
public User getUser(int position) throws RemoteException{
return mUsers.get(position);
}
}
//将Binder返回给客户端
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
...
}
客户端
//监听是否连上service
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
UserManager userManager = UserManager.Stub.asInterface(service);
User user = userManager.getUser(2);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
//绑定服务并添加监听
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, UserManagerService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
...
}
注:onServiceConnected和onServiceDisconnected是运行在UI线程中,不要在这两个方法中运行耗时方法
AIDL中常用权限验证方法
- 在onBind中验证
- 验证不通过直接返回null,可以通过设定permission,Uid,Pid或者其他参数传值等方式进行判断.
- 在onTransact中验证
- 不通过直接返回false.方法同上
Android的跨进程的通信方式
- Bundle
- 传递的数据必须能够被序列化,Bundle本身也实现了Parcelable接口
- 文件共享
- SharedPreferences是个特例,系统对于SharedPreferences的读写有一定的缓存策略,在多进程模式下,系统对其的读写会变得不可靠,很大几率会丢失数据,所以不推荐在多进程中使用SharedPreferences.
- Messenger
- 底层还是AIDL,轻量级的IPC方案,用于传递Message对象
- AIDL
- 前面已经研究了AIDL的基本使用,较为常用的跨进程通讯方式
- ContentProvider
- 底层实现为Binder,提供不同应用实践的数据共享的方式
- Socket
- 网络通信的方式,不仅可以实现进程间的通信,还可以进行设备之间的通信
本文摘自 Android开发艺术探索 一书,并进行整理