AIDL 安卓进程间通信/跨应用通信

文章目录

前言

最近出去面试找工作,被人问到 AIDL,我就回答这个东西我用过,也大概理解,Android 的进程间通信语言嘛
人家不置可否,那我能咋着呢,毕竟没深入研究过,也没办法,咱只能回来奋发图强了

写在前面

我以前就看过的一个博客,里面原理代码什么都有,写的水平肯定比我高

首先字面解释

A=Android IDL=Interface definition language 意译就是 android 接口定义语言,马丹,完全看不明白 算了,就是 Android 官方给我们定义出来跨进程,甚至跨应用通信用的

开发平台

Android Studio 2.2+Android 手机一部

新建工程

这个就不说了,跳过 就是新建工程后再建一个 module 也是 android app,功能后面再说

aidl 语法

这里请去看我写在前面,里面比较详细,或者自行 baidu/google,我也了解的不多

代码示例

最关键的地方到了

图片

其实就是新建一个 aidl 文件

1// IMyAidlInterface.aidl
2package com.kikt.aidl;
3
4// Declare any non-default types here with import statements
5
6interface IMyAidlInterface {
7
8    void test(int sum,int sum2);
9}

接着 make project,生成下 java 代码 找到生成的代码看下 我靠 好复杂,还是渣格式,这里格式化一下:

不想看完全代码的可以看下后面的结构图

  1/*
  2 * This file is auto-generated.  DO NOT MODIFY.
  3 * Original file: H:\\Git\\ASWorkSpace\\public\\AidlDemo\\app\\src\\main\\aidl\\com\\kikt\\aidl\\IMyAidlInterface.aidl
  4 */
  5package com.kikt.aidl;
  6// Declare any non-default types here with import statements
  7
  8public interface IMyAidlInterface extends android.os.IInterface {
  9    /**
 10     * Local-side IPC implementation stub class.
 11     */
 12    public static abstract class Stub extends android.os.Binder implements com.kikt.aidl.IMyAidlInterface {
 13        private static final java.lang.String DESCRIPTOR = "com.kikt.aidl.IMyAidlInterface";
 14
 15        /**
 16         * Construct the stub at attach it to the interface.
 17         */
 18        public Stub() {
 19            this.attachInterface(this, DESCRIPTOR);
 20        }
 21
 22        /**
 23         * Cast an IBinder object into an com.kikt.aidl.IMyAidlInterface interface,
 24         * generating a proxy if needed.
 25         */
 26        public static com.kikt.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
 27            if ((obj == null)) {
 28                return null;
 29            }
 30            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
 31            if (((iin != null) && (iin instanceof com.kikt.aidl.IMyAidlInterface))) {
 32                return ((com.kikt.aidl.IMyAidlInterface) iin);
 33            }
 34            return new com.kikt.aidl.IMyAidlInterface.Stub.Proxy(obj);
 35        }
 36
 37        @Override
 38        public android.os.IBinder asBinder() {
 39            return this;
 40        }
 41
 42        @Override
 43        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws
 44                android.os.RemoteException {
 45            switch (code) {
 46                case INTERFACE_TRANSACTION: {
 47                    reply.writeString(DESCRIPTOR);
 48                    return true;
 49                }
 50                case TRANSACTION_test: {
 51                    data.enforceInterface(DESCRIPTOR);
 52                    int _arg0;
 53                    _arg0 = data.readInt();
 54                    int _arg1;
 55                    _arg1 = data.readInt();
 56                    this.test(_arg0, _arg1);
 57                    reply.writeNoException();
 58                    return true;
 59                }
 60            }
 61            return super.onTransact(code, data, reply, flags);
 62        }
 63
 64        private static class Proxy implements com.kikt.aidl.IMyAidlInterface {
 65            private android.os.IBinder mRemote;
 66
 67            Proxy(android.os.IBinder remote) {
 68                mRemote = remote;
 69            }
 70
 71            @Override
 72            public android.os.IBinder asBinder() {
 73                return mRemote;
 74            }
 75
 76            public java.lang.String getInterfaceDescriptor() {
 77                return DESCRIPTOR;
 78            }
 79
 80            @Override
 81            public void test(int sum, int sum2) throws android.os.RemoteException {
 82                android.os.Parcel _data = android.os.Parcel.obtain();
 83                android.os.Parcel _reply = android.os.Parcel.obtain();
 84                try {
 85                    _data.writeInterfaceToken(DESCRIPTOR);
 86                    _data.writeInt(sum);
 87                    _data.writeInt(sum2);
 88                    mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
 89                    _reply.readException();
 90                } finally {
 91                    _reply.recycle();
 92                    _data.recycle();
 93                }
 94            }
 95        }
 96
 97        static final int TRANSACTION_test = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
 98    }
 99
100    public void test(int sum, int sum2) throws android.os.RemoteException;
101}

图片

图片

这里生成了一个静态内部类 Stub 方法签名如下

1public static abstract class Stub extends android.os.Binder implements com.kikt.aidl.IMyAidlInterface

这个类是抽象类 这里类本身实现了对应的接口 IMyAidlInterface,但没有实现我们在 aidl 中定义的方法 test(int,int)方法 这也代表了 test 方法需要我们自己来实现

另外方法继承了 android.os.Binder,看到这个的时候,我们就知道,这个应该用在什么地方了 对了,和 Service 必须实现的那个接口 onBind 的返回参数一样!

这里写个类继承下

 1package com.kikt.aidldemo;
 2
 3import android.app.Service;
 4import android.content.Intent;
 5import android.os.IBinder;
 6import android.os.RemoteException;
 7import android.util.Log;
 8
 9import com.kikt.aidl.IMyAidlInterface;
10
11public class MyService extends Service {
12    private static final String TAG = "MyService";
13    public MyService() {
14    }
15
16    @Override
17    public IBinder onBind(Intent intent) {
18       return new AidlEntity();
19    }
20
21    public class AidlEntity extends IMyAidlInterface.Stub{
22
23        @Override
24        public void test(int sum, int sum2) throws RemoteException {
25            Log.d(TAG, "test() called with: sum = [" + sum + "], sum2 = [" + sum2 + "]");
26        }
27    }
28}

为了方便,直接写在 service 内部 这里只是简单的做一个日志输出,其他什么也没有干

同应用调用

这里是同应用调用

直接看核心代码

 1        bindService(new Intent(this, MyService.class), new ServiceConnection() {
 2            @Override
 3            public void onServiceConnected(ComponentName name, IBinder service) {
 4                IMyAidlInterface test = IMyAidlInterface.Stub.asInterface(service);
 5                try {
 6                    test.test(1, 2);
 7                } catch (RemoteException e) {
 8                    e.printStackTrace();
 9                }
10            }
11
12            @Override
13            public void onServiceDisconnected(ComponentName name) {
14
15            }
16        }, BIND_AUTO_CREATE);

这里就是一个按钮点击后 bindService,然后 connection 会接受到一个 IBinder 的实例,这个实例就是上面 service 的那个

当然还有一个写法,直接强转也不会报错

 1    bindService(new Intent(this, MyService.class), new ServiceConnection() {
 2            @Override
 3            public void onServiceConnected(ComponentName name, IBinder service) {
 4//                IMyAidlInterface test = IMyAidlInterface.Stub.asInterface(service);
 5//                try {
 6//                    test.test(1, 2);
 7//                } catch (RemoteException e) {
 8//                    e.printStackTrace();
 9//                }
10                MyService.AidlEntity service1 = (MyService.AidlEntity) service;
11                try {
12                    service1.test(4,5);
13                } catch (RemoteException e) {
14                    e.printStackTrace();
15                }
16            }
17
18            @Override
19            public void onServiceDisconnected(ComponentName name) {
20
21            }
22        }, BIND_AUTO_CREATE);

效果都是一样的,那么这个什么 aidl 还有个毛用啊,强转就好了

接着就是高潮了

跨应用调用

没错,我个人认为最重要的也就是这点了,aidl 可以在不同应用中调用 比如我要做一个提供服务的应用,或者说是同公司的核心组件,而不希望别人知道我的具体实现 这时我直接丢给你我的 aidl 文件,告诉你包名和服务名称,你就去调就好了!

 1ComponentName componentName = new ComponentName(
 2                "com.kikt.aidldemo", "com.kikt.aidldemo.MyService");
 3        Intent intent = new Intent();
 4        intent.setComponent(componentName);
 5
 6        bindService(intent, new ServiceConnection() {
 7            @Override
 8            public void onServiceConnected(ComponentName name, IBinder service) {
 9                IMyAidlInterface iinterface = IMyAidlInterface.Stub.asInterface(service);
10                try {
11                    iinterface.test(10, 20);
12                } catch (RemoteException e) {
13                    e.printStackTrace();
14                }
15            }
16
17            @Override
18            public void onServiceDisconnected(ComponentName name) {
19
20            }
21        }, BIND_AUTO_CREATE);

这里和上面的代码看上去差不多,但是我和原来的不是一个应用,而是第二个应用 这里使用 test 方法时,可以看到提供服务的应用可以输出日志

而引申下 如果这里有一个算法,实现了两个参数间的计算,那么我们是不是可以得到计算结果呢,比如说一个加密方法,我不需要知道具体实现,只要调用就好了

aidl 修改如下:

 1// IMyAidlInterface.aidl
 2package com.kikt.aidl;
 3
 4// Declare any non-default types here with import statements
 5
 6interface IMyAidlInterface {
 7
 8    void test(int sum,int sum2);
 9
10    int add(int num,int num2);
11}

内部类实现修改

 1 public class AidlEntity extends IMyAidlInterface.Stub{
 2
 3        @Override
 4        public void test(int sum, int sum2) throws RemoteException {
 5            Log.d(TAG, "test() called with: sum = [" + sum + "], sum2 = [" + sum2 + "]");
 6        }
 7
 8        @Override
 9        public int add(int num, int num2) throws RemoteException {
10            return num + num2;
11        }
12    }

调用者应用的 bindService 中修改如下:

 1bindService(intent, new ServiceConnection() {
 2            @Override
 3            public void onServiceConnected(ComponentName name, IBinder service) {
 4                IMyAidlInterface iinterface = IMyAidlInterface.Stub.asInterface(service);
 5                try {
 6                    iinterface.test(10, 20);
 7                } catch (RemoteException e) {
 8                    e.printStackTrace();
 9                }
10
11                try {
12                    int add = iinterface.add(1, 2);
13                    Log.d("MainActivity", "add:" + add);
14                } catch (RemoteException e) {
15                    e.printStackTrace();
16                }
17            }
18
19            @Override
20            public void onServiceDisconnected(ComponentName name) {
21
22            }
23        }, BIND_AUTO_CREATE);

在第二个中点击可以看到日志输出

图片

思维发散

其实 aidl 就像一个核心,模块化开发的时候,核心模块不用给你代码,只要你去 aidl 文件,和跨应用的调用方法即可,而你实现后续的业务逻辑,也算是一种解耦的方式吧

高级/原理

再回到 Stub 的实现中,看下 onTransact 方法就可以看出,其实 aidl 是实现自 android 提供的序列化 通过约定的方式,将方法名的参数和返回值序列化后再通过约定的方式取出来,这样来实现进程间通信 当然具体的内部原理因为我对于 framework 层没有深入研究,传输的过程我不太了解

 1        @Override
 2        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws
 3                android.os.RemoteException {
 4            switch (code) {
 5                case INTERFACE_TRANSACTION: {
 6                    reply.writeString(DESCRIPTOR);
 7                    return true;
 8                }
 9                case TRANSACTION_test: {
10                    data.enforceInterface(DESCRIPTOR);
11                    int _arg0;
12                    _arg0 = data.readInt();
13                    int _arg1;
14                    _arg1 = data.readInt();
15                    this.test(_arg0, _arg1);
16                    reply.writeNoException();
17                    return true;
18                }
19                case TRANSACTION_add: {
20                    data.enforceInterface(DESCRIPTOR);
21                    int _arg0;
22                    _arg0 = data.readInt();
23                    int _arg1;
24                    _arg1 = data.readInt();
25                    int _result = this.add(_arg0, _arg1);
26                    reply.writeNoException();
27                    reply.writeInt(_result);
28                    return true;
29                }
30            }
31            return super.onTransact(code, data, reply, flags);
32        }

后记

其实 aidl 很方便也是一种跨应用的解决方案,非常实用,面试和工作中都应该用得到,后续如果有机会可以多使用下,就先写这么多吧,谢谢网上的 android 先驱者和大神们!!!
遨游在 android 知识的海洋中不可自拔的博主