AIDL源码解析in、out和inout

为什么会想写这篇文章,只因为一个error。”aidl.exe E 4928 5836 type_namespace.cpp:130] ‘Book’ can be an out type, so you must declare it as in, out or inout. “看过上一篇文章Android:IPC之AIDL的学习和总结的同学都知道这是因为在AIDL文件中使用非常规类型作为参数传递的时候没有标记指向tag,那么到底为什么会是这样子的呢,作为一个好奇宝宝我想好好看看。

介绍

官网介绍AIDL的时候上有这么一段话

  • All non-primitive parameters require a directional tag indicating which way the data goes. Either in, out, or inout (see the example below).
  • Primitives are in by default, and cannot be otherwise.
  • Caution: You should limit the direction to what is truly needed, because marshalling parameters is expensive.

大概意思是非默认类型的参数都需要添加指向标签in,out或inout。根据自己的需求去添加,因为实现是有代价的。

已知结论

看过我写的Android:IPC之AIDL的学习和总结的同学都知道:

  • in表示输入型参数(Server可以获取到Client传递过去的数据,但是不能对Client端的数据进行修改)
  • out表示输出型参数(Server获取不到Client传递过去的数据,但是能对Client端的数据进行修改)
  • inout表示输入输出型参数(Server可以获取到Client传递过去的数据,但是能对Client端的数据进行修改)。

提出问题

下边我们就研究一个in,out或inout为什么能代表不同的传输方式,为什么实现的代价不一样。

过程验证

创建Book.aidl文件

1
2
package com.tzx.aidldemo.aidl;
parcelable Book;

创建Book.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
package com.tzx.aidldemo.aidl;
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book() {
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
//从序列化后的对象中创建原始对象
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
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];
}
};
//返回当前对象的内容描述。如果含有文件描述符,返回1,否则返回0,几乎所有情况都返回0
@Override
public int describeContents() {
return 0;
}
//将当前对象写入序列化结构中,其flags标识有两种(1|0)。
//为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况下都为0.
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
@Override
public String toString() {
return "[bookId=" + bookId + ",bookName='" + bookName + "']";
}
}

创建aidl接口文件IBookManager.aidl文件

