> 文章列表 > Android进程间通信

Android进程间通信

Android进程间通信

        在操作系统中,每个进程都有一块独立的内存空间。为了保证程序的的安全性,操作系统都会有一套严格的安全机制来禁止进程间的非法访问,但是,很多情况下进程间也是需要相互通信的

进程间通信(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的实现

  • 服务端进程:
  1. 在A进程创建一个 Service 来处理其他进程的连接请求
  2. 创建一个 Handler 来创建一个 Messenger 对象
  3. 在 Service 的 onBind() 中返回这个 Messenger 对象底层的 Binder 
public class MessengerService extends Service{private Handler messengerHandler = new Handler() {@Overridepublic 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();}}};//创建服务端Messengerprivate final Messenger mMessenger = new Messenger(messengerHandler);@Overridepublic IBinder onBind(Intent intent) {//向客户端返回IBinder对象,客户端利用该对象访问服务端return mMessenger.getBinder();}@Overridepublic void onCreate() {super.onCreate();}
}
  • 客户端进程:
  1. 在进程B中绑定远程进程 Service
  2. 绑定成功后,根据 Service 返回的 IBinder 对象创建 Messenger 对象,并使用此对象发送消息
  3. 客户端创建了一个 Messenger 发送给 Service 端,Service 端就可以通过客户端的 Messenger 向客户端发送消息
public class MessengerActivity extends Activity{  private ServiceConnection conn = new ServiceConnection() {@Overridepublic 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();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};//为了收到Service的回复,客户端需要创建一个接收消息的Messenger和Handler  private Handler messengerHandler = new Handler() {@Overridepublic 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);@Overrideprotected 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);}@Overrideprotected 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 statementsinterface 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 statementspublic 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;}@Overridepublic 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;@Overrideprotected 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() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {// 连接服务后,根据是否跨进程获取Binder或者Binder的代理对象mProxy = IMyAidlInterface.Stub.asInterface(iBinder);}@Overridepublic void onServiceDisconnected(ComponentName componentName) {mProxy = null;}};
}

五、使用ContentProvider的方式传递数据

        应用程序通过ContentProvider 暴露数据操作的接口,其他应用程序通过接口来操作接口内的数据,包括数据的增、删、改、查等操作。

        ContentProvider 分为系统的(如:联系人,图片等),和自定义的

自定义ContentProvider

  1. 定义ContentProvider 类,继承 ContentProvider 基类
  2. 在 AndroidMainfest.xml 中注册ContentProvider,给 ContentProvider 绑定一个域名
  3. 其他应用就可以访问 ContentProvider 暴露出来的数据了
    1. 调用 Activity 的 getContentResolver() 获取 ContentResolver 对象;
    2. 根据调用的 ContentResolver 的 insert()、delete()、update() 和 query() 方法操作数据库即可。

六、使用BroadcastReceiver的方式

        通过广播来实现跨进程通信

实现方式:

  1. 创建需要启动的 BroadcastReceivert 的 intent;
  2. 调用 Context 的 sendBroadcast() 或者 sendOrderBroadcast() 方法来启动指定的 BroadcastReceivert。

七、使用 Socket 的方式

        通过网络通信来实现跨进程通信