当前位置: 代码迷 >> 综合 >> JAVA层Bidner——AIDL
  详细解决方案

JAVA层Bidner——AIDL

热度:12   发布时间:2023-12-17 19:16:48.0

JAVA层Bidner——AIDL

一、使用

??在定义好一个aidl文件并且编译过后,Android studio会自动生成一个aidl对应的java文件。比如定义一个IMyAidlInterface.aidl如下:

interface IMyAidlInterface {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);
}

??在编译过后会自动生成一个IMyAidlInterface.java文件。在该java文件中,有一个以IMyAidlInterface命名的interface接口:

public interface IMyAidlInterface extends android.os.IInterface {
    

??在这个接口域中,又定义了几个关键内部类,分别是IMyAidlInterface.Stub、IMyAidlInterface.Stub.Proxy()、IMyAidlInterface.Default。除开Default使用比较少外,stub和proxy便分别代表了这个Binder的服务端和客户端。

public static abstract class Stub extends android.os.Binder implements com.example.bindertest.IMyAidlInterface;private static class Proxy implements com.example.bindertest.IMyAidlInterface;

??需要注意的是,Stub是一个虚拟类。它有一些未实现的接口,这些接口全是业务接口,需要由用户自己在继承Stub类后自己去实现。体现在此例中,Stub未实现的接口便是IMyAidlInterface中的接口basicTypes。
??大致类关系图如下所示:
在这里插入图片描述

二、服务端Stub

??服务端便是Binder实现具体业务的地方,一般放在app的service中,当然,如果想要实现service对其余app的回调,那么app中也需要实现一个回调服务端,并把该回调服务端传给service,service拿到该回调的客户端进行回调。但回到服务端本身,一般想要实现自己的服务端时,都会选择将这个服务类继承stub类,然后在这个服务类中实现stub未实现的业务接口。比如:

public class MyAidlInterfaceImp extends IMyAidlInterface.Stub{
    @Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
    Log.i(this.getClass().getName(), "basicTypes: ");}
}

??MyAidlInterfaceImp便是我实现的服务端,service可以通过onBinder接口把该服务端的Binder传给其余app,经过底层一系列转换,app到手的时候就会获得一个Binder客户端,通过该客户端接口,便可以调用服务端的服务了。

public class MyAidlInterfaceService extends Service {
    public MyAidlInterfaceService() {
    }@Overridepublic IBinder onBind(Intent intent) {
    return new MyAidlInterfaceImp().asBinder();}
}

??粗浅的使用便是如此了,但IMyAidlInterface.Stub中又是怎么实现的呢。从UML图就可以看出,Stub继承了两个类,分别是android.os.Binder和com.example.bindertest.IMyAidlInterface。android.os.Bidner的职责是负责进行Binder通信,com.example.bindertest.IMyAidlInterface则是负责业务接口部分。这也体现了Binder设计的核心思想,无论是java层的Binder还是C++层的Binder都是如此,将一个Binder的客户端或者服务端分成两部分来看待,负责Binder通信的Binder类,负责业务接口调用以及实现的interface接口。值得注意的是,客户端IMyAidlInterface.Stub.Proxy中的Binder是作为成员变量存在的,通过继承于IMyAidlInterface的Proxy来代理它的成员变量mRemote(一个binder对象),这是一个典型的代理模式。而服务端的Stub则直接通过继承来同时实现了Binder和接口的功能。

??回到代码分析上来。Stub类总共有三个核心函数,分别是asInterface、asBinder、onTransact。

??看asBinder方法:

@Override public android.os.IBinder asBinder()
{
    return this;
}

??该方法很简单,就是返回了自己,并且返回时把自己降级成IBinder类型。但是使用上就有一些讲究了。该方法一般在进程间传递Binder时需要用到,就比如上面贴的MyAidlInterfaceService代码,在Service实现onBind方法时,会调用Stub的onBinder方法。我们知道Binder可以实现进程间传递信息,而且它还可以实现进程间传递Binder,由Binder来传递Binder,当然本质上,所谓的传递Binder,其实就是传递Binder驱动中的Binder索引号。但有一个前提,你想要实现进程间传递Binder,那必须有一个母体Binder来传递这个目标Binder。在该例中Service的onBind方法会把MyAidlInterfaceImp的IBinder传递给AMS,AMS再把这个IBinder传递给启动服务的APP(一般为另一个进程),该APP收到这个IBinder后,再通过asInterface方法把它转换为接口,至于AMS传递时所使用的Binder来自于哪,那就是分析AMS时需要分析的了。

??但可以知道的是,既然要传递一个Binder是需要一个母体Binder的,就像fork一个进程需要一个父进程,那么必然存在一个源头Binder,这个源头Binder不需要通过Binder传递便可以由某个进程获取。就像Linux有一个0号进程来fork出其余进程,Android apk有一个zygote进程来生成其他Android虚拟机,Binder也有一个0号Binder,在系统启动的初期用来传递其他Binder,这个0号Binder就由servicemanager来管理,我之所以叫它0号Binder,就是因为这个Binder在Binder驱动中的Binder索引便是0。

??既然前面提到APP在收到Service通过onBind方法传递的IBinder,会通过调用asInterface方法来将这个IBinder转换为业务接口,那我们就来看看这个asInterface方法。

public static com.example.bindertest.IMyAidlInterface asInterface(android.os.IBinder obj)
{
    if ((obj==null)) {
    return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.bindertest.IMyAidlInterface))) {
    return ((com.example.bindertest.IMyAidlInterface)iin);}return new com.example.bindertest.IMyAidlInterface.Stub.Proxy(obj);
}

??asInterface函数的主要功能通过传参和返回值就可以看出来了。它会将一个Binder对象转换为一个接口实例化对象,其实本质就是实例化了一个Proxy对象,并且会将从service那边传过来的IBinder通过Proxy构造传递进去。而Proxy本身就是继承与IMyAidlInterface,自然可以当作接口实例化对象来使用。

??再来看onTransact方法:

static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);@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;}default:{
    return super.onTransact(code, data, reply, flags);}}
}

??该方法也很简单,从data中读取各参数再调用自己的业务接口,如果业务接口有返回值,则会将返回值写入reply中,reply会作为返回值返回到客户端。onTransact方法本质上便是实现进程间通信与业务接口之间转换的。当客户端调用服务端方法时,Binder机制在经过一系列调用后,便会调用到onTransact,再由onTransact去调用服务端的业务实现。onTransact自身被Binder类调用,具体分析放在Binder类分析里写。在onTransact函数中有一个传参code,这个值是由客户端传过来的,其不同的值代表着不同的业务接口,看到这就很明显了,所谓的Binder跨进程调用,实现IPC通信,本质上就是客户端和服务端先提前规定好特定的枚举值(code),由这些值来代表不同的方法,客户端传入哪个code值,服务端就根据这个值去调用其对应的方法,至于方法调用时需要的传入值和返回值,则对应着onTransact传参中的data和reply,至于其类型Parcel,这是Binder机制中特殊的进程间通信类,它会将各种不同类型的值进行序列化,从而方便进程间数据传递。

三、客户端Proxy

??前面已经讲过,当客户端拿到服务端传过来的Binder后,会通过asInterface方法把IBinder转换为业务接口IMyAidlInterface,当然其本质就是Proxy,Proxy构造函数将这个IBinder传入到Proxy中。这个IBinder会被赋值给Proxy的成员变量mRemote。

Proxy(android.os.IBinder remote)
{
    mRemote = remote;
}

??而客户端在调用业务接口时,其中的代码如下:

@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();
}
}

??这个方法实现其实有点类似于Stub的onTransact,只不过一个是Binder进程间通信转换到业务接口,一个是业务接口转换到Binder进程间通信。Proxy自己的业务接口会调用到IBinder的transact方法,从而来实现进程间通信。

??Proxy类还有一个关键方法asBinder:

@Override public android.os.IBinder asBinder()
{
    return mRemote;
}

??和Stub一样,这一般也是在进程间传递Binder时需要用到,它会返回自己的mRemote成员变量。

  相关解决方案