借鉴自开发艺术
跨进程通信方式:
Intent附加extras
共享文件
Binder
ContentProvider
Socket
Bundle不支持的类型传不了。一种特殊场景:A进程正在进行一个计算,计算完后,需要发送给B,而且开启B的一个模块。但是结果的类型Bundle不支持。这个时候可以让A用Intent开启B的服务,让服务进行后台计算,计算完后再开启B中的某个模块,这样就用极小的代价满足了需求。
共享文件就是存储,读取一个文件。虽然不同的进程的资源不一样,但是外部的资源仍然是一样的。但是共享文件在Linux上是可以并发的,所以有可能出错。
Messenger
他可以在不同进程间传递Message对象,是一种轻量级的IPC,底层实现是AIDL。但是服务端不需要考虑同步问题,因为一次只会处理一个Message。
1.服务端进程
在服务端创建一个Service来处理客户端的连接请求,再创建一个Handler,通过这个Handler来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
2.客户端进程
客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messenger,并且把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。
一个通过Messenger发送消息的例子
public class MessageService extends Service { private static class MessengerHandler extends Handler {//Handler在这里是接收到消息后的回调 @Override public void handleMessage(Message msg) { switch (msg.what) { case 666: Log.i("xbh", "消息是" + msg.getData().getString("msg")); break; default: super.handleMessage(msg); } } } private final Messenger mMessenger = new Messenger(new MessengerHandler());//相当于给他一个回调 public MessageService() { } @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder();//从Messenger中获取binder并返回 } }
<service android:name=".MessageService" android:process=":hello" />
public class MessengerActivity extends AppCompatActivity { private Messenger mService; private ServiceConnection mConnection = new ServiceConnection() {//连接建立时的回调:发送信息 @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service); Message msg = Message.obtain(null, 666); Bundle data = new Bundle(); data.putString("msg", "hello, this is client"); msg.setData(data); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); Intent intent = new Intent(this, MessageService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService(mConnection); super.onDestroy(); } }
Message中的载体只有what,arg1,arg2,Bundle,replyTo。Message中的另一个字段object在同一个进程中是很实用的,但是在进程间通信的时候,也仅仅是系统提供的实现了Parcelable接口的对象才能通过它来传输。所以我们自定义的实现Parcelable接口的对象是不能通过object来传输的。
如果上面的例子想要实现服务端回应客户端的功能的话
服务端,修改MessageHandler
private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case 666: Log.i("xbh", "消息是" + msg.getData().getString("msg")); Messenger client = msg.replyTo;//msg中会维护之前那个Messenger,仍然是之前那个跨进程的消息队列 Message replyMessage = Message.obtain(null, 888); Bundle bundle = new Bundle(); bundle.putString("reply", "get it!"); replyMessage.setData(bundle); try { client.send(replyMessage); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } private final Messenger mMessenger = new Messenger(new MessengerHandler());
客户端,新建一个Handler,在ServiceConnection中的回调中把这个回复的Handler交给msg的replyTo子段去维护
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler()); private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case 888: Log.i("xbh", msg.getData().getString("reply")); break; default: super.handleMessage(msg); } } }
private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service); Message msg = Message.obtain(null, 666); Bundle data = new Bundle(); data.putString("msg", "hello, this is client"); msg.setData(data); msg.replyTo = mGetReplyMessenger;//这句就是让他去维护 try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } };其核心在于1.服务端可以拿到mGet...的实例。2.Activity第一个Messenger的参数是service,在这之后的Messenger都会先后接受到service是谁,发送者是谁,只不过这一切都是隐式的。(最后有个注意点,另外进程的log要切换到另外进程的log里去看)
流程