文章

AIDL基础

AIDL基础

AIDL

什么是 AIDL?

AIDL 是 Android Interface definition language 的缩写,它是一种 Android 内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。

为什么要设计出这么一门语言?

设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。

AIDL 用来解决 Android 的跨进程通信问题,底层原理是 Binder ,实现思路是 C/S 架构思想。

  • Server:接收请求,提供处理逻辑,并发送响应数据。
  • Client:发起请求,接收响应数据。

C/S 之间通过 Binder 对象进行通信。

  • Server 需要实现一个 Service 作为服务器,Client 侧则需要调用发起请求的能力。
  • Client 需要调用 bindService 绑定到远程服务,然后通过 ServiceConnection 来接收远程服务的 Binder 对象。拿到 Binder 对象后就可以调用远程服务中定义的方法了。

因为是跨进程通信,所以需要实现序列化,AIDL 专门为 Android 设计,所以它的序列化不能使用 Java 提供的 Serializable ,而是 Android 提供的 Parcelable 接口。

AIDL 语法?

基本上它的语法和 Java 是一样的。

文件类型

用 AIDL 书写的文件的后缀是 .aidl,而不是 .java。

数据类型

AIDL 默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下(在 Java 中,这种情况是不需要导包的)。

默认支持的数据类型包括:

  • Java 中的八种基本数据类型,包括 byte、short、int、long、float、double、boolean、char。
  • String 类型
  • CharSequence 类型
  • List 类型:List 中的所有元素必须是 AIDL 支持的类型之一,或者是一个其他 AIDL 生成的接口,或者是定义的 parcelable。List 可以使用泛型
  • Map 类型:Map 中的所有元素必须是 AIDL 支持的类型之一,或者是一个其他 AIDL 生成的接口,或者是定义的 parcelable。Map 是不支持泛型的。

定向 tag

AIDL 中的定向 tag 表示了在跨进程通信中数据的流向。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。

服务端到客户端数据流向不是返回值,而是传入参数对象服务端修改后会同步给客户端

Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in 。还有,请注意,请不要滥用定向 tag ,而是要根据需要选取合适的。

AIDL 中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

案例:

1
2
3
4
5
6
7
8
9
interface BookManager {
    //保证客户端与服务端是连接上的且数据传输正常
    List<Book> getBooks();

