Binder是什么?
在开始介绍AIDL之前,我们必须对Binder有一个初步的了解。
Binder就是Android用来跨进程通信的一种方式,这个涉及的知识点太多,我会单独开一篇讲述。
在这里,我们需要知道的是,既然涉及到跨进程通讯,就肯定会有两个不同的进程,以及一条连接两个进程的通道。
这两个进程中,提供服务的进程我们把它叫做服务端,享受服务的进程我们把它叫做客户端,连接服务端和客户端的通道有很多,binder就是其中之一。
AIDL是什么?
Android Interface Definition Language,即Android接口定义语言
可以理解为用来生成binder通信代码的脚本语言
AIDL该怎么用?
我会选用Android的Doze服务来分析AIDL的使用步骤。
涉及到的文件有以下三个:
如果把客户端比作一个想吃饭的人,服务端比作一家提供很多菜品餐厅,那么AIDL就是餐厅中的菜单,ServiceManager就是指向各个餐厅的地图
一、通道(Binder)
我们看下IDeviceIdleController.aidl的具体代码实现
package android.os;
import android.os.IMaintenanceActivityListener;
import android.os.UserHandle;
/** @hide */
interface IDeviceIdleController {
void addPowerSaveWhitelistApp(String name);
...
void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason);
}
这就是一个AIDL的标准写法,看起来就是这么枯燥无味且平淡,主要组成部分就是在interface中定义所有服务端向外提供的接口,就像是一家餐厅提供的菜单。
相信有些细心的同学,就会有些疑问,服务端的接口通过AIDL抛出来了,但是客户端怎么找到服务端呢?
这就涉及到Binder的核心实现ServiceManager,在本篇中不多介绍,后续会单开一章。
二、服务端
服务端作为服务的提供者,就像是一家餐厅,菜单上写出来的菜,硬着头皮也得烧出来。
我们看下服务端具体代码实现的关键代码
public class DeviceIdleController extends SystemService
implements AnyMotionDetector.DeviceIdleCallback {
BinderService mBinderService;
private final class BinderService extends IDeviceIdleController.Stub {
@Override public void addPowerSaveWhitelistApp(String name) {
if (DEBUG) {
Slog.i(TAG, "addPowerSaveWhitelistApp(name = " + name + ")");
}
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
long ident = Binder.clearCallingIdentity();
try {
addPowerSaveWhitelistAppInternal(name);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override public void addPowerSaveTempWhitelistApp(String packageName, long duration,
int userId, String reason) throws RemoteException {
addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason);
}
...
}
@Override
public void onStart() {
...
mBinderService = new BinderService();
publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
...
}
}
DeviceIdleController作为服务端,定义了一个内部类BinderService继承IDeviceIdleController.Stub,实现了IDeviceIdleController.aidl中定义的所有接口。(疑问1:IDeviceIdleController.Stub哪儿来的?)
服务端中的publishBinderService(),也通过调用ServiceManager的addService方法,将服务端注册到ServiceManager中。(疑问2:为什么BinderService能够被作为addService的参数)
三、客户端
如果把客户端看作是一个想要吃饭的人,那就需要以下几个步骤:找到餐厅,打开菜单,最后点菜吃饭。
我们来通过客户端代码的实例来具体分析以下这几个步骤
import android.os.IDeviceIdleController;
public class UsageStatsService extends SystemService implements
UserUsageStatsService.StatsUpdatedListener {
IDeviceIdleController mDeviceIdleController;
...
@Override
public void onBootPhase(int phase) {
...
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
...
}
@Override
public void whitelistAppTemporarily(String packageName, long duration, int userId)
throws RemoteException {
...
mDeviceIdleController.addPowerSaveTempWhitelistApp(packageName, duration, userId,
reason.toString());
}
...
}
首先,ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)这段代码就充当了找到餐厅这个步骤
其次,通过IDeviceIdleController.Stub.asInterface方法,就好比是将餐厅中的菜单打开
最后,mDeviceIdleController.addPowerSaveTempWhitelistApp就相当于点菜吃饭,这样一来,整个从客户端调用服务端的方法的过程就结束了。
AIDL到底怎么实现的?
看到这里,如果是只想要知道怎么用的同学肯定能够愉快的用起来了。但是,对于AIDL的实现逻辑还是一团雾水。
既然开头时候说到过,AIDL只是一个生成Binder实现的脚本语言,那么想要了解AIDL的实现逻辑,就必须从AIDL生成的java文件开始分析。在源码中,AIDL会被编译生成java文件放在out/soong目录下,感兴趣的同学可以通过find命令去目录下发掘以下。
话不多说,先贴上生成的IDeviceIdleController.java代码
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package android.os;
// Declare any non-default types here with import statements
public interface IDeviceIdleController extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.os.IDeviceIdleController
{
private static final java.lang.String DESCRIPTOR = "android.os.IDeviceIdleController";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an android.os.IDeviceIdleController interface,
* generating a proxy if needed.
*/
public static android.os.IDeviceIdleController asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.os.IDeviceIdleController))) {
return ((android.os.IDeviceIdleController)iin);
}
return new android.os.IDeviceIdleController.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_addPowerSaveTempWhitelistApp:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
long _arg1;
_arg1 = data.readLong();
int _arg2;
_arg2 = data.readInt();
java.lang.String _arg3;
_arg3 = data.readString();
this.addPowerSaveTempWhitelistApp(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements android.os.IDeviceIdleController
{
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 void addPowerSaveTempWhitelistApp(java.lang.String name, long duration, int userId, java.lang.String reason) 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.writeString(name);
_data.writeLong(duration);
_data.writeInt(userId);
_data.writeString(reason);
mRemote.transact(Stub.TRANSACTION_addPowerSaveTempWhitelistApp, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_addPowerSaveTempWhitelistApp = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void addPowerSaveTempWhitelistApp(java.lang.String name, long duration, int userId, java.lang.String reason) throws android.os.RemoteException;
...
}
我已经对IDeviceIdleController.java进行了删减,只留下了binder的通信流程以及addPowerSaveTempWhitelistApp的具体实现。
我们从最外层开始一步步剖析,IDeviceIdleController接口生成了一个stub的抽象类,以及服务端能够提供的方法,比如:addPowerSaveTempWhitelistApp。
这也就能解释上文中的第一个疑问,IDeviceIdleController.Stub是通过AIDL生成的。同时,stub类继承了Binder,所以,能够转化成IBinder对象,这也解释了第二个疑问,因为BinderService继承了stub类,也是能转化成IBinder对象的。
接下来,我们开始肢解stub类的具体实现,这也是binder通讯java层实现的关键。
(1)构造函数
构造函数中调用了attachInterface方法,这是父类Binder中的方法,用于将本接口与Binder相关联,在用户通过queryLocalInterface方法进行请求的时候,通过对比DESCRIPTOR,相同的话,返回当前接口
(2)客户端调用时使用的asInterface方法
我们能看到,asInterface中调用了queryLocalInterface方法,而在构造函数中,我们讲过,queryLocalInterface是获取接口对象的,但是前提是DESCRIPTOR相同。所以,当同进程通讯时,asInterface返回的是stub对象,而跨进程通讯时,返回的是Stub.Proxy对象。
(3)Proxy内部类
在上文中,我们知道了跨进程通讯时,我们获取到的是一个Proxy对象,那么,我们的调用也就发生在Proxy类中。
Proxy类中也实现了我们所需要调用的接口addPowerSaveTempWhitelistApp,但我们仔细观察addPowerSaveTempWhitelistApp的具体实现不难发现,他是通过transact方法,将需求抛出,至于服务端如何接到transact的请求并实现,就设计到Binder的底层实现,这部分我们以后再讲。