1
2
3
4
5
6
7
package com.tzx.aidlinout.aidl;
import com.tzx.aidlinout.aidl.Book;
interface IBookManager {
Book addInBook(in Book book);
Book addOutBook(out Book book);
Book addInoutBook(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
//将bookId都改为-1,在bookName后面都添加参数的tag标记
public class BookManagerService extends Service {
private CopyOnWriteArrayList list = new CopyOnWriteArrayList();
private IBinder mBinder = new IBookManager.Stub(){
@Override
public Book addInBook(Book book) throws RemoteException {
book.bookId = -1;
book.bookName = book.bookName + "-in";
list.add(book);
return book;
}
@Override
public Book addOutBook(Book book) throws RemoteException {
book.bookId = -1;
book.bookName = book.bookName + "-out";
list.add(book);
return book;
}
@Override
public Book addInoutBook(Book book) throws RemoteException {
book.bookId = -1;
book.bookName = book.bookName + "-inout";
list.add(book);
return book;
}
@Override
public List<Book> getBookList() throws RemoteException {
return list;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}

在创建上面的文件的过程中,遇到不太清楚的或者编译出现Error的,可以参考上一篇文章Android:IPC之AIDL的学习和总结

具体方法调用的Activity就不写全部代码了,我们看看三种方法的调用

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
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.book_in:
try {
int bookId = Integer.parseInt(bookIdET.getText().toString());
String bookName = bookNameET.getText().toString();
if (bookId <= 0 || TextUtils.isEmpty(bookName)) return;
StringBuilder builder = new StringBuilder();
//LogUtils.d("-----------book_in-----------------");
Book book0 = new Book(bookId, bookName);
String source = "source:" + book0.toString();
//LogUtils.d(source);
builder.append(source);
builder.append('\n');
String result = "result:" + bookManager.addInBook(book0).toString();
//LogUtils.d(result);
builder.append(result);
builder.append('\n');
source = "source" + book0.toString();
//LogUtils.d(source);
builder.append(source);
//LogUtils.d("**************book_in****************");
bookinfoTV.setText(builder.toString());
} catch (Exception e) {
e.printStackTrace();
}
break;
case R.id.book_out:
try {
int bookId = Integer.parseInt(bookIdET.getText().toString());
String bookName = bookNameET.getText().toString();
if (bookId <= 0 || TextUtils.isEmpty(bookName)) return;
StringBuilder builder = new StringBuilder();
//LogUtils.d("-----------book_out-----------------");
Book book0 = new Book(bookId, bookName);
String source = "source:" + book0.toString();
//LogUtils.d(source);
builder.append(source);
builder.append('\n');
String result = "result:" + bookManager.addOutBook(book0).toString();
//LogUtils.d(result);
builder.append(result);
builder.append('\n');
source = "source" + book0.toString();
//LogUtils.d(source);
builder.append(source);
//LogUtils.d("**************book_out****************");
bookinfoTV.setText(builder.toString());
} catch (Exception e) {
e.printStackTrace();
}
break;
case R.id.book_inout:
try {
int bookId = Integer.parseInt(bookIdET.getText().toString());
String bookName = bookNameET.getText().toString();
if (bookId <= 0 || TextUtils.isEmpty(bookName)) return;
StringBuilder builder = new StringBuilder();
//LogUtils.d("-----------book_inout-----------------");
Book book0 = new Book(bookId, bookName);
String source = "source:" + book0.toString();
//LogUtils.d(source);
builder.append(source);
builder.append('\n');
String result = "result:" + bookManager.addInoutBook(book0).toString();
//LogUtils.d(result);
builder.append(result);
builder.append('\n');
source = "source" + book0.toString();
//LogUtils.d(source);
builder.append(source);
//LogUtils.d("**************book_inout****************");
bookinfoTV.setText(builder.toString());
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}

创建好上面三个文件后,我们编译整个项目工程(PS:生成aidl接口实现类)。

运行结果
AIDLinout运行结果

下边是与结果相对应的Log输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
14962-14962/com.tzx.aidlinout D/xxx: -----------book_in-----------------
14962-14962/com.tzx.aidlinout D/xxx: source:[bookId=1212,bookName=C++]
14962-14962/com.tzx.aidlinout D/xxx: result:[bookId=-1,bookName=C++-in]
14962-14962/com.tzx.aidlinout D/xxx: source[bookId=1212,bookName=C++]
14962-14962/com.tzx.aidlinout D/xxx: **************book_in****************
14962-14962/com.tzx.aidlinout D/xxx: -----------book_out-----------------
14962-14962/com.tzx.aidlinout D/xxx: source:[bookId=1212,bookName=C++]
14962-14962/com.tzx.aidlinout D/xxx: result:[bookId=-1,bookName=null-out]
14962-14962/com.tzx.aidlinout D/xxx: source[bookId=-1,bookName=null-out]
14962-14962/com.tzx.aidlinout D/xxx: **************book_out****************
14962-14962/com.tzx.aidlinout D/xxx: -----------book_inout-----------------
14962-14962/com.tzx.aidlinout D/xxx: source:[bookId=1212,bookName=C++]
14962-14962/com.tzx.aidlinout D/xxx: result:[bookId=-1,bookName=C++-inout]
14962-14962/com.tzx.aidlinout D/xxx: source[bookId=-1,bookName=C++-inout]
14962-14962/com.tzx.aidlinout D/xxx: **************book_inout****************

实际结果与我们已知结论一致~!~!

但问题我们还没有解决,我们继续看代码,其实所有的实现都是在改接口实现类中IBookManager.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
package com.tzx.aidlinout.aidl;
public interface IBookManager extends android.os.IInterface {
public com.tzx.aidlinout.aidl.Book addInBook(
com.tzx.aidlinout.aidl.Book book) throws android.os.RemoteException;
public com.tzx.aidlinout.aidl.Book addOutBook(
com.tzx.aidlinout.aidl.Book book) throws android.os.RemoteException;
public com.tzx.aidlinout.aidl.Book addInoutBook(
com.tzx.aidlinout.aidl.Book book) throws android.os.RemoteException;
public java.util.List<com.tzx.aidlinout.aidl.Book> getBookList()
throws android.os.RemoteException;
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.tzx.aidlinout.aidl.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.tzx.aidlinout.aidl.IBookManager";
static final int TRANSACTION_addInBook = (android.os.IBinder.FIRST_CALL_TRANSACTION +
0);
static final int TRANSACTION_addOutBook = (android.os.IBinder.FIRST_CALL_TRANSACTION +
1);
static final int TRANSACTION_addInoutBook = (android.os.IBinder.FIRST_CALL_TRANSACTION +
2);
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION +
3);
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.tzx.aidlinout.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.tzx.aidlinout.aidl.IBookManager asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) &&
(iin instanceof com.tzx.aidlinout.aidl.IBookManager))) {
return ((com.tzx.aidlinout.aidl.IBookManager) iin);
}
return new com.tzx.aidlinout.aidl.IBookManager.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 {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_addInBook: {
data.enforceInterface(DESCRIPTOR);
//声明输入的参数_arg0的引用
com.tzx.aidlinout.aidl.Book _arg0;
//并根据输入的数据为其创建对象
if ((0 != data.readInt())) {
_arg0 = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//获取调用this.addInBook方法返回的_result
com.tzx.aidlinout.aidl.Book _result = this.addInBook(_arg0);
reply.writeNoException();
//并向reply中写入返回值_result
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addOutBook: {
data.enforceInterface(DESCRIPTOR);
//声明输入的参数_arg0的引用
com.tzx.aidlinout.aidl.Book _arg0;
//并为其创建新的对象
_arg0 = new com.tzx.aidlinout.aidl.Book();
//获取调用this.addOutBook方法返回的_result
com.tzx.aidlinout.aidl.Book _result = this.addOutBook(_arg0);
reply.writeNoException();
//并向reply中写入返回值_result
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
//再将参数_arg0写入reply中,至于为什么写入,我们看看客户端Proxy中的读取
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addInoutBook: {
data.enforceInterface(DESCRIPTOR);
//声明输入的参数_arg0的引用
com.tzx.aidlinout.aidl.Book _arg0;
//并根据输入的数据为其创建对象
if ((0 != data.readInt())) {
_arg0 = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//获取调用this.addInoutBook方法返回的_result
com.tzx.aidlinout.aidl.Book _result = this.addInoutBook(_arg0);
reply.writeNoException();
//并向reply中写入返回值_result
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
//再将参数_arg0写入reply中,至于为什么写入,我们看看客户端Proxy中的读取
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.tzx.aidlinout.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.tzx.aidlinout.aidl.IBookManager {
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 com.tzx.aidlinout.aidl.Book addInBook(
com.tzx.aidlinout.aidl.Book book)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.tzx.aidlinout.aidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//将客户端调用时传入的参数写入_data中
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//将_data、_reply序列化对象和Stub.TRANSACTION_addInBook指令传递到Server端
mRemote.transact(Stub.TRANSACTION_addInBook, _data, _reply,
0);
_reply.readException();
//读取Server端返回的序列化_reply中的对象
if ((0 != _reply.readInt())) {
_result = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
//然后直接将_result返回
//我们发现整个方法调用期间传入的对象book只是将数据写入到Server,它的值进行并没有任何修改。
//总结:in类型的参数,它向服务端传入数据,但是却不接受Server返回的值。
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.tzx.aidlinout.aidl.Book addOutBook(
com.tzx.aidlinout.aidl.Book book)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.tzx.aidlinout.aidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//将_data、_reply序列化对象和Stub.TRANSACTION_addInBook指令传递到Server端
//_data和_reply序列化对象并没有进行写入
mRemote.transact(Stub.TRANSACTION_addOutBook, _data,
_reply, 0);
_reply.readException();
//读取Server端返回的序列化_reply中的对象,写入到_result
if ((0 != _reply.readInt())) {
_result = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
//读取Server端返回的序列化_reply中的对象,写入到传入的book对象中
if ((0 != _reply.readInt())) {
book.readFromParcel(_reply);
}
//然后直接将_result返回
//我们发现整个方法调用期间传入的对象book并没有将数据写入到Server,它的值确实是Server返回的。
//总结:out类型的参数,它并不向服务端传入数据,但是却接受Server返回的值。
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.tzx.aidlinout.aidl.Book addInoutBook(
com.tzx.aidlinout.aidl.Book book)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.tzx.aidlinout.aidl.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//将客户端调用时传入的参数写入_data中
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//将_data、_reply序列化对象和Stub.TRANSACTION_addInoutBook指令传递到Server端
mRemote.transact(Stub.TRANSACTION_addInoutBook, _data,
_reply, 0);
_reply.readException();
//读取Server端返回的序列化_reply中的对象,写入到_result
if ((0 != _reply.readInt())) {
_result = com.tzx.aidlinout.aidl.Book.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
//读取Server端返回的序列化_reply中的对象,写入到传入的book对象中
if ((0 != _reply.readInt())) {
book.readFromParcel(_reply);
}
//然后直接将_result返回
//我们发现整个方法调用期间传入的对象book将其数据写入到Server,并且它的值被Server返回的数据修改。
//总结:inout类型的参数,它既向服务端传入数据,也却接受Server返回的值。
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public java.util.List<com.tzx.aidlinout.aidl.Book> getBookList()
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.tzx.aidlinout.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data,
_reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.tzx.aidlinout.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
}

看了这么多代码是不是感觉脑袋大了,没事接下来一张图帮你理的清清楚楚的:
aidl-tag-type

经过两篇文章对aidl的讲解,我想你已经把它理解的透透的了,如果还有什么问题可以给我留言哦~!

GitHubDemo地址

想阅读作者的更多文章,可以查看我的公共号:

振兴书城
文章目录
  1. 1. 介绍
  2. 2. 已知结论
  3. 3. 提出问题
  4. 4. 过程验证
  5. 5. 源码解析
,