IPC为进程间通信或跨进程通信,是指两个进程进行进程间通信的过程。在PC和移动设备上一个进程指的是一个程序或者一个应用,所以我们可以将进程间通信简单理解为不同应用之间的通信,当然这种说法并不严谨。
在Android中,为每一个应用程序都分配了一个独立的虚拟机,或者说每个进程都分配了一个独立的虚拟机,不同虚拟机在内存分配上有不同的地址空间,这就导致了在不同的虚拟机间访问数据需要借助其他手段。下面分别介绍一下在Android中进行IPC的方式。
进程间通信(IPC)方式
- 使用Bundle
- 使用文件共享
- 使用Messenger
- 使用AIDL
- 使用COntentProvider
- 使用Socket
一、使用Bundle
我们都知道Android中三大组件Activity,Service,Receiver都支持在Intent中传递Bundle数据,而Bundle实现了Parcelable接口,所以它可以方便的在不同的进程间进行传输。当我我们在一个进程中启动另外一个进程的Activity、Service、Receiver时,我们就可以在Bundle中附加我们所需要传输给远程进程的信息并通过intent发送出去。这里注意,我们传输的数据必须能够被序列化。
下面我们看一下利用Bundle进行进程间通信的例子:
private void startWithIntent(){ Intent intent = new Intent(); //制定要打开的程序的包名(必须写全包名,不然会报错)和地址(activity名) intent.setComponent(new ComponentName("PackageName", "PackageName.intentIpcActivity")); //通过budle传递数据,可以携带序列化数据 Bundle bundle = new Bundle(); bundle.putInt("intextra", 0); bundle.putString("stringextra", "测试数据"); intent.putExtras(bundle); try{ startActivity(intent); }catch(Exception e){ ToastUtils.showMessage("没有找到对应文件"); } }
利用Bundle进行进程间通信是很容易的,大家应该也注意到,这种方式进行进程间通信只能是单方向的简单数据传输,它的使用有一定的局限性。
二、使用文件共享
共享文件也是种不错的进程间通信的方式,两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件FILE,B进程可以通过读取这个文件来获取这个数据。通过这种方式,除了可以交换简单的文本信息外,我们还可以序列化一个对象到文件系统中,另一个进程可以通过反序列化恢复这个对象。
比如A在进程中创建一个线程进行写数据
new Thread(new Runnable(){ @Override public void run(){ User user = new User(1, "user", false); File cachedFile = new File(CACHE_FILE_PATH); ObjectOutputStream objectOutputStream = null; try{ objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile)); objectOutputStream.writeObject(user); }catch(IOException e){ e.printStackTrace(); }finally{ objectOutputStream.close(); } } }).start();
在B进程创建一个线程进行读取数据
new Thread(new Runnable(){ @Override public void run(){ User user = null; File cachedFile = new File(CACHE_FILE_PATH); if (cachedFile.exists()){ ObjectInputStream objectInputStream = null; try{ objectInputStream = new ObjectInputStream(new FileInputStream(cachedFile)); user = objectInputStream.readObject(user); } catch(IOException e){ e.printStackTrace(); }finally{ objectInputStream.close(); } } try{ objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile)); objectOutputStream.writeObject(user); }catch (IOException e){ e,printStackTrace(); }finally{ objectOutputStream.close(); } }
通过文件共享的这种方式来共享数据对文件的格式是没有具体要求的,比如可以是文本文件、也可以是XML文件,只要读写双方约定数据格式即可。这种方式进行进程间通信虽然方便,可是也是有局限性的,比如并发读/写,这会导致比较严重的问题,如读取的数据不完整或者读取的数据不是最新的。因此通过文件共享的方式适合在对数据同步要求不高的进程之间通信,并且要妥善处理并发读/写问题。
三、使用Messenger
Messenger是一种轻量级的IPC方案,它的底层实现是AIDL,可以在不同进程中传递Messenger对象,在Messenger中放入我们需要传递的数据。它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。
Messenger的使用方法也是比较简单的,实现一个Messenger有如下几步,分为服务端和客户端:
服务端进程:在A进程创建一个Service来处理其他进程的连接请求,同时创建一个Handler并通过他来创建一个Messenger对象,然后在Service的onBind中返回这个Messneger对象底层的Binder即可。
public class MessengerService extends Service { private static final String TAG = MessengerService.class.getSimpleName(); private class MessengerHandler extends Handler { /** * @param msg */ @Override public void handleMessage(Message msg) { switch (msg.what) { case Constants.MSG_FROM_CLIENT: Log.d(TAG, "receive msg from client: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]"); Toast.makeText(MessengerService.this, "receive msg from client: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]", Toast.LENGTH_SHORT).show(); Messenger client = msg.replyTo; Message replyMsg = Message.obtain(null, Constants.MSG_FROM_SERVICE); Bundle bundle = new Bundle(); bundle.putString(Constants.MSG_KEY, "我已经收到你的消息,稍后回复你!"); replyMsg.setData(bundle); try { client.send(replyMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } private Messenger mMessenger = new Messenger(new MessengerHandler()); @Nullable @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } }
客户端进程:首先绑定服务端 Service ,绑定成功之后用服务端的 IBinder 对象创建一个 Messenger ,通过这个 Messenger 就可以向服务端发送消息了,消息类型是 Message 。如果需要服务端响应,则需要创建一个 Handler 并通过它来创建一个 Messenger(和服务端一样),并通过 Message 的 replyTo 参数传递给服务端。服务端通过 Message 的 replyTo 参数就可以回应客户端了。
public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private Messenger mGetReplyMessenger = new Messenger(new MessageHandler()); private Messenger mService; //为了收到Service的回复,客户端需要创建一个接收消息的Messenger和Handler private class MessageHandler extends Handler { @Override public void handleMessage(Message msg) { //消息处理 switch (msg.what) { case Constants.MSG_FROM_SERVICE: Log.d(TAG, "received msg form service: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]"); Toast.makeText(MainActivity.this, "received msg form service: msg = [" + msg.getData().getString(Constants.MSG_KEY) + "]", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } @Override protected void onCreate(Bundle savedInstanceState) { super