开启多进程模式
在Android中开启多进程只有一种方法,就是给四大组件(Activity、Service、Receiver、ContentProvider)在AndroidMenifest中指定android:process
属性。
序列化
通过Intent和Binder传数据时,需要使用Serializable或者Parcelable,数据持久化也需要Serializable。
系统提供的Parcelable接口的类:Intent、Bundle、Bitmap,List和Map也可以序列化,前提是每个元素都是可序列化的。
所以通信的数据自定义格式基本上都需要序列化
Binder
Android开发中,Binder主要用在Service中,包括AIDL及Messenger。普通Service中的Binder不涉及进程间通信,Messenger的底层其实是AIDL。
Android中的IPC方式
使用Bundle
在Bundle中附加需要传输给远程的数据并通过Intent发送出去
使用共享文件
A进程把数据写入文件,B进程通过读取这个文件来获取数据。可以是文本,xml,也可以是自定义的。SharedPreferences由于在内存中会有一份缓存,所以多进程时不建议使用。
使用Messenger
通过它可以在不同进程中传递Message对象,它的底层实现是AIDL,它一次处理一个请求,因此服务端不用考虑线程同步问题。
实现步骤:
- 服务端进程
在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它创建一个Messenger对象,然后在Service的onBind中返回这Messenger对象底层的Binder即可。
private final Messenger mMessenger=new Messenger(new MyHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
- 客户端进程
首先绑定服务端的Service,绑定成功后,用服务端返回的IBinder创建一个Messenger,通过这个Messenger就可以向服务端发送消息了。
private Messenger mService;
//
private ServiceConnection mConn=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService=new Messenger(iBinder);
Message msg= Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
Bundle bundle=new Bundle();
bundle.putString("msg","client is connect");
msg.setData(bundle);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
//服务端的回调
private Messenger mReplyMessenger=new Messenger(new ReplyHandler());
private static class ReplyHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MyConstants.MSG_FROM_SERVICE:
Log.d(TAG,"receive msg from service:"+msg.getData().getString("msg"));
break;
default:
super.handleMessage(msg);
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
Intent intent=new Intent(this,MessengerService.class);
bindService(intent,mConn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConn);
super.onDestroy();
}
使用AIDL
Messenger
本质是AIDL实现,主要是为了传递消息,无法处理大量并发。另外无法做到调用服务端方法。
AIDL通信流程:
- 服务端
创建一个服务端来监听客户端的请求,然后创建AIDL文件,将给客户端的接口在这个文件中声明,在Service中实现这个AIDL接口。
- 处理并发请求,所以要用
CopyOnWriteArrayList
- Binder会把传过来的对象重新生成新对象,
RemoteCallbackList
是系统提供的用于删除进程Listener的接口,内部实现线程同步,用它来注册及解注册。
- 客户端
绑定服务端的Service,将服务端返回的Binder对象转换成AIDL接口所属的类型,然后就可以调用AIDL中的方法了。
- 调用的方法可能是耗时操作,最好是在线程中去调用
- AIDL接口的创建
AIDL文件支持的数据类型:
基本类型(int
,long
,char
,boolean
,double
等)
String
和CharSequence
;
List
: 只支持ArrayList
(里面每个元素都必须支持)
Map
: 只支持HashMap
(key,value每个元素都必须支持)
Parcelable
: 所有实现了Parcelable
接口的对象
AIDL
: 所有AIDL接口本身也可以在AIDL文件中使用。
由于代码相对较多,请在文末链接去查看实现代码。
注意点:
- Parcelable对象和AIDL对象必须显示import进来
- AIDL文件中用到的Parcelable对象,必须新建一个和它同名的AIDL文件,并在其中声明为Parcelable类型
使用ContentProvider
创建一个自定义的ContentProvider只需继承ContentProvider并实现6个接口方法即可。
使用Socket
建立一个ServerSocket当服务端,再建立一个Socket当客户端去连接,它们通过本地网络来进行通信。
使用场景比较
一个表格来说明优缺点及使用场景
名称 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Bundle | 简单易用 | 只能传输Bundle支持的数据 | 四大组件间通信 |
文件共享 | 简单易用 | 不适合高并发,不能即时通信 | 无并发,简单交换数据 |
AIDL | 功能强大,支持一对多,实时通信 | 使用复杂,需处理好了线程同步 | 有一对多,远程调用的需求 |
Messenger | 支持一对多串行通信,支持实时通信 | 不支持高并发,不支持远程调用,只能传递Bundle数据 | 低并发一对多即时通信,无远程调用需求,或无须返回结果的远程调用 |
ContentProvider | 数据访问方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其它操作 | 可理解为受约束的AIDL,主要提供CRUD操作 | 一对多的进程间的数据共享 |
Socket | 功能强大,通过网络传输字节流,支持一对多并发实时通信 | 实现烦琐,不支持直接的远程调用 | 网络数据交换 |
最后给出Messenger,AIDL及Socket通信的完整示例,可以移步github