Android(安卓)进程间通信:AIDL
1. 概述
AIDL:Android Interface Definition Language,即Android接口定义语言。在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。(摘自Google developer网站)。
只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。
2. AIDL 支持的数据类型
a. Java 编程语言中的所有基本数据类型。(如 int、long、char、boolean 等等)
b. String 和 CharSequence
c. List 和Map:List /Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。
d. AIDL 生成的接口。
e. 实现Parcelable接口的类
3. AIDL 实现步骤:
服务端:
1. 创建 .aidl 文件
此文件定义带有方法签名的编程接口。
2. 实现接口
Android SDK 工具会生成一个以 .aidl
文件命名的 .java
接口文件。生成的接口包括一个名为 Stub
的子类,这个子类是其父接口(例如,YourInterface.Stub
)的抽象实现,用于声明 .aidl
文件中的所有方法。
3. 向客户端公开接口
实现 Service 并重写onBind() 以返回 Stub 类的实现。
客户端:
1.在项目中加入 .aidl 文件
2. 实现 ServiceConnection 。
3. 调用 bindService ,以传入 ServiceConnetion。
4. 在 onServiceConnected() 实现中,将收到一个 IBinder 实例(名为 service)。调用 YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为 YourInterface 类型。
4. AIDL实例
以下为具体的代码实现:
服务端:
1. 创建要传递的实体类 Person,并实现 Parcelable 接口。
package cn.zzw.aidl;import android.os.Parcel;import android.os.Parcelable;public class PersonInfo implements Parcelable{ private String name; private int age; private String sex; private float height; public PersonInfo(String name, int age, String sex, float height) { this.name = name; this.age = age; this.sex = sex; this.height = height; } public PersonInfo() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public float getHeight() { return height; } public void setHeight(float height) { this.height = height; } public static Creator getCREATOR() { return CREATOR; } protected PersonInfo(Parcel in) { name = in.readString(); age = in.readInt(); sex = in.readString(); height = in.readFloat(); } public static final Creator CREATOR = new Creator() { @Override public PersonInfo createFromParcel(Parcel in) { return new PersonInfo(in); } @Override public PersonInfo[] newArray(int size) { return new PersonInfo[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); dest.writeString(sex); dest.writeFloat(height); }}
注意:此文件所在的包名要和 .aidl 文件所在的包名一样。
2. 创建 IPerson.aidl 文件
// IPerson.aidlpackage cn.zzw.aidl;import cn.zzw.aidl.PersonInfo;// Declare any non-default types here with import statementsinterface IPerson { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ String sayHello(in PersonInfo personInfo); int sumnNum(int num1,int num2);}
注意: 需要import 相关的实体类,否则会报如下错误:
Process 'command 'D:\Android\sdk\build-tools\28.0.3\aidl.exe'' finished with non-zero exit value 1
3. 对于实体类 Person 也必须创建对应的 .aidl 文件
// PersonInfo.aidlpackage cn.zzw.aidl;parcelable PersonInfo;
以上三步创建后,目录如下:
4. 创建隐式意图的Service(对于此文件所在的包没有要求),并在onBind方法返回已经实现AIdl 接口的对象。
package cn.zzw.aidlserverdemo.service;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import cn.zzw.aidl.IPerson;import cn.zzw.aidl.PersonInfo;public class AidlServerService extends Service { private class MyBinder extends IPerson.Stub{ @Override public String sayHello(PersonInfo personInfo) throws RemoteException { if(null!=personInfo) { String name = personInfo.getName(); int age = personInfo.getAge(); float height = personInfo.getHeight(); String sex = personInfo.getSex(); return "Hello,My Name is "+name; } return null; } @Override public int sumnNum(int num1, int num2) throws RemoteException { return num1+num2; } }; @Override public IBinder onBind(Intent intent) { return new MyBinder(); }}
在 AndroidManifest.xml 中注册隐式意图的Service:
至此Server端的实现就算完成了,接下来就是客户端如何调用了。
客户端:
1. 在客户端中实现服务端中的第 1,2,3 步。
注意:文件所在的包名一定要和服务端一样!!!
2. 绑定远程服务,获取服务对象。
private void bindToClientService() { intent = new Intent(); intent.setAction("cn.zzw.PERSON.SERVER"); intent.setPackage("cn.zzw.aidlserverdemo"); bindService(intent,conn,BIND_AUTO_CREATE); } ServiceConnection conn =new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService= IPerson.Stub.asInterface(service); Log.d(TAG,"Service Connect Successfully"); } @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG,"Service Connect Fail"); } };
3. 调用远程服务中的方法
public void onClick(View v) { switch (v.getId()) { case R.id.mBtn_hello: if(mService!=null) { String msgInfo = null; try { msgInfo = mService.sayHello(mInfo); Toast.makeText(this,msgInfo,Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } }else { Log.d(TAG,"Service is Null"); bindToClientService(); } break; case R.id.mBtn_calculate: if(mService!=null) { String msgInfo = null; try { int sumInfo = mService.sumnNum(50,50); Toast.makeText(this,""+sumInfo,Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } }else { Log.d(TAG,"Service is Null"); bindToClientService(); } break; } }
效果:
先尝试只安装Client,会发现绑定不了Service:
log如下:
D/Client: Service is NullD/Client: Service is Null
安装服务端后,再次运行客户端后:
附上以上实例的代码:
https://download.csdn.net/download/zzw0221/11248522
5. AIDL 的工作原理
当创建AIDL文件并Clean Project 代码后,会生成相应的Java文件:
先来一段伪代码:类整体结构
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\AndroidProject\\AIDLServerDemo\\app\\src\\main\\aidl\\cn\\zzw\\aidl\\IPerson.aidl */package cn.zzw.aidl;// Declare any non-default types here with import statementspublic interface IPerson extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements cn.zzw.aidl.IPerson //1{ ... } public java.lang.String sayHello(cn.zzw.aidl.PersonInfo personInfo) throws android.os.RemoteException; //2 public int sumnNum(int num1, int num2) throws android.os.RemoteException; //3}
注释2 和注释3 就是我们在.aidl 中定义的方法。
注释1 是一个静态的抽象类,并且继承了Binder类。Binder是Android中实现IPC方式。AIDL就是利用Binder来实现的。
这里就不对于Binder进行解释,后面会对Binder的源码进行解读,再记录一篇关于Binder的原理。
看看Stub这个类:
public static abstract class Stub extends android.os.Binder implements cn.zzw.aidl.IPerson { private static final java.lang.String DESCRIPTOR = "cn.zzw.aidl.IPerson"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an cn.zzw.aidl.IPerson interface, * generating a proxy if needed. */ public static cn.zzw.aidl.IPerson asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof cn.zzw.aidl.IPerson))) { return ((cn.zzw.aidl.IPerson) iin); } return new cn.zzw.aidl.IPerson.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_sayHello: { data.enforceInterface(descriptor); cn.zzw.aidl.PersonInfo _arg0; if ((0 != data.readInt())) { _arg0 = cn.zzw.aidl.PersonInfo.CREATOR.createFromParcel(data); } else { _arg0 = null; } java.lang.String _result = this.sayHello(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_sumnNum: { data.enforceInterface(descriptor); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.sumnNum(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } default: { return super.onTransact(code, data, reply, flags); } } } private static class Proxy implements cn.zzw.aidl.IPerson { 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; } @Override public java.lang.String sayHello(cn.zzw.aidl.PersonInfo personInfo) 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); if ((personInfo != null)) { _data.writeInt(1); personInfo.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public int sumnNum(int num1, int num2) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(num1); _data.writeInt(num2); mRemote.transact(Stub.TRANSACTION_sumnNum, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_sumnNum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); }
Stub类的类结构如下:
下面两个int常量是用来标识我们在接口中定义的方法的:
static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_sumnNum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
DESCRIPTOR 常量是Binder的唯一标识。
private static final java.lang.String DESCRIPTOR = "cn.zzw.aidl.IPerson";
asInterface 方法非常熟悉,上面客户端才用到的,用于将服务端的Binder对象转换为客户端所需要的接口对象。该过程区分进程,如果进程一样,就返回服务端Stub对象本身,否则就返回封装后的Stub.Proxy对象。
public static cn.zzw.aidl.IPerson asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof cn.zzw.aidl.IPerson))) { return ((cn.zzw.aidl.IPerson) iin); } return new cn.zzw.aidl.IPerson.Stub.Proxy(obj); }
onTransact 方法是实现 Binder 类后重写的方法。这是运行在服务端的Binder线程中的,当客户端发起远程请求后,在底层的方法就会触发此方法。
5. 总结
AIDL的原理就是Binder,AIDL的最终结果就是让进程间通讯变得简单,自动的完成了参数序列化发送以及解析返回数据的一系列麻烦。
更多相关文章
- Android(安卓)4.0 Launcher2源码分析——主布局文件
- Android(安卓)利用drawable中的gradient属性实现背景颜色渐变
- 源码茶舍之android:externalService是什么属性?实现原理?
- 实现Android(安卓)Studio JNI开发C/C++使用__android_log_print
- freetype 在android编译时上的一个makefile文件
- android的binder机制研究二
- Android(安卓)中级教程之------Android(安卓)MediaPlayer播放mp3
- android 上 webkit js 扩展之全局本地对象实现步骤
- android中的资源访问