Android中Binder与AIDL之间的关系

Android中Binder与AIDL之间的关系

Binder

  • Android中特有的一种进程间通信方式,一种虚拟的物理设备,设备驱动是/dev/binder
  • Android中实现了IBinder接口的一个类
  • ServiceManager连接各种Manager(ActivityManager、WindowManager等等)和相应ManagerService的桥梁
  • 应用层客户端和服务端进行通信的媒介

Android开发中,Binder主要用在Service中包括AIDL和Messager,当客户端bindService时服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据

AIDL

AIDL全称Android接口描述语言,是Android开发工具中提供的一种文件格式,通过在文件中预先定义接口,然后快速生成Binder机制的代码,省去了手动编写Binder机制

AIDL通信流程

使用AIDL来实现进程间通信分为服务端和客户端两个方面:

  • 服务端
    服务端需要创建Service用来监听客户端请求,然后创建AIDL文件声明暴露给客户端的接口,最后在Serive中实现这个AIDL接口
  • 客户端
    客户端需要先绑定Service,绑定成功后将服务端返回的Binder对象转换成AIDL接口所属的类型,然后调用AIDL中的方法

通过AIDL生成Binder机制文件

在客户端新建一个AIDL文件后默认会有一个basicTypes函数(基本示例,演示可以用在AIDL中作为参数和返回值的一些基本类型,这里删除),添加一个login函数

// Test.aidl
package com.gavinandre.aidl;

interface Test {
    void login(String userName, String pwd);
}

rebuild后会在build/generated/source/aidl/debug/${packageName}/目录下生成同名的java文件,下面代码就是系统生成的,只是为了方便查看稍微做了一下格式和顺序调整

package com.gavinandre.aidl;
/**
 * 根据Test.aidl生成的接口
 */
public interface Test extends android.os.IInterface {
    /**
     * 声明login接口
     */
    public void login(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException;

    /**
     * Stub类继承自Binder,并且实现了test接口
     */
    public static abstract class Stub extends android.os.Binder implements com.gavinandre.aidl.Test {
        private static final java.lang.String DESCRIPTOR = "com.gavinandre.aidl.Test";

        /**
         * 用于标识在transact过程中客户端所请求的到底是哪个方法
         */
        static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * 将Binder转换为com.gavinandre.aidl.Test接口(同进程),
         * 或者包装为一个Proxy(不同进程)
         */
        public static com.gavinandre.aidl.Test asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.gavinandre.aidl.Test))) {
                return ((com.gavinandre.aidl.Test) iin);
            }
            return new com.gavinandre.aidl.Test.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;
                }

                /**
                 * 执行login函数时提交给Binder的数据
                 */
                case TRANSACTION_login: {
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    this.login(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        /**
         * 不同进程会创建本地代理,通过Binder与服务端的对象进行交互
         */
        private static class Proxy implements com.gavinandre.aidl.Test {
            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;
            }

            /**
             * 实现login接口
             */
            @Override
            public void login(java.lang.String userName, java.lang.String pwd) 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(userName);
                    _data.writeString(pwd);
                    mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
    }
}

Binder机制分析

通过AIDL自动生成的这个类看起来逻辑混乱,但是实际上其实很清晰,通过它可以清楚的了解到Binder的工作机制,下面代码我做了一些精简:

package com.gavinandre.aidl;
/**
 * 根据Test.aidl生成的接口
 */
public interface Test extends android.os.IInterface {
    /**
     * 声明login接口
     */
    public void login(java.lang.String userName, java.lang.String pwd) throws android.os.RemoteException;

    /**
     * Stub类继承自Binder,并且实现了test接口
     */
    public static abstract class Stub extends android.os.Binder implements com.gavinandre.aidl.Test {
        
        //省略...

        /**
         * 用于标识在transact过程中客户端所请求的到底是哪个方法
         */
        static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        /**
         * 将Binder转换为com.gavinandre.aidl.Test接口(同进程),
         * 或者包装为一个Proxy(不同进程)
         */
        public static com.gavinandre.aidl.Test asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.gavinandre.aidl.Test))) {
                return ((com.gavinandre.aidl.Test) iin);
            }
            return new com.gavinandre.aidl.Test.Stub.Proxy(obj);
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            //省略...
            switch (code) {
                //省略...
                /**
                 * 执行login函数时提交给Binder的数据
                 */
                case TRANSACTION_login: {
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    this.login(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
                //省略...
            }
        }

        /**
         * 不同进程会创建本地代理,通过Binder与服务端的对象进行交互
         */
        private static class Proxy implements com.gavinandre.aidl.Test {
            private android.os.IBinder mRemote;

            //省略...

            /**
             * 实现login接口
             */
            @Override
            public void login(java.lang.String userName, java.lang.String pwd) 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(userName);
                    _data.writeString(pwd);
                    mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
    }
}

首先声明了login方法,显然就是Test.aidl中声明的方法。同时还声明了一个整型id(TRANSACTION_login)来标识这个方法,这个id用于标识在transact过程中客户端所请求的到底是哪个方法。然后可以看到一个内部类Stub继承于Binder并且实现了Test接口,当客户端和服务端都处于同一个进程时,login方法调用不会走跨进程的transact,而当处于不同进程时才会走transact过程,这个逻辑由asInterface方法与内部代理类Proxy来完成

