前言
在Android的系统中有几种跨进程通信方式,但我们使用最常见的就是Aidl,Aidl底层调用使用的是Binder,Binder通信又是Android四大组件跨进程通信的根本,所以学习Aidl有助于我们理解Binder,也助于我们后续理解四大组件。这里我会详细讲解Aidl的实现方式,以及生成的Aidl Java文件
-
Aidl是什么
-
Aidl支持哪些数据类型
-
Aidl的使用
-
Aidl生成文件的讲解
Aidl(Android接口定义语言)是Android提供的一种ICP通信机制。 首先学习Aidl我们心中要有Clien端和Server端的概念
,并且在Clien和Server通信过程中涉及到Binder的相关知识。Aidl支持的数据类型一共有
4种
- Java的基本数据类型
- List和map: 集合中数据必须是Aidl支持的数据类型, 并且在Server端必须是ArrayList和HashMap
- 其他Aidl生成的接口
- 实现Parcelable的实体
Aidl的使用一般是两个App一个作为客户端一个作为服务端,以及一个 客户端和服务端通信的媒介Aidl,客户端的Aidl和服务端的Aidl 需要定义成一样的,在客户端序列化后要在服务端反序列换查询。
在这里我为了方便就讲客户端和服务端写在一起,实际的效果就是在使用Aidl与Service通讯。
新建Aidl
- 在main目录新建Aidl,文件名叫StudentAidl, 并添加方法 getStudent(int no)方法;然后会在main目录下生成aidl文件夹,文件下会有一个和package相同的包,包下有一个 StudentAidl.aidl文件。
StudentAidl.aidl文件代码如下
interface StudentAidl {
/**
* 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 getStudent(int no);
}
- 然后Build 一下项目,会在build/generated/source/aidl 下面看到生成了一个和你项目目录一下的StudentAidl的Java文件,这就是clien 和 server通信的媒介,代码结构如下
public interface StudentAidl extends android.os.IInterface
{
public static abstract class Stub extends android.os.Binder implements com.example.aidl.StudentAidl{
...
}
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 getStudent(int no) throws android.os.RemoteException;
}
新建server:
StudentService
继承Service
,实现Service
中的抽象方法onBinder()
;在onCreate()
中创建一个IBinder
对象,在onBinder()
中返回 IBinder
对象。这里我们要知道 StudentAidl.Stub的Stub对象是继承于Binder,而Binder又实现了IBinder接口
。
public class StudentService extends Service {
private String[] studentNo = {"张三", "李四", "王五"};
private IBinder binder;
@Override
public void onCreate() {
super.onCreate();
Log.e("LOG", "-------StudentService---onCreate--");
binder = new StudentQueryBinder();
}
@Override
public void unbindService(ServiceConnection conn) {
Log.e("LOG", "-------StudentService---unbindService--");
super.unbindService(conn);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("LOG", "-------StudentService---onBind--");
return binder;
}
@Override
public void onRebind(Intent intent) {
Log.e("LOG", "-------StudentService---onRebind--");
super.onRebind(intent);
}
class StudentQueryBinder extends StudentAidl.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String getStudent(int no) throws RemoteException {
int l = studentNo.length;
if (l < 0) {
no = 0;
}
if (no >= l) {
no = l - 1;
}
return studentNo[no];
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("LOG", "-------StudentService---onStartCommand--");
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("LOG", "-------StudentService---onUnbind--");
return super.onUnbind(intent);
}
@Override
public boolean stopService(Intent name) {
Log.e("LOG", "-------StudentService---stopService--");
return super.stopService(name);
}
@Override
public void onDestroy() {
Log.e("LOG", "-------StudentService---onDestroy--");
super.onDestroy();
}
}
Service需要在Manifest中注册,注册如下:
在注册中需要设置 android:process,其规则如下
android:process=":xxx"与android:process=“XXX"不仅仅是用来定义当前进程的名字,一般情况各组件的进程名均为当前应用的包名。那有”:"开头意味着:
- 1、进程名字是在当前进程名(即包名)下追加命名,如当前包名为com.hongliang.demo,那么第一种情况下的进程名就是com.hongliang.demo:xxx而第二种就直接以"xxx"来命名;
- 2、":"表示当前新进程为主进程(进程名为包名)的私有子进程,其他应用的组件不可以和它跑在同一个进程中。而后者则是全局的进程,其他应用通过设置相同的ShareUID可以和它跑在同一个进程。
- 3、另外一种方式就是android:process=“com.aidl.student.service”,直接指定不带冒号,这样在控制台中就会直接显示显示一个名字为com.aidl.student.service的进程
两种指定方式的区别:
1.以冒号设置的为私有进程: android:process为“com.hongliang.demo:xxx”。这种设置形式表示该进程为当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中
2. 没有冒号设置的为全局进程:如android:process=“com.aidl.student.service”,以小写字母开头,表示运行在一个以这个名字命名的全局进程中,其他应用通过设置相同的ShareUID可以和它跑在同一个进程。
因为我们是在同一个App中启动Service所以不需要设置 android:exported=“true”,如果是用两个App去实现service和Clien,那么service端就徐需要添加该属性
<service
android:name=".StudentService"
android:process="com.aidl.student.service">
<intent-filter>
<action android:name="com.hongliang.demo.StudentService" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</service>
接下来Client端实现:
- Client 是以一个Activity来实现,首先使用
Intent
绑定Service
, 绑定的时候,会需要传递一个ServiceConnection
对象 - ServiceConnectionImpl
内部类
实现ServiceConnection接口,生成一个ServiceConnection对象 ServiceConnection
对象需要实现onServiceConnected
和onServiceDisconnected
方法。onServiceConnected会返回一个IBinder
,这个IBinder就是bindService注册后在StudentService类onBind方法里返回的
- 然后调用会在
onServiceConnected
中调用StudentAidl.Stud.asInterface(IBinder obj)
方法,返回一个StudentAidl
,而StudentAidl对象就是我们后面调用之前Aidl中定义的相应方法所需要的对象
。
public class AidlActivity extends Activity implements View.OnClickListener {
private ServiceConnectionImpl sci;
private StudentAidl student;
/**
* 发起通信
*/
private Button mBtStart;
/**
* 绑定Server
*/
private Button mBtBind;
/**
* 解除Server绑定
*/
private Button mBtUnBind;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
initView();
}
public void initView() {
mBtStart = (Button) findViewById(R.id.bt_start);
mBtStart.setOnClickListener(this);
mBtBind = (Button) findViewById(R.id.bt_bind);
mBtBind.setOnClickListener(this);
mBtUnBind = (Button) findViewById(R.id.bt_unBind);
mBtUnBind.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_start:
queryOnClick();
break;
case R.id.bt_bind:
onBind();
break;
case R.id.bt_unBind:
unbindService();
break;
}
}
/**
* onBind() 后执行onCreate onBind
* 后再绑定不在再执行
*/
private void onBind() {
Intent intent = new Intent("com.hongliang.demo.StudentService");//Service所设置的action
intent.setPackage("com.hongliang.demo");Service所在的package名称
sci = new ServiceConnectionImpl();
bindService(intent, sci, Service.BIND_AUTO_CREATE);
}
class ServiceConnectionImpl implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//将其转换为service实现类的对象
student = StudentAidl.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
student = null;
}
}
/**
* unbindService() 后执行onUnbind onDestroy
*/
private void unbindService() {
if (sci != null) {
unbindService(sci);
sci = null;
}
}
public void queryOnClick() {
String name;
try {
if (student == null) {
return;
}
name = student.getStudent(1);
Toast.makeText(this, "sname" + name, Toast.LENGTH_SHORT).show();
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService();
}
分析Aidl生成的Java文件
- IInterface 类如下,返回一个IBinder, IBinder是一个接口,Binder类实现IBinder 接口
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
public class Binder implements IBinder {
}
查看首先我们看整个类的大结构,我们会返现整个StudentAidl接口外层只有三个东西,
- StudentAidl是一个接口并继承了IInterface接口,IInterface里面有一个asBinder方法,返回一个IBinder
该接口里面有一个静态抽象类Stub
,并继承了Binder
类,实现了外层的StudentAidl接口
,那么继承Stub类就必须实现StudentAidl类的抽象方法getStudent
- 系统生成的方法basicTypes和getStudent方法。
**接下来我们看看 静态抽象类Stub
**
4. 该类实现了StudentAidl继承于IInterface的接口asBinder;
5. 该类同时提供了一个可以将Binder转化为StudentAidl实现类的方法,asInterface。
6. 该类继承了android.os.Binder类,重写了Binder类的onTransact方法。该方法的作用是,接收客户端发来的请求,并将请求分发至各个接口。
7. 该类中又自定义了一个静态类Proxy,实现了IMyService。该类在asInterface中被调用。
8. 该类为每个接口方法定义了一个整型的唯一标识。
再看Proxy
类,该类的作用是作为一个代理,将接受到的请求转发给服务器
9. 该类实现了IMyService继承自IInterface的方法asBinder
10. 该类的构造函数,以Binder作为传参,保存为成员变量。
11. 该类实现了IMyService中定义的两个接口
具体的调用时机是,在client端中返回IBinder的时候,会调用asInterface
,在这个地方会在本地查询返回Interface
,如果不为空且为当前接口,则为同一进程那么就返回Stub
,否则Stub.Proxy
,返回结果将作为Service实现类的实例
public interface StudentAidl extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.hongliang.demo.StudentAidl {
private static final java.lang.String DESCRIPTOR = "com.hongliang.demo.StudentAidl";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.hongliang.demo.StudentAidl interface,
* generating a proxy if needed.
*/
public static com.hongliang.demo.StudentAidl asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.hongliang.demo.StudentAidl))) {
return ((com.hongliang.demo.StudentAidl) iin);
}
return new com.hongliang.demo.StudentAidl.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
//MyBinder继承了Binder类同时实现了IEthan接口中的方法。同时在onTransact中得到客户端发过来的数据,
// 并且解析,然后调用实现getAdd()和getminus();为什么要在onTransact执行方法,因为客户端拿到的是一个IBinder接口对象,
// 会调用IBinder中的Transact()方法,由于Transact()方法被IBinder的实现类重写了,
// 根据多态,客户端接口对象会调用Binder(实现类)中的Transact()方法,
// 而Binder类中的Transact()内部又会调用onTransact(),因此最终会执行到onTransact()方法中。
// 2 onTransact 根据标识调用对应的方法
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
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_getStudent: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.getStudent(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//在Aidl跨进程通信,则调用`Proxy`的对应方法,根据`transact`方法将信息传递到服务端,此时客户端挂起等待响应
private static class Proxy implements com.hongliang.demo.StudentAidl {
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);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.lang.String getStudent(int no) 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.writeInt(no);
mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* 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 getStudent(int no) throws android.os.RemoteException;
}
在这个例子中,由于我将Service在注册的时候设置成了跨进程启动,那整个流程
是什么样的呢
- 客户端调用bindService绑定服务时,将触发Service的onBind监听方法。该方法将调用asBinder方法,返回一个Binder对象。
- 客户端将通过onServiceConnected回调函数,获取到该Binder对象(以传参的形式传入)。
- 客户端获取到Binder对象后,可调用stub.asInterface方法,将其转换为service实现类的对象。
- 在asInterface方法中,将判断service与当前进程,是否在同一进程中。若是,则返回stub本身,否则返回stub.proxy。返回结果将作为Service实现类的实例。
- 在通过Service实现类的实例调用接口方法时,若为同一进程,则直接调用方法本身。若为跨进程,则调用stub.proxy的对应接口方法,通过Transact方法将信息传送到服务端。此时,客户端将挂起,等待结果返回。
- 服务端接收到信息,通过onTransact()方法,根据方法的唯一标识,将信息转发至各对应方法。
- 信息处理完成后,再由服务端onTransact返回结果。
在整个分析中还有没有实际去分析的和理解的
- bindService注册的时候是怎么注册的,又怎么回传一个IBinder对象的
- 跨进程中stub.proxy中调用接口方法mRemote.transact的时候是这么传递到服务端的
- server处理后怎么回传到onTransact又怎么返回到client的
- Adil中使用了代理模式,代理模式的理解,为什么Aidl使用代理模式
server告诉你我提供哪些方法供你使用,可是server在onBinder()方法返回一个对象的时候,返回的是一个IBinder,这个对象并不能直接调用server提供的方法,所以Aidl引入了代理模式,重新封装的Proxy代理了server所有的方法。当在多进程调用的时候,那么Proxy中原先保存的原始IBinder就可以调用mRemote.transact方法,经过底层Binder的实现就将消息传递到Server,Server根据方法的code处理相应的逻辑,然后再经过底层onTransact将数据reply并写入到Bindle,Bindle返回并唤醒Client
后面有更加深入的理解再添加到之中来!
从android aidl理解Proxy/stub模式
Binder与AIDL的关系
Android应用程序绑定服务(bindService)的过程源代码分析
IPC(一)利用纯Binder通信(非aidl)
Binder学习指南
Android Binder之应用层总结与分析