在操作系统中,每个进程都有一块独立的内存空间。为了保证程序的的安全性,操作系统都会有一套严格的安全机制来禁止进程间的非法访问,但是,很多情况下进程间也是需要相互通信的
进程间通信(Inter-process communication,简称IPC):是指运行在不同进程中的若干线程间的数据交换。
一、使用 Intent 中传递 Bundle 数据
由于 Bundle 实现了 Parceable 接口,在一个进程中启动另外一个进程的 Activity,Service,Receiver 时,可以很方便的在不同的进程之间进行传输,传输的数据类型必须是基本数据类型或能够被序列化
可以被Bundle传输的数据类型:
- 基本数据类型(int, long, char, boolean, double等)
- String 和 CharSequence
- ArrayList
- HashMap
- 所有实现 Parcelable 接口的对象
代码示例:
Intent intent = new Intent(MainActivity.this, TwoActivity.class); Bundle bundle = new Bundle(); bundle.putString("data", "data"); intent.putExtras(bundle); startActivity(intent);
总结:
Bundle进行进程间通信只能进行单方向的简单数据传输,有一定的局限性
二、使用文件共享传输数据
两个进程通过读/写同一个文件来交换数据,除了交换简单的文本信息之外,还可以序列化一个对象到文件中,来进行数据通信
文件共享可以是文本文件,也可以是XML文件,读写双方约定数据格式即可
总结:
使用方便,也是有局限性,并发读写问题,只能适用于数据同步要求不高的进程间通信
三、使用Messenger传输数据
Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL
Messenger的实现
- 服务端进程:
- 在A进程创建一个 Service 来处理其他进程的连接请求
- 创建一个 Handler 来创建一个 Messenger 对象
- 在 Service 的 onBind() 中返回这个 Messenger 对象底层的 Binder
public class MessengerService extends Service{ private Handler messengerHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //取出客户端的消息内容 Bundle bundle = msg.getData(); String clientMsg = bundle.getString("client"); Log.i(TAG, "来自客户端的消息:" + clientMsg); //新建一个Message对象,作为回复客户端的对象 Message message = Message.obtain(); Bundle bundle1 = new Bundle(); bundle1.putString("service", "服务端收到"); message.setData(bundle1); try { msg.replyTo.send(message); } catch (RemoteException e) { e.printStackTrace(); } } }; //创建服务端Messenger private final Messenger mMessenger = new Messenger(messengerHandler); @Override public IBinder onBind(Intent intent) { //向客户端返回IBinder对象,客户端利用该对象访问服务端 return mMessenger.getBinder(); } @Override public void onCreate() { super.onCreate(); } }
- 客户端进程:
- 在进程B中绑定远程进程 Service
- 绑定成功后,根据 Service 返回的 IBinder 对象创建 Messenger 对象,并使用此对象发送消息
- 客户端创建了一个 Messenger 发送给 Service 端,Service 端就可以通过客户端的 Messenger 向客户端发送消息
public class MessengerActivity extends Activity{ private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //获取服务端关联的Messenger对象 Messenger mService = new Messenger(service); //创建Message对象 Message message = Message.obtain(); Bundle bundle = new Bundle(); bundle.putString("client", "服务端在吗,听到请回答"); message.setData(bundle); //在message中添加一个回复mReplyMessenger对象 message.replyTo = mReplyMessenger; try { mService.send(message); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; //为了收到Service的回复,客户端需要创建一个接收消息的Messenger和Handler private Handler messengerHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //消息处理 Bundle bundle = msg.getData(); String serviceMsg = bundle.getString("service"); Log.i(TAG, "来自服务端的回复:" + serviceMsg); } }; private Messenger mReplyMessenger = new Messenger(messengerHandler); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); init(); } private void init() { Intent intent = new Intent(MessengerActivity.this, MessengerService.class); bindService(intent, conn, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService(conn); super.onDestroy(); } }
总结:
使用 Handler 实现,以串行的方式处理客服端发送过来的消息,只能适用于并发小的消息传递
四、使用AIDL传递数据
AIDL的原理
使用了代理模式对Binder的使用进行了优化,使用AIDL保证了代码的整洁,省去了编写繁琐的代理类相关代码。
实现步骤:
1.创建AIDL接口
在要创建AIDL的目录上右键->New->AIDL->AIDl File 来创建一个AIDL文件
创建一个名为IXXXService的AIDL文件,并添加一个getxxx的方法
Rebuild一下项目,IDE会自动生成AIDL的代码
2.AIDL生成的代码
在项目的build同包名目录下,自动生成的一个名为IXXXService的接口
在接口中有一个名为Stub的内部类,它继承了Binder,并实现了IXXXService接口,它的内部有一个asInterface的方法
Stub类中还有一个名为Proxy的内部类,Proxy的getxxx方法通过Binder去读取服务端的写入数据。
3.AIDL客户端
连接到服务端后通过IXXXService.Stub下的asInterface方法来获取Binder或者Binder的代理对象
示例代码:
1.编写aidl类
// IMyAidlInterface.aidl package com.lf.hilibrary.aidl; // Declare any non-default types here with import statements interface IMyAidlInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); String getname(String name); }
2.项目build生成java文件
/* * This file is auto-generated. DO NOT MODIFY. */ package com.lf.hilibrary.aidl; // Declare any non-default types here with import statements public interface IMyAidlInterface extends android.os.IInterface { /** Default implementation for IMyAidlInterface. */ public static class Default implements com.lf.hilibrary.aidl.IMyAidlInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { } @Override public java.lang.String getname(java.lang.String name) throws android.os.RemoteException { return null; } @Override public android.os.IBinder asBinder() { return null; } } /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.lf.hilibrary.aidl.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.lf.hilibrary.aidl.IMyAidlInterface"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.lf.hilibrary.aidl.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.lf.hilibrary.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.lf.hilibrary.aidl.IMyAidlInterface))) { return ((com.lf.hilibrary.aidl.IMyAidlInterface)iin); } return new com.lf.hilibrary.aidl.IMyAidlInterface.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(descriptor); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0!=data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } case TRANSACTION_getname: { data.enforceInterface(descriptor); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _result = this.getname(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } default: { return super.onTransact(code, data, reply, flags); } } } private static class Proxy implements com.lf.hilibrary.aidl.IMyAidlInterface { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean)?(1):(0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString); return; } _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public java.lang.String getname(java.lang.String name) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(name); boolean _status = mRemote.transact(Stub.TRANSACTION_getname, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getname(name); } _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } public static com.lf.hilibrary.aidl.IMyAidlInterface sDefaultImpl; } static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getname = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); public static boolean setDefaultImpl(com.lf.hilibrary.aidl.IMyAidlInterface impl) { // Only one user of this interface can use this function // at a time. This is a heuristic to detect if two different // users in the same process use this function. if (Stub.Proxy.sDefaultImpl != null) { throw new IllegalStateException("setDefaultImpl() called twice"); } if (impl != null) { Stub.Proxy.sDefaultImpl = impl; return true; } return false; } public static com.lf.hilibrary.aidl.IMyAidlInterface getDefaultImpl() { return Stub.Proxy.sDefaultImpl; } } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException; public java.lang.String getname(java.lang.String name) throws android.os.RemoteException; }
3.客户端调用
public class AidlActivity extends AppCompatActivity { private IMyAidlInterface mProxy; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); bindService(); // btn.setOnClickListener(view -> getName("lf")); } // 绑定服务 private void bindService() { String action = "android.intent.action.server.aidl.gradeservice"; Intent intent = new Intent(action); intent.setPackage(getPackageName()); bindService(intent, mServiceConnection, BIND_AUTO_CREATE); } // 查询成绩 private void getName(String name) { String result; try { result = mProxy.getname(name); } catch (RemoteException e) { e.printStackTrace(); } } private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { // 连接服务后,根据是否跨进程获取Binder或者Binder的代理对象 mProxy = IMyAidlInterface.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { mProxy = null; } }; }
五、使用ContentProvider的方式传递数据
应用程序通过ContentProvider 暴露数据操作的接口,其他应用程序通过接口来操作接口内的数据,包括数据的增、删、改、查等操作。
ContentProvider 分为系统的(如:联系人,图片等),和自定义的
自定义ContentProvider
- 定义ContentProvider 类,继承 ContentProvider 基类
- 在 AndroidMainfest.xml 中注册ContentProvider,给 ContentProvider 绑定一个域名
- 其他应用就可以访问 ContentProvider 暴露出来的数据了
- 调用 Activity 的 getContentResolver() 获取 ContentResolver 对象;
- 根据调用的 ContentResolver 的 insert()、delete()、update() 和 query() 方法操作数据库即可。
六、使用BroadcastReceiver的方式
通过广播来实现跨进程通信
实现方式:
- 创建需要启动的 BroadcastReceivert 的 intent;
- 调用 Context 的 sendBroadcast() 或者 sendOrderBroadcast() 方法来启动指定的 BroadcastReceivert。
七、使用 Socket 的方式
通过网络通信来实现跨进程通信