进程间通讯分为以下几种方式:
1.Bundle,在Intent中通过通过Bundle传递数据,最简单的比如说我们自定义的app可以启动别的大型app,比如百度地图。
Intent实现的数据传递必须序列化,这是最简单的进程间通讯方式。
这里特别说明一种情况:在a进程中需要计算结果,然后计算完成后启动B进程并且将结果传递给B进程,这个过程计算的结果如果不能通过Bundle传递,如果用其他IPC会比较麻烦,这个时候就可以用另外一种方式解决:通过Intent启动B进程的一个Service组件(比如IntentService),让Service在后天进行计算,计算完毕后再启动B进程中真正要启动的组件,这样就可以直接解决跨进程的问题,核心思想是将A的计算任务转移到B进程的后台Service中去执行。
2.通过共享文件来共享数据,一个app将数据写入到本地文件中,别的进程可以访问到本地文件并且获取数据,这个主要是用到io流读写数据,注意读写对象的时候需要将对象序列化。这种方式也是有局限性的,比如并发读写的时候数据就会可能出差错。只适用于数据同步性不高的情况下。特别说明一下SharedPreference,其底层是通过xml文件实现键值对存储。每个应用的sh文件都可以在当前包目录下查询到。因为SharePreference系统采用了缓存策略,即在内存中会有一份sharedPreference文件的缓冲,因此多进程模式下面对高并发的读写,有很大几率会丢失数据,所以不建议进程间通讯使用sharedPreference.
3.通过Binder来通讯,会用到aidl
3.1使用Messenger通讯,查看Messenger会发现它的原理也即是aidl封装。实现Messenger的方法:
服务端:创建service处理客户端请求,同时创建一个Handler并且通过它来创建一个Messenger,然后在Service的OnBind中返回这个Messager对象底层 的Binder.
客户端:绑定服务端的service,用服务器返回的IBinder对象创建一个Messenger,通过这个Messenger就可以像服务端发消息了,发消息的类型是Messenger对象,如果需要回应服务单,就需要在客户端创建同服务端一样的Handler和Messenger.需要注意到是在Messenger中传输数据必须将数据放入Message中;并且Message.obj传输的对象只能是系统序列化的对象,所以非系统序列化的对象不能通过message.obj赋值
以下是自定义的Service类,即服务端:
public class IPCService1 extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();//返回Messenger的Binder.即handler
}
private static class IPCHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1://客户端响应返回的数据
Logger.d("serviceData====" + msg.getData().getString("msg"));
Messenger client = msg.replyTo;//获取客户端的Messenger;
Message message = Message.obtain(null, 2);
Bundle bundle = new Bundle();
bundle.putString("reply", "服务端已经收到请求,响应了客户端");
message.setData(bundle);
try {
client.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
break;
}
}
}
private final Messenger mMessenger = new Messenger(new IPCHandler());
}
客户端的Activity:
public class ThridActivity extends AppCompatActivity {
private Messenger mMessenger;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_thrid);
new Thread(new Runnable() {
@Override
public void run() {
getCacheData();
}
}).start();
Intent intent = new Intent(this, IPCService1.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);//先实例化Messenger对象
Message message = Message.obtain(null, 1);
Bundle bundle = new Bundle();
bundle.putString("msg", "来自于客户端的请求");
message.setData(bundle);//Message数据封装Message数据
//将客户端响应服务端的Messenger通过这个参数传递给服务端,这样服务端接受到数据之后就会通过这个replyMsg将数据反馈回来,从而达到接收的效果
message.replyTo=replyMsg;
try {
mMessenger.send(message);//通过Messgener发送数据,让服务端接受
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
//为了接受服务端的传递数据,客户端需要也定义一个Handler和Messenger
private static class MsgHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 2://服务端回应
Logger.d(msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
break;
}
}
}
private Messenger replyMsg=new Messenger(new MsgHandler());
}
注意上面的Service和Activity我在清单中通过process属性声明分属于两个不同的进程。以上会发现通过Messenger来实现进程间通讯,如果消息太多的话,服务器只能一条一条的处理,是串行的方式处理,所以当面临并发消息的时候也不合适,或者说想要跨进程调用服务端的方法的时候Messenger也无法达到目的。
4.aidl通讯
4.ContentProvider,内容提供者,天生就是提供跨进程数据通讯的,ContentProvider也是通过Binder的原理来实现进程间通讯的,只不过系统为我们封装好了,便于使用。
5.通过网络通讯实现数据传递,即Socket也可以实现ipc(进程间通讯);