美文网首页
Android AIDL 讲解

Android AIDL 讲解

作者: 付凯强 | 来源:发表于2022-06-14 12:37 被阅读0次

    一步一步的,带着疑问,用示例讲解aidl文件。

    创建生成aidl文件

    路径:src/main/aidl/com/example/aidl/IMyAidlInterface.aidl

    package com.example.aidl;
    
    interface IMyAidlInterface {
        void test(in int a);
    }
    

    编译生成java文件:

    /*
     * This file is auto-generated.  DO NOT MODIFY.
     * Original file: /media/fukaiqiang/Disk960/AndroidProject/Test_AIDL/app/src/main/aidl/com/example/aidl/IMyAidlInterface.aidl
     */
    package com.example.aidl;
    
    public interface IMyAidlInterface extends android.os.IInterface {
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.example.aidl.IMyAidlInterface {
            private static final java.lang.String DESCRIPTOR = "com.example.aidl.IMyAidlInterface";
    
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * Cast an IBinder object into an com.example.aidl.IMyAidlInterface interface,
             * generating a proxy if needed.
             */
            public static com.example.aidl.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.aidl.IMyAidlInterface))) {
                    return ((com.example.aidl.IMyAidlInterface) iin);
                }
                return new com.example.aidl.IMyAidlInterface.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_test: {
                        data.enforceInterface(descriptor);
                        int _arg0;
                        _arg0 = data.readInt();
                        int _result = this.test(_arg0);
                        reply.writeNoException();
                        reply.writeInt(_result);
                        return true;
                    }
                    default: {
                        return super.onTransact(code, data, reply, flags);
                    }
                }
            }
    
            private static class Proxy implements com.example.aidl.IMyAidlInterface {
                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 int test(int a) 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(a);
                        mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
                        _reply.readException();
                        _result = _reply.readInt();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
            }
    
            static final int TRANSACTION_test = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        }
    
        public int test(int a) throws android.os.RemoteException;
    }
    
    

    结构分析

    package com.example.aidl;
    
    public interface IMyAidlInterface extends android.os.IInterface {
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.example.aidl.IMyAidlInterface {
        }
    
        public void test(int a) throws android.os.RemoteException; 
    }
    
    1. aidl生成的java文件依然是一个接口文件,它继承了android.os.IInterface,并创建了一个名为Stub的内部抽象类,原来的test方法抛出一个RemoteException异常。
    2. 先看IInterface的接口文件,内容如下:
    package android.os;
    
    /**
     * Base class for Binder interfaces.  When defining a new interface,
     * you must derive it from IInterface.
     */
    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();
    }
    

    通过注释了解到,当创建一个新的aidl接口文件的时候,你就需要继承IInterface,并让与IMyAidlInterface关联的Binder对象所在类实现asBinder方法,并返回this,即返回这个Binder对象。所以一个普通类如果实现IMyAidlInterface,需要实现两个方法,一个方法是test,一个方法是asBinder,asBinder这个时候返回null即可,因为根本不需要通过binder进行通信;如果一个继承了Binder的类实现IMyAidlInterface,同样需要实现上面两个方法,而且asBinder需要返回这个Binder对象。
    普通类:

    public class TestAidl implements IMyAidlInterface{
        @Override
        public void test(int a) throws RemoteException {
    
        }
        @Override
        public IBinder asBinder() {
            return null;
        }
    }
    

    继承了Binder的类

    class InterTestAild extends Binder implements IMyAidlInterface{
        @Override
        public void test(int a) throws RemoteException {
    
        }
        @Override
        public IBinder asBinder() {
            return this;
        }
    }
    
    1. 现在看内部抽象类Stub(强调Stub是一个抽象类,必须由其他类继承使用),这个类继承了Binder,并实现了IMyAidlInterface接口,为了更加方便的看清内部类的结构,截图如下:

      让我们分析下内部类的结构:
      首先:定义了一个静态常量DESCRIPTOR,其值由包名加接口名组成。
      其次:在构造方法中调用了attachInterface方法,并传入了两个参数分别是this对象和静态常量DESCRIPTOR。这里的this对象指的是继承了Stub的类的对象。
      接着:定义了一个静态方法asInterface,其参数的类型是IBinder接口类型,即实参必须是实现了IBinder接口的对象。
      继续:如分析2,实现了接口IInterface的类,需要实现asBinder方法,并返回其对象this。
      然后:实现了onTransact方法,一共4个参数,分别是int类型的code,Parcel类型的data,Parcel类型的reply,int类型的flags。
      接着:定义了一个静态内部类Proxy,并实现了IMyAidlInterface接口。
      最后:定义一个静态常量TRANSACTION_test,其名称组成是TRANSACTION + 下划线 + 方法名,其值是常量FIRST_CALL_TRANSACTION + 0,FIRST_CALL_TRANSACTION = 0x00000001。

    详细分析

    首先看attachInterface方法。这里涉及到了this,所以需要改写下InterTestAild类,如下:

    class InterTestAild extends IMyAidlInterface.Stub {
        @Override
        public void test(int a) throws RemoteException {
    
        }
        @Override
        public IBinder asBinder() {
            return this;
        }
    }
    

    让InterTestAild由继承Binder,改为继承IMyAidlInterface.Stub。所以当创建InterTestAild对象的时候,就会调用IMyAidlInterface.Stub类的构造方法,并调用attachInterface方法。这里的this即InterTestAild对象。

        public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
            mOwner = owner;
            mDescriptor = descriptor;
        }
    

    所以这里的mOwner = InterTestAild对象,mDescriptor即为常量DESCRIPTOR。
    接着分析asInterface方法:

            /**
             * Cast an IBinder object into an com.example.aidl.IMyAidlInterface interface,
             * generating a proxy if needed.
             */
            public static com.example.aidl.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.aidl.IMyAidlInterface))) {
                    return ((com.example.aidl.IMyAidlInterface) iin);
                }
                return new com.example.aidl.IMyAidlInterface.Stub.Proxy(obj);
            }
    

    通过注释,了解到这个方法的作用:将接口IBinder类型转换为IMyAidlInterface接口,如果需要,创建IMyAidlInterface.Stub.Proxy类型的对象。大体逻辑,如果obj == null直接返回,如果iin == null,那就创建com.example.aidl.IMyAidlInterface.Stub.Proxy类的对象,否则强转为IMyAidlInterface类型。
    核心的地方就是queryLocalInterface方法,它是在IBinder中定义的。这个时候你要注意了,用AndroidStudio做的跳转是错误的。如果你用AndroidStudio跳转,只会跳转到Binder类的queryLocalInterface方法。如下:

        /**
         * Use information supplied to attachInterface() to return the
         * associated IInterface if it matches the requested
         * descriptor.
         */
        public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
            if (mDescriptor != null && mDescriptor.equals(descriptor)) {
                return mOwner;
            }
            return null;
        }
    

    但其实不止Binder类实现了IBinder的queryLocalInterface方法,还有BinderProxy类也实现了IBinder的queryLocalInterface方法(我是怎么发现的,这个后面说)。

        /**
         * Retrieve a local interface - always null in case of a proxy
         */
        public IInterface queryLocalInterface(String descriptor) {
            return null;
        }
    

    通过注释你会发现BinderProxy中的queryLocalInterface总是会返回null。也就是说如果obj的类型是BinderProxy,那么会返回null,并创建com.example.aidl.IMyAidlInterface.Stub.Proxy类的对象,如果obj的类型是Binder,那么会返回attachInterface方法存储的继承了Binder类的对象(前面有说,这里不详细说)。所以关键就是obj到底是什么类型(后面会说何时是Binder类型,何时是BinderProxy类型)。
    下面分析onTransact方法。

            @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_test: {
                        data.enforceInterface(descriptor);
                        int _arg0;
                        _arg0 = data.readInt();
                        this.test(_arg0);
                        reply.writeNoException();
                        return true;
                    }
                    default: {
                        return super.onTransact(code, data, reply, flags);
                    }
                }
            }
    

    看到这里,第一反应就是一个aidl接口的方法对应一个code,根据code调用这个方法,方法和code是一一对应的关系。以下是执行这个方法的地方。

     this.test(_arg0);
    

    通过data和reply调用的方法,了解到从data中读取数据,把数据写入reply,即data用来传递数据过来,reply用来回调处理后的信息。
    最后看Proxy内部类的内部类.

            private static class Proxy implements com.example.aidl.IMyAidlInterface {
                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 test(int a) 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(a);
                        mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
                        _reply.readException();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                }
            }
    

    类Proxy也实现了IMyAidlInterface,自然也有一个test方法。再看构造函数,把传入的IBinder接口类型的对象存储到mRemote变量中。getInterfaceDescriptor方法返回DESCRIPTOR常量。test方法里面,定义了_data和_reply,并把数据写入到_data,从reply中读取数据,并调用了transact方法,注意它是IBinder中定义的,在Binder和BinderProxy中都有实现,所以mRemote是Binder类型,还是BinderProxy类型,调用的transact方法是有区别的,一共有4个参数,分别是code,data,reply,flags。看到这里,你会发现test中的工作和onTransact有关联,前者负责数据的发送,后者负责数据的读取和回调,前者负责回调的读取。也就是说执行Proxy的test方法,最终会调到Stub的onTransact方法。那Proxy的test方法何时执行呢?test方法执行,首先依赖Proxy对象的创建,那最终还是回到了asInterface方法。让我们用示例调试的方法看看。

    示例进行信息挖掘

    以Activity和Service进行数据传递的方式进行信息挖掘。

    public class AidlService extends Service {
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new InterTestAild();
        }
    
        static class InterTestAild extends IMyAidlInterface.Stub {
            private static final String TAG = "InterTestAild";
    
            @Override
            public void test(int a) throws RemoteException {
                Log.d(TAG, "InterTestAild test a = " + a);
            }
    
            @Override
            public IBinder asBinder() {
                return this;
            }
        }
    }
    
        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG, "onServiceConnected: " + service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            bindService(new Intent(this,AidlService.class),mServiceConnection,BIND_AUTO_CREATE);
        }
    
            <service android:name=".AidlService"/>
    

    创建AidlService,InterTestAild作为内部类存在。在Activity里面bindService,接收回调,获取 InterTestAild的类对象。看看这个时候对象的类型:

    2022-06-13 14:39:06.199 29443-29443/com.example.aidl D/MainActivity: onServiceConnected: com.example.aidl.AidlService$InterTestAild@d9f0308
    

    实现了IBinder接口的对象的类型是com.example.aidl.AidlService$InterTestAild,也就是说这个时候asInterface传入的类型是com.example.aidl.AidlService$InterTestAild。此时InterTestAild和activity是在同一个进程中。假如在不同的进程中呢?

    <service android:name=".AidlService" android:process=":remote"/>
    
    2022-06-13 14:40:06.190 29638-29638/com.example.aidl D/MainActivity: onServiceConnected: android.os.BinderProxy@4c430ab
    

    实现了IBinder接口的对象的类型是android.os.BinderProxy。也就说在同一个进程中,实现IBinder接口的对象的类型是"com.example.aidl.AidlService$InterTestAild",不同进程是android.os.BinderProxy。
    所以这个时候我们就可以推断到asInterface方法的参数类型,当同一个进程的时候是com.example.aidl.AidlService$InterTestAild,不同进程的时候android.os.BinderProxy。进而推断出,不同进程的时候会创建com.example.aidl.IMyAidlInterface.Stub.Proxy的对象。再推断出,当相同进程的时候,调用test方法,会回调IMyAidlInterface接口类的test方法,当不同进程的时候,会调用Proxy的test方法。
    所以调用test方法的代码,可以这样写,如下:
    当在一个进程的时候:

        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG, "onServiceConnected: " + service);
                //same process
                try {
                    ((AidlService.InterTestAild)service).test(5);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
    

    当在一个进程或者不同进程的时候:

        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG, "onServiceConnected: " + service);
                //same process and different process
                try {
                    IMyAidlInterface.Stub.asInterface(service).test(5);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
    

    总结

    1. Stub是用来实现功能的类的父类,而Proxy是提供给外部进程进行功能调用的类。
    2. 我们把继承了Binder类的类成为Binder类,其对象成为Binder对象。
    3. 当我们需要创建一个Binder对象的时候,只需要让类继承Stub即可。
    4. 一个接口方法对应一个code,按顺序,从1开始排序。
    5. aidl 文件的存在其实就是为我们提供了不差别的访问binder对象的方法,让我们不用关心binder对象和获取动作是在一个进程,还是在不同的进程。
    6. binder进行数据的传递,需要binder对象,方法名,参数,异步还是同步4个要素。
    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
    final IActivityManager am = IActivityManager.Stub.asInterface(b);
    

    你现在再看看这段代码,是不是比之前要明白许多了!、
    到此aidl的分析完毕。

    建议:

    学习binder,关注核心通信逻辑,忽视面向对象的业务逻辑,忽视术语(本地,远程,对端等等)。

    相关文章

      网友评论

          本文标题:Android AIDL 讲解

          本文链接:https://www.haomeiwen.com/subject/qxzpurtx.html