由此可见这个接口的核心就是它的内部类Stub和Stub的内部类Proxy,下面来详细介绍下这两个类内的重要方法:

  • DESCRIPTOR
    Binder的唯一标识,一般用当前Binder的类名表示
    private static final java.lang.String DESCRIPTOR = "com.gavinandre.aidl.Test";
    
  • asInterface(android.os.IBinder obj)
    用于将服务端的Binder对象转换成客户端所需的AIDL接口类型对象,如果客户端和服务端位于同一进程则返回服务端Stub对象本身,如果不同进程就返回系统封装后的Stub.proxy对象
    /**
     * 将Binder转换为com.gavinandre.aidl.Test接口(同进程),
     * 或者包装为一个Proxy(不同进程)
     */
    public static com.gavinandre.aidl.Test asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.gavinandre.aidl.Test))) {
            return ((com.gavinandre.aidl.Test) iin);
        }
        return new com.gavinandre.aidl.Test.Stub.Proxy(obj);
    }
    
  • asBinder
    返回当前Binder对象
    @Override
    public android.os.IBinder asBinder() {
        return this;
    }
    
  • onTransact
    运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。服务端通过Stub中定义的整型id确定请求的目标方法,接着从data中取出目标方法的所需参数(如果有参数),然后执行目标方法。当目标方法执行完毕后向reply中写入返回值(如果有返回值)。
    如果onTransact方法返回false,那么客户端的请求就会失败,因此可以用这个特性来做权限验证
    @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;
            }
    
            /**
             * 执行login函数时提交给Binder的数据
             */
            case TRANSACTION_login: {
                data.enforceInterface(descriptor);
                java.lang.String _arg0;
                _arg0 = data.readString();
                java.lang.String _arg1;
                _arg1 = data.readString();
                this.login(_arg0, _arg1);
                reply.writeNoException();
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }
    
  • Proxy#login
    客户端发起跨进程请求才会调用此方法,内部实现:首先创建输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象(如果有的话),然后把该方法的参数信息写入_data中(如果有参数),接着调用transace方法来发起RPC(远程过程调用)请求,同时挂起当前线程,然后服务端的onTransact方法会被调用到,直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果,最后将数据放入返回值对象中返回数据
    /**
     * 实现login接口
     */
    @Override
    public void login(java.lang.String userName, java.lang.String pwd) 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(userName);
            _data.writeString(pwd);
            mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }
    

AIDL支持的数据类型

  • 基本数据类型 int、long、char、boolean、double等,只支持定向tag in
  • String和CharSequence,只支持定向tag in
  • List:其中元素必须是aidl支持的数据类型,接收方得到的总是ArrayList
  • Map:其中元素必须是aidl支持的数据类型,接收方得到的总是HashMap
  • Parcelable:所有实现了Parcelable接口的对象
  • AIDL:所有的AIDL接口本身也可以在AIDL文件中使用

以上6种数据类型就是AIDL所支持的所有类型,其中自定义的Parcelable对象和AIDL对象必须要显式import进来,不管是否在同一个包内,如果是Parcelable对象的话必须新建一个同名的AIDL文件,并在其中声明为Parcel类型。

AIDL中除了基本数据类型,其他类型的参数必须标上方向:in(客户端到服务端)、out(服务端到客户端)或者inout(双向),要根据实际需要指定参数类型,不能一概使用out或者inout,因为这在底层实现是有开销的

AIDL接口中只支持方法,不支持声明静态常量

AIDL接口的管理方式

RemoteCallbackList是系统专门提供用来删除跨进程listener接口的,当客户端进程终止后,它能够自动移除客户端所注册的listener,它内部还自动实现了线程同步功能

RemoteCallbackList是一个泛型,支持管理任意的AIDL接口(所有的AIDL接口都继承自IInterface接口),如下:

public class RemoteCallbackList<E extends IInterface>

它的内部有一个Map接口专门用来保存所有的AIDL回调,这个Map的Key是IBinder类型,value是Callback类型,如下:

ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();

其中CallBack封装了真正的远程listener。当客户端注册listener的时候,它会把这个listener的信息存入mCallbacks中,其中key和value分别通过下面的方式获得:

IBinder key = listener.asBinder();
Callback value = new Callback(listnener, cookie);

RemoteCallbackList的使用方式

//创建RemoteCallbackList
private RemoteCallbackList<IAidlListener> mListenerList = new RemoteCallbackList<IAidlListener>();
//添加aidl接口
mListenerList.register(listener);
//删除aidl接口
mListenerList.unregister(listener);

遍历RemoteCallbackList需要用下面的方式进行,beginBroadcast和finishBroadcast必须要配对使用,无论仅仅是获取RemoteCallbackList中元素的个数

final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
    IAidlListener l = mListenerList.getBroadcastItem(i);
    if(l != null) {
        //TODO
    }
}
mListenerList.finishBroadcast();

Binder死亡代理

Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,会导致客户端到服务端的Binder连接断裂(称之为Binder死亡),远程调用就会失败

想要解决这个问题就要使用DeathRecipient,DeathRecipient是系统提供用来监听Binder连接断开的一个接口,它是一个接口内部只有一个方法binderDied,当binder连接断开时就会回调binderDied,然后就可以调用unlinkToDeath移除之前绑定的binder死亡代理

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        binder.unlinkToDeath(mDeathRecipient, 0);
        // TODO:这里重新绑定远程Service
    }
};

在客户端绑定远程服务成功后,给binder设置死亡代理

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName className, IBinder service) {
        service.linkToDeath(mDeathRecipient, 0);
    }
}

还有用Binder的isBinderAlive也可以判断Binder是否死亡

AIDL使用实例

完整使用实例:

https://blog.csdn.net/lj402159806/article/details/85095699

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值