    //通过三种定位tag做对比试验,观察输出的结果
    Book addBookIn(in Book book);
    Book addBookOut(out Book book);
    Book addBookInout(inout Book book);
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public void addBookIn(View view) {
    //如果与服务端的连接处于未连接状态,则尝试连接
    if (!mBound) {
        attemptToBindService();
        Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
        return;
    }
    if (mBookManager == null) return;

    Book book = new Book();
    book.setName("APP研发录In");
    book.setPrice(30);
    try {
        //获得服务端执行方法的返回值,并打印输出
        Book returnBook = mBookManager.addBookIn(book);
        Log.e("hacket", "addBookIn returnBook: " + returnBook.toString()
                + "-->> book:" + book.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

public void addBookOut(View view) {
    if (!mBound) {
        attemptToBindService();
        Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
        return;
    }
    if (mBookManager == null) return;

    Book book = new Book();
    book.setName("APP研发录Out");
    book.setPrice(30);
    try {
        Book returnBook = mBookManager.addBookOut(book);
        Log.e("hacket", "addBookOut returnBook: " + returnBook.toString() + "-->> book:" + book.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

public void addBookInOut(View view) {
    if (!mBound) {
        attemptToBindService();
        Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();
        return;
    }
    if (mBookManager == null) return;

    Book book = new Book();
    book.setName("APP研发录InOut");
    book.setPrice(30);
    try {
        Book returnBook = mBookManager.addBookInout(book);
        Log.e("hacket", "addBookInOut returnBook: " + returnBook.toString() + "-->> book:" + book.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

结果:

1
2
3
4
5
6
7
8
E/hacket: invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333]
E/hacket: addBookIn returnBook: name : APP研发录In , price : 2333-->> book:name : APP研发录In , price : 30

E/hacket: invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333, name : null , price : 2333]
E/hacket: addBookOut returnBook: name : null , price : 2333-->> book:name : null , price : 2333

E/hacket: invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333, name : null , price : 2333, name : APP研发录InOut , price : 2333]
E/hacket: addBookInOut returnBook: name : APP研发录InOut , price : 2333-->> book:name : APP研发录InOut , price : 2333

原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    switch (code) {
        // ...
        case TRANSACTION_addBookIn: {
            data.enforceInterface(DESCRIPTOR);
            //很容易看出来,_arg0就是输入的book对象
            com.lypeer.ipcclient.Book _arg0;
            //从输入的_data流中读取book数据,并将其赋值给_arg0
            if ((0 != data.readInt())) {
                _arg0 = com.lypeer.ipcclient.Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            //在这里才是真正的开始执行实际的逻辑,调用服务端写好的实现
            this.addBookIn(_arg0);
            //执行完方法之后就结束了,没有针对_reply流的操作,所以客户端不会同步服务端的变化
            reply.writeNoException();
            return true;
        }
        case TRANSACTION_addBookOut: {
            data.enforceInterface(DESCRIPTOR);
            com.lypeer.ipcclient.Book _arg0;
            //可以看到,用out作为定向tag的方法里,根本没有从_data里读取book对象的操作,
            //而是直接new了一个book对象,这就是为什么服务端收不到客户端传过来的数据
            _arg0 = new com.lypeer.ipcclient.Book();
            //执行具体的事物逻辑
            this.addBookOut(_arg0);
            reply.writeNoException();
            //在这里,_arg0是方法的传入参数,故服务端的实现里对传参做出的任何修改,
            //都会在_arg0中有所体现,将其写入_reply流,就有了将这些修改传回客户端的前提
            if ((_arg0 != null)) {
                reply.writeInt(1);
                _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
        case TRANSACTION_addBookInout: {
            data.enforceInterface(DESCRIPTOR);
            com.lypeer.ipcclient.Book _arg0;
            //inout同样兼具上两个方法中的细节
            if ((0 != data.readInt())) {
                _arg0 = com.lypeer.ipcclient.Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            this.addBookInout(_arg0);
            reply.writeNoException();
            if ((_arg0 != null)) {
                reply.writeInt(1);
                _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.lypeer.ipcclient.BookManager {
    private android.os.IBinder mRemote;

    // ...
    //通过三种定位tag做对比试验,观察输出的结果
    @Override
    public void addBookIn(com.lypeer.ipcclient.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //可以看到,这里执行的操作很简单,仅仅是判断book是否为空,
            // 如果为空,则_data写入int值1,将其book写入_data中
            // 如果不为空,则_data写入int值0
            if ((book != null)) {
                _data.writeInt(1);
                book.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            //之后直接调用transact()方法,将方法的编码,
            // _data(包含从客户端流向服务端的book流),
            // _reply(包含从服务端流向客户端的数据流)传入
            mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }

    @Override
    public void addBookOut(com.lypeer.ipcclient.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //在定向tag为out的方法里,没有将book对象写入_data流的操作
            mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
            _reply.readException();
            //与tag为in的方法里面不同的是,在执行transact方法之后,
            //还有针对_reply的操作,并且将book赋值为_reply流中的数据
            if ((0 != _reply.readInt())) {
                book.readFromParcel(_reply);
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }

    @Override
    public void addBookInout(com.lypeer.ipcclient.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //定向tag为inout的方法里综合了上两个方法里的操作
            if ((book != null)) {
                _data.writeInt(1);
                book.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0);
            _reply.readException();
            if ((0 != _reply.readInt())) {
                book.readFromParcel(_reply);
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }
}

两种 AIDL 文件

  1. 一类是用来定义 parcelable 对象,以供其他 AIDL 文件使用 AIDL 中非默认支持的数据类型的。
  2. 一类是用来定义方法接口,以供系统使用来完成跨进程通信的

所有的非默认支持数据类型必须通过第一类 AIDL 文件定义才能被使用

第一类 AIDL:

1
2
3
4
5
6
7
8
// Book.aidl
//第一类AIDL文件的例子
//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是一样的
package me.hacket.ipcclient;

//注意parcelable是小写
parcelable Book;

第二类 AIDL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// BookManager.aidl
//第二类AIDL文件的例子
package me.hacket.ipcclient;
//导入所需要使用的非默认支持数据类型的包
import me.hacket.ipcclient.Book;

interface BookManager {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    List<Book> getBooks();
    Book getBook();
    int getBookCount();

    //传参时除了Java基本类型以及String,CharSequence之外的类型
    //都需要在前面加上定向tag,具体加什么量需而定
    void setBookPrice(in Book book , int price)
    void setBookName(in Book book , String name)
    void addBookIn(in Book book);
    void addBookOut(out Book book);
    void addBookInout(inout Book book);
}

在进行跨进程通信的时候,在 AIDL 中定义的方法里包含非默认支持的数据类型与否,我们要进行的操作是不一样的。如果不包含,那么我们只需要编写一个 AIDL 文件,如果包含,那么我们通常需要写 n+1 个 AIDL 文件( n 为非默认支持的数据类型的种类数)。

使用 AIDL

AIDL 基础

定义数据类实现 Parcelable 接口

由于不同的进程有着不同的内存区域,并且它们只能访问自己的那一块内存区域,所以我们不能像平时那样,传一个句柄过去就完事了。——句柄指向的是一个内存区域,现在目标进程根本不能访问源进程的内存,那把它传过去又有什么用呢?所以我们必须将要传输的数据转化为能够在内存之间流通的形式。 这个转化的过程就叫做序列化与反序列化。

简单来说是这样的:比如现在我们要将一个对象的数据从客户端传到服务端去,我们就可以在客户端对这个对象进行序列化的操作,将其中包含的数据转化为序列化流,然后将这个序列化流传输到服务端的内存中去,再在服务端对这个数据流进行反序列化的操作,从而还原其中包含的数据——通过这种方式,我们就达到了在一个进程中访问另一个进程的数据的目的。

AIDL 进行跨进程通信的时候,选择的序列化方式是 Parcelable

若 AIDL 文件中涉及到的所有数据类型均为默认支持的数据类型,不需要什么操作,因为默认支持的那些数据类型都是可序列化的;如果有自定义的类型,需要引入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class Book implements Parcelable{
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    private String name;
    private int price;
    public Book(){}

    public Book(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(price);
    }

    /**
     * 参数是一个Parcel,用它来存储与传输数据
     * @param dest
     */
    public void readFromParcel(Parcel dest) {
        // 注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        name = dest.readString();
        price = dest.readInt();
    }
}
  • 默认生成的模板类的对象只支持为 in 的定向 tag,只生成 writeToParcel,如果要支持为 out 或者 inout 的定向 tag 的话,还需要实现 readFromParcel() 方法
  • 可以用插件生成
AIDL 文件
1
2
3
4
5
6
7
8
// Book.aidl
//第一类AIDL文件
//这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用
//注意:Book.aidl与Book.java的包名应当是一样的
package me.hacket.ipcclient;

//注意parcelable是小写
parcelable Book;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// BookManager.aidl
//第二类AIDL文件
//作用是定义方法接口
package me.hacket.ipcclient;
//导入所需要使用的非默认支持数据类型的包
import me.hacket.ipcclient.Book;

interface BookManager {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    List<Book> getBooks();

    //传参时除了Java基本类型以及String,CharSequence之外的类型
    //都需要在前面加上定向tag,具体加什么量需而定
    void addBook(in Book book);
}

定义通信协议 AIDL

AIDL 解决的是远程通信问题,是一种 C / S 架构思想,参考网络通信模型,客户端和服务端之间,需要约定好彼此支持哪些东西,提供了什么能力给对方(这里主要是服务端提供给客户端的),而定义能力在面向对象的编程中,自然就是通过接口来定义。所以 AIDL 是 Interface Definition Language。

定义服务端暴露给客户端的能力,首先要创建 AIDL 文件。AIDL 文件在同包名下,与 java/res 等目录同级,创建 aidl 文件夹,其内部结构和 Java 保持一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
src
- java
-- com
--- chunyu
---- aidl
----- service
------ ...// java file

- aidl
-- com
--- chunyu
---- aidl
----- service
------ ... // aidl file

- ...

在定义 AIDL 接口前,我们现实现一个数据类,这个数据类作为服务端和客户端之间通信的数据结构。如果你的通信不需要复杂的数据对象,而是 int 、 long 等基本数据类型和 String ,则不需要这一个步骤。

  • 使用基本类型通信,无需定义数据类
1
2
3
4
5
6
7
8
// IServerManager.aidl
interface IServerManager {
    /**
     * 演示了一些可以在AIDL中用作参数和返回值的基本类型。
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}
  • 使用数据类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package me.hacket.aidl.demo;
class Msg(var msg: String, var time: Long? = null): Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString() ?: "",
        parcel.readValue(Long::class.java.classLoader) as? Long
    ) {
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(msg)
        parcel.writeValue(time)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Creator<Msg> {
        override fun createFromParcel(parcel: Parcel): Msg {
            return Msg(parcel)
        }

        override fun newArray(size: Int): Array<Msg?> {
            return arrayOfNulls(size)
        }
    }
}
// 实现完这个类后,需要在 aidl 同包名下创建一个 Msg.aidl 文件,将这个类型进行声明:
package me.hacket.aidl.demo;  
  
parcelable Msg;

数据对象定义好了,下一步就可以定义服务端暴露给客户端的接口了。

首先定义一个服务端主动回调给客户端的接口,所有注册了的 Client ,都可以接收到服务端的主动消息:

1
2
3
4
5
6
7
8
// IReceiveMsgListener.aidl
package me.hacket.assistant.aidl.msg.service;

import me.hacket.assistant.aidl.msg.Msg;

interface IReceiveMsgListener {
    void onReceive(in Msg msg);
}

需要注意的一点是,AIDL 文件中的 import 并不会自动导入,需要开发者自行添加。

然后定义 Server 暴露给 Client 的能力:

1
2
3
4
5
6
7
8
9
10
11
// IMsgManager.aidl
package me.hacket.assistant.aidl.msg.service;  
  
import me.hacket.assistant.aidl.msg.Msg;  
import me.hacket.assistant.aidl.msg.service.IReceiveMsgListener;  
  
interface IMsgManager {  
    void sendMsg(in Msg msg);  
    void registerReceiveListener(IReceiveMsgListener listener);  
    void unregisterReceiveListener(IReceiveMsgListener listener);  
}

IMsgManager 提供了三个方法,用来解决两个场景:

  • Client 主动发送 Msg 给 Server (sendMsg 方法,也可以理解为网络通信中的客户端发起请求)。
  • Server 主动发消息给所有订阅者,通过回调 IReceiveMsgListener 中的 onReceive 方法,每个注册的 Client 都会收到回调(典型的观察者模式)。

手动运行 build 重新构建项目,这样 AIDL 会在 build 后生成一些 class 文件,供项目代码中调用。

AIDL 生成的类

image.png

build/generated/aidl_source_output_dir/debug/out/包名/ 路径下生成:

image.png

IReceiveMsgListener.aidl

生成代码的结构:

image.png

生成的核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package me.hacket.assistant.aidl.msg.service;
public interface IReceiveMsgListener extends android.os.IInterface
{
	/** Default implementation for IReceiveMsgListener. */  
	public static class Default implements IReceiveMsgListener  
	{  
	  @Override public void onReceive(Msg msg) throws android.os.RemoteException  
	  {  
	  }  
	  @Override  
	  public android.os.IBinder asBinder() {  
	    return null;  
	  }  
	}
	/** Local-side IPC implementation stub class. */  
	public static abstract class Stub extends android.os.Binder implements IReceiveMsgListener  
	{
		private static final java.lang.String DESCRIPTOR = "me.hacket.assistant.aidl.msg.service.IReceiveMsgListener";  
		/** Construct the stub at attach it to the interface. */  
		public Stub()  
		{  
		  this.attachInterface(this, DESCRIPTOR);  
		}
		/**  
		 * Cast an IBinder object into an me.hacket.assistant.aidl.msg.service.IReceiveMsgListener interface, * generating a proxy if needed. */
		public static IReceiveMsgListener asInterface(android.os.IBinder obj)  
		{  
		  if ((obj==null)) {  
		    return null;  
		  }  
		  android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
		  if (((iin!=null)&&(iin instanceof IReceiveMsgListener))) {  
		    return ((IReceiveMsgListener)iin);  
		  }  
		  return new IReceiveMsgListener.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_onReceive:  
			  {  
			    data.enforceInterface(descriptor);  
			    me.hacket.aidl.demo.Msg _arg0;  
			    if ((0!=data.readInt())) {  
			      _arg0 = me.hacket.aidl.demo.Msg.CREATOR.createFromParcel(data);  
			    }  
			    else {  
			      _arg0 = null;  
			    }  
			    this.onReceive(_arg0);  
			    reply.writeNoException();  
			    return true;  
			  }  
			  default:  
			  {  
			    return super.onTransact(code, data, reply, flags);  
			  }  
			}
		}
		private static class Proxy implements IReceiveMsgListener {
			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 onReceive(me.hacket.aidl.demo.Msg msg) throws android.os.RemoteException  
			{  
			  android.os.Parcel _data = android.os.Parcel.obtain();  
			  android.os.Parcel _reply = android.os.Parcel.obtain();  
			  try {  
			    _data.writeInterfaceToken(DESCRIPTOR);  
			    if ((msg!=null)) {  
			      _data.writeInt(1);  
			      msg.writeToParcel(_data, 0);  
			    }  
			    else {  
			      _data.writeInt(0);  
			    }  
			    boolean _status = mRemote.transact(Stub.TRANSACTION_onReceive, _data, _reply, 0);  
			    if (!_status && getDefaultImpl() != null) {  
			      getDefaultImpl().onReceive(msg);  
			      return;  
			    }  
			    _reply.readException();  
			  }  
			  finally {  
			    _reply.recycle();  
			    _data.recycle();  
			  }  
			}  
			public static me.hacket.assistant.aidl.msg.service.IReceiveMsgListener sDefaultImpl;
		}
		static final int TRANSACTION_onReceive = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
		public static boolean setDefaultImpl(IReceiveMsgListener impl) {  
		  // Only one user of this interface can use this function  
		  // at a time. This is a heuristic to detect if two different  
		  // users in the same process use this function.  if (Stub.Proxy.sDefaultImpl != null) {  
		    throw new IllegalStateException("setDefaultImpl() called twice");  
		  }  
		  if (impl != null) {  
		    Stub.Proxy.sDefaultImpl = impl;  
		    return true;  
		  }  
		  return false;  
		}  
		public static IReceiveMsgListener getDefaultImpl() {  
		  return Stub.Proxy.sDefaultImpl;  
		}
	}
	
	public void onReceive(Msg msg) throws android.os.RemoteException;
}

IMsgManager.aidl

image.png

IMsgManager.Sub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public static abstract class Stub extends android.os.Binder implements IMsgManager {
	// asInterface()
	// asBinder()
	@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_sendMsg:  
	    {  
	      data.enforceInterface(descriptor);  
	      Msg _arg0;  
	      if ((0!=data.readInt())) {  
	        _arg0 = Msg.CREATOR.createFromParcel(data);  
	      }  
	      else {  
	        _arg0 = null;  
	      }  
	      this.sendMsg(_arg0);  
	      reply.writeNoException();  
	      return true;  
	    }  
	    case TRANSACTION_registerReceiveListener:  
	    {  
	      data.enforceInterface(descriptor);  
	      IReceiveMsgListener _arg0;  
	      _arg0 = IReceiveMsgListener.Stub.asInterface(data.readStrongBinder());  
	      this.registerReceiveListener(_arg0);  
	      reply.writeNoException();  
	      return true;  
	    }  
	    case TRANSACTION_unregisterReceiveListener:  
	    {  
	      data.enforceInterface(descriptor);  
	      IReceiveMsgListener _arg0;  
	      _arg0 = IReceiveMsgListener.Stub.asInterface(data.readStrongBinder());  
	      this.unregisterReceiveListener(_arg0);  
	      reply.writeNoException();  
	      return true;  
	    }  
	    default:  
	    {  
	      return super.onTransact(code, data, reply, flags);  
	    }  
	  }  
	}
}

IMsgManager.Sub 是一个 Binder 和实现了 IMsgManager 接口,核心方法:

  • asInterface() 本地对象返回 IMsgManager 实现类;如果是跨进程的,则返回 IMsgManager.Stub.Proxy
  • asBinder() 返回 IMsgManager.Sub 对象本身
  • onTransact() Server 服务端调用
IMsgManager.Sub.Pxoy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static abstract class Stub /**/ {
	private static class Proxy implements IMsgManager  
	{
		@Override 
		public void sendMsg(Msg msg) {
			android.os.Parcel _data = android.os.Parcel.obtain();  
			android.os.Parcel _reply = android.os.Parcel.obtain();  
			try {  
			  _data.writeInterfaceToken(DESCRIPTOR);  
			  if ((msg!=null)) {  
			    _data.writeInt(1);  
			    msg.writeToParcel(_data, 0);  
			  }  
			  else {  
			    _data.writeInt(0);  
			  }  
			  boolean _status = mRemote.transact(Stub.TRANSACTION_sendMsg, _data, _reply, 0);  
			  if (!_status && getDefaultImpl() != null) {  
			    getDefaultImpl().sendMsg(msg);  
			    return;  
			  }  
			  _reply.readException();  
			}  
			finally {  
			  _reply.recycle();  
			  _data.recycle();  
			}
		}
		// registerReceiveListener
		// unregisterReceiveListener
	}
}

定义服务端

服务端的定义需要创建一个 Service 来作为服务器。这里创建一个 MyService 类,然后在 AndroidManifest.xml 中配置一个 action ,这个 action 后续会用来进行跨进程启动:

1
2
3
4
5
6
7
8
<service
		android:name=".MyService"
		android:enabled="true"
		android:exported="true">
		<intent-filter>
			<action android:name="me.hacket.aidl.demo.service.MyService"></action>
		</intent-filter>
	</service>

配置完这个后,就可以在 MyService 中实现服务器的逻辑了。

Service 的启动方式有两种,startServicebindService ,后者是我们在实现 Client 连接 Server 的核心方法。通过 bindService 创建 C/S 之间的连接。而 Service 在通过 bindService 启动时,会回调 onBind 方法:

1
fun onBind(intent: Intent): IBinder 

onBind 的返回类型时 IBinder ,这个就是跨进程通信间传递的 Binder 。在同一个进程中不需要进行跨进程通信,这里可以返回为 null 。而此时需要实现 IPC ,自然这个方法需要返回一个存在的 Binder 对象,所以,配置服务器的第二步,就是实现一个 Binder 并在 onBind 中返回。

Server 提供给 Client 能力的接口 IMsgManager,经过 build 后,编译器会自动生成 IMsgManager.Stub ,这是一个自动生成的实现了 IMsgManager 接口的 Binder 抽象实现:

1
public static abstract class Stub extends android.os.Binder implements IMsgManager

在 MyService 中实现这个抽象类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class MyService : Service() {

  	// ...
  
    inner class MyBinder: IMsgManager.Stub() {
        override fun sendMsg(msg: Msg?) {
            // todo 收到 Client 发来的消息,此处实现 Server 的处理逻辑
        }
        override fun sendMsg(msg: Msg?) {
            // todo 收到 Client 发来的消息,此处实现 Server 的处理逻辑
            val n = receiveListeners.beginBroadcast()
            for (i in 0 until n) {
                val listener = receiveListeners.getBroadcastItem(i)
                listener?.let {
                    try {
                        val serverMsg = Msg("服务器响应 ${Date(System.currentTimeMillis())}\n ${packageName}", System.currentTimeMillis())
                        listener.onReceive(serverMsg)
                    } catch (e: RemoteException) {
                        e.printStackTrace()
                    }
                }
            }
            receiveListeners.finishBroadcast()
        }

        override fun registerReceiveListener(listener: IReceiveMsgListener?) {
          	// receiveListeners 记录观察者
            receiveListeners.register(listener)
        }

        override fun unregisterReceiveListener(listener: IReceiveMsgListener?) {
            val success = receiveListeners.unregister(listener)
            if (success) {
                Log.d(TAG, "解除注册成功")
            } else {
                Log.d(TAG, "解除注册失败")
            }
        }
    }
}

然后,在 onBind 方法中返回这个类的对象:

1
2
3
override fun onBind(intent: Intent): IBinder {
	return MyBinder()
}

定义客户端

Client 中需要实现的核心逻辑包括:

  • 创建 Client 到 Server 的连接。
  • 实现发送请求的功能。
  • 实现接收 Server 消息的功能。
  • 在销毁的生命周期中主动关闭连接。

创建连接

创建连接主要通过 bindService 来实现,分为两种情况,在同一个进程中,不需要跨进程,直接通过显式的 Intent 启动 Service :

1
2
val intent = Intent(this, MyService::class.java)
bindService(intent, connection, BIND_AUTO_CREATE)

这是因为在同一个进程中,能直接访问到服务端 Service 的类。而跨进程没有这个 class ,需要通过隐式 Intent 启动 :

1
2
3
4
val intent = Intent()
intent.action = "me.hacket.aidl.service.MyService"
intent.setPackage("me.hacket.aidl.service")
bindService(intent, connection, BIND_AUTO_CREATE)

Action 在实现服务端时,在 AndroidManifest.xml 中进行配置。

不管是哪个进程调用 bindService ,都会需要一个 connection 参数,这是一个 ServiceConnection 的对象。ServiceConnection 是一个监控应用 Service 状态的接口。和很多系统的其他回调一样,这个接口的实现的方法在进程的主线程中调用。

1
2
3
4
5
6
7
8
9
10
11
12
public interface ServiceConnection {

    void onServiceConnected(ComponentName name, IBinder service);

    void onServiceDisconnected(ComponentName name);

    default void onBindingDied(ComponentName name) {
    }
    
    default void onNullBinding(ComponentName name) {
    }
}

在 Client 中实现一个 ServiceConnection 对象,这里用匿名对象的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private var deathRecipient = object : IBinder.DeathRecipient {
    override fun binderDied() {
        iMsgManager?.let {
          	// 当 binder 连接断开时,解除注册
            it.asBinder().unlinkToDeath(this, 0)
            iMsgManager = null
        }
    }
}

val connection = object : ServiceConnection {
  	// 服务连接创建成功时
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        iMsgManager = IMsgManager.Stub.asInterface(binder)
        try {
            iMsgManager?.asBinder()?.linkToDeath(deathRecipient, 0)
            iMsgManager?.registerReceiveListener(receiveMsgListener)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }
    }
    override fun onServiceDisconnected(name: ComponentName?) { }
}

接收 Server 消息

这里注册的 listener 是一个 IReceiveMsgListener.Stub 对象:

1
2
3
4
5
6
7
private var receiveMsgListener = object : IReceiveMsgListener.Stub() {
	override fun onReceive(msg: Msg?) {
		displayTv.post {
			displayTv.text = "客户端:${msg?.msg}, time: ${msg?.time}"
		}
	}
}

为什么是 IReceiveMsgListener.Stub 类型,而不是一个 IReceiveMsgListener 的匿名对象?

首先 IReceiveMsgListener.Stub 的继承关系是:

1
public static abstract class Stub extends android.os.Binder implements IReceiveMsgListener

IReceiveMsgListener.Stub 本身也是 IReceiveMsgListener 的实现,而且它还继承自 Binder ,也就是具备了 Binder 的能力,能够在跨进程通信中作为 Binder 传输,所以这里是 IReceiveMsgListener.Stub 类型的对象。

而如果直接使用 IReceiveMsgListener ,匿名对象要求多实现一个 asBinder 方法:

1
2
3
4
5
6
7
8
9
10
11
12
private var receiveMsgListener = object : IReceiveMsgListener {
	override fun asBinder(): IBinder {
		TODO("Not yet implemented")
	}

	override fun onReceive(msg: Msg?) {
		displayTv.post {
			displayTv.text = "客户端:${msg?.msg}, time: ${msg?.time}"
		}
		Log.d(TAG, "客户端:$msg")
	}
}

其实 IReceiveMsgListener.Stub 就是帮我们实现好了一些逻辑,减少了开发的复杂度。

而从另一个方面也验证了 AIDL 跨进程通信调用的对象能需要具备作为 Binder 的能力。

Client 发送消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// iMsgManager 是 AIDL 中定义的 Server 暴露给 Client 的能力的接口对象
private var iMsgManager: IMsgManager?  = null
val connection = object : ServiceConnection {
  	// 服务连接创建成功时
    override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
        iMsgManager = IMsgManager.Stub.asInterface(binder)
        try {
            iMsgManager?.asBinder()?.linkToDeath(deathRecipient, 0)
            iMsgManager?.registerReceiveListener(receiveMsgListener)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }
    }
    override fun onServiceDisconnected(name: ComponentName?) { }
}

这里的 asInerfaceasBinder 方法,说明 AIDL 中定义的接口可以转换为 Binder ,Binder 也可以转换为 Interface ,因为这里的 Binder 来自于 Service 的 onBind 方法,在 onBind 中返回的就是 IMsgManager.Stub 的实现,自然可以转换为 IMsgManager 。

通过 IMsgManager 就可以调用 Server 中的方法了:

1
2
3
sendMsgBtn.setOnClickListener {
	iMsgManager?.sendMsg(Msg("from 客户端,当前第 ${count++} 次", System.currentTimeMillis()))
}

生命周期管理

在 Client 的生命周期中及时关闭连接,清除不需要的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// in MainActivity
override fun onDestroy() {
	if (iMsgManager?.asBinder()?.isBinderAlive == true) {
		try {
			iMsgManager?.unregisterReceiveListener(receiveMsgListener)
		} catch (e: RemoteException) {
			e.printStackTrace()
		}
	}
	connection?.let {
		unbindService(it)
	}
	super.onDestroy()
}

小结

  • AIDL 是一套快速实现 Android Binder 机制的框架。
  • Android 中的 Binder 机制,架构思想是 C / S 架构。
  • 所有跨进程传递的数据需要实现 Parcelable (除了一些基本的类型)。
  • 所有跨进程调用的对象,都必须是 Binder 的实现。
  • Binder 对象可以和 Interface 实例进行转换,这是因为 Service 中返回的 Binder 对象实现了 Interface。
  • 通过 aidl 文件中定义的接口,可以跨进程调用远程对象的方法。

示例

计算器

ICalcAIDL.aidl

1
2
3
4
5
6
7
// ICalcAIDL.aidl
package me.hacket.assistant;
// Declare any non-default types here with import statements
interface ICalcAIDL {
    int add(int x, int y);
    int minus(int x, int y);
}

生成的. java 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package me.hacket.assistant;
// Declare any non-default types here with import statements

public interface ICalcAIDL extends android.os.IInterface
{
  /** Default implementation for ICalcAIDL. */
  public static class Default implements me.hacket.assistant.ICalcAIDL
  {
    @Override public int add(int x, int y) throws android.os.RemoteException
    {
      return 0;
    }
    @Override public int minus(int x, int y) throws android.os.RemoteException
    {
      return 0;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements me.hacket.assistant.ICalcAIDL
  {
    private static final java.lang.String DESCRIPTOR = "me.hacket.assistant.ICalcAIDL";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an me.hacket.assistant.ICalcAIDL interface,
     * generating a proxy if needed.
     */
    public static me.hacket.assistant.ICalcAIDL asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof me.hacket.assistant.ICalcAIDL))) {
        return ((me.hacket.assistant.ICalcAIDL)iin);
      }
      return new me.hacket.assistant.ICalcAIDL.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_add:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          int _arg1;
          _arg1 = data.readInt();
          int _result = this.add(_arg0, _arg1);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        case TRANSACTION_minus:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          int _arg1;
          _arg1 = data.readInt();
          int _result = this.minus(_arg0, _arg1);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements me.hacket.assistant.ICalcAIDL
    {
      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 add(int x, int y) 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(x);
          _data.writeInt(y);
          boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().add(x, y);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public int minus(int x, int y) 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(x);
          _data.writeInt(y);
          boolean _status = mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().minus(x, y);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static me.hacket.assistant.ICalcAIDL sDefaultImpl;
    }
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_minus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(me.hacket.assistant.ICalcAIDL impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static me.hacket.assistant.ICalcAIDL getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public int add(int x, int y) throws android.os.RemoteException;
  public int minus(int x, int y) throws android.os.RemoteException;
}

如何通过它来达到我们的目的的?

AIDL 文件是怎么工作的?

AIDL 文件的目的其实就是为了生成用于跨进程通信的 .java 文件,我们手动写这个文件也可以。

原理

从客户端获取 ICalcAIDL 入口来看:

1
2
3
4
5
class MyConn: ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        ICalcAIDL mICalcAIDL = ICalcAIDL.Stub.asInterface(service)
    }
}

调用的是 ICalcAIDL.Stub.asInterface (service),而这个 service 调试发现就是个 BinderProxy

image.png

返回的为一个 Proxy 对象:

image.png

接着看 Stub.asInterface

1
2
3
4
5
6
7
8
9
10
11
12
public static me.hacket.assistant.ICalcAIDL asInterface(android.os.IBinder obj) {
    if ((obj==null)) {
        return null;
    }
    // 搜索本地是否已经有可用的对象了,如果有就将其返回
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof me.hacket.assistant.ICalcAIDL))) {
        return ((me.hacket.assistant.ICalcAIDL)iin);
    }
    // 如果本地没有的话就新建一个返回
    return new me.hacket.assistant.ICalcAIDL.Stub.Proxy(obj);
}
  • 通过 queryLocalInterface 查询是否已经有可用的对象
  • 没有的话重新创建一个 Proxy 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
private static class Proxy implements me.hacket.assistant.ICalcAIDL 
{
  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 add(int x, int y) throws android.os.RemoteException
  {
    // ...
  }
  @Override public int minus(int x, int y) throws android.os.RemoteException
  {
    // _data用来存储流向服务端的数据流
    android.os.Parcel _data = android.os.Parcel.obtain();
    // _reply用来存储服务端流回客户端的数据流
    android.os.Parcel _reply = android.os.Parcel.obtain();
    int _result;
    try {
      _data.writeInterfaceToken(DESCRIPTOR);
      _data.writeInt(x);
      _data.writeInt(y);
      // 调用 transact()方法将方法id和两个 Parcel 容器传过去
      boolean _status = mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0);
      if (!_status && getDefaultImpl() != null) {
        return getDefaultImpl().minus(x, y);
      }
      _reply.readException();
      // 从_reply中取出服务端执行方法的结果
      _result = _reply.readInt();
    }
    finally {
      _reply.recycle();
      _data.recycle();
    }
    // 将结果返回
    return _result;
  }
  static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  static final int TRANSACTION_minus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
  public static boolean setDefaultImpl(me.hacket.assistant.ICalcAIDL impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
  }
  public static me.hacket.assistant.ICalcAIDL getDefaultImpl() {
    return Stub.Proxy.sDefaultImpl;
  }
}
  • 关于 _data_reply 对象:一般来说,我们会将方法的传参的数据存入 _data 中,而将方法的返回值的数据存入 _reply 中(在没涉及定向 tag 的情况下。如果涉及了定向 tag ,情况将会变得稍微复杂些)
  • 关于 Parcel:Parcel 是一个用来存放和读取数据的容器。我们可以用它来进行客户端和服务端之间的数据传输,当然,它能传输的只能是可序列化的数据。
  • 关于 transact () 方法:这是客户端和服务端通信的核心方法。调用这个方法之后,客户端将会挂起当前线程,等候服务端执行完相关任务后通知并接收返回的 _reply 数据流。 关于这个方法的传参,这里有两点需要说明的地方:
    1. 方法 ID code :transact () 方法的第一个参数是一个方法 ID,这个是客户端与服务端约定好的给方法的编码,彼此一一对应。在 AIDL 文件转化为 .java 文件的时候,系统将会自动给 AIDL 文件里面的每一个方法自动分配一个方法 ID。
    2. 第四个参数 flags:它的作用是设置进行 IPC 的模式,为 0 表示数据可以双向流通,即 _reply 流可以正常的携带数据回来,如果为 1 的话那么数据将只能单向流通,从服务端回来的 _reply 流将不携带任何数据。注:AIDL 生成的 .java 文件的这个参数均为 0。

现在看 onTransact

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@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_add:
    {
      data.enforceInterface(descriptor);
      int _arg0;
      _arg0 = data.readInt();
      int _arg1;
      _arg1 = data.readInt();
      int _result = this.add(_arg0, _arg1);
      reply.writeNoException();
      reply.writeInt(_result);
      return true;
    }
    case TRANSACTION_minus:
    {
      data.enforceInterface(descriptor);
      int _arg 0;
      _arg 0 = data.readInt ();
      int _arg 1;
      _arg 1 = data.readInt ();
      int _result = this.minus (_arg 0, _arg 1);
      reply.writeNoException ();
      reply.writeInt (_result);
      return true;
    }
    default:
    {
      return super.onTransact (code, data, reply, flags);
    }
  }
}

为什么要这么设计这门语言?会不会有更好的方式来实现我们的目的?

Ref

  • Android:学习 AIDL,这一篇文章就够了 (上)

https://blog.csdn.net/luoyanglizi/article/details/51980630

  • Android:学习 AIDL,这一篇文章就够了 (下)

https://blog.csdn.net/luoyanglizi/article/details/52029091

  • 你真的理解 AIDL 中的 in,out,inout 么?

https://www.jianshu.com/p/ddbb40c7a251

AIDL in out inout oneway

in out inout

  1. in 参数使得实参顺利传到服务方,但服务方对实参的任何改变,不会反应给调用方
  2. out 参数使得实参不会真正传递到服务方,只是传一个实参的初始值过去,但服务方对实参的任何改变,在调用结束后会反应回调用方
  3. inout 是上面二者的结合,实参会顺利传到服务方,且服务方对实参的任何改变,在调用结束后会反应回调用方

其实 inout,都是相对于服务方,in 参数使得实参传到了服务方,所以是 in 进入了服务方,out 参数使得实参在调用结束后从服务方传回给调用方,所以 out 是从服务方出来

oneway

oneway 主要有两个特性:异步调用串行化处理

  • 异步调用是指应用向 binder 驱动发送数据后不需要挂起线程等待 binder 驱动的回复,而是直接结束。像一些系统服务调用应用进程的时候就会使用 oneway,比如 AMS 调用应用进程启动 Activity,这样就算应用进程中做了耗时任务,也不会阻塞系统服务的运行
  • 串行化处理是指对于一个服务端的 AIDL 接口而言,所有的 oneway 方法不会同时执行,binder 驱动会将它们串行化处理,排队一个一个调用

非 oneway 情况: 非 oneway 的话,Client 会挂起,相当于 Thread 的 sleep,底层调用的是 wait_event_interruptible() Linux 系统函数。 image.png

oneway 情况: oneway 的话,Client 就不需要挂起线程等待 image.png

开源

remoter

GitHub - josesamuel/remoter: Remoter - An alternative to Android AIDL for Android Remote IPC services using plain java interfaces

本文由作者按照 CC BY 4.0 进行授权