本文源码分析基于Android Q
java
这是一篇短文,用于阐述一个简单却又容易被人忽略的知识点。android
毛主席在实践论中告诉咱们:“你要知道梨子的滋味,你就得变革梨子,亲口吃一吃。”这句话翻译成程序员的行话就是:“Read the fucking source code.”程序员
AIDL中inout概念的理解一样如此,不少人认识不清晰的直接缘由就是没有亲自看过通过aidl-gen生成的Java文件。我想了想,可能有两个缘由阻碍了人们去直接“变革”AIDL:bash
理解in/out/inout最简单的方式不是旁征博引、大段阐述,而是找一个AIDL生成的java文件来直接感觉它。ide
这里咱们选择的AIDL文件是IAidlTest.aidl。源码分析
frameworks/base/core/tests/coretests/src/android/os/IAidlTest.aidlui
18 package android.os;
19
20 import android.os.AidlTest;
21
22 interface IAidlTest {
23 int intMethod(int a);
24
25 AidlTest.TestParcelable parcelableIn(in AidlTest.TestParcelable p);
26 AidlTest.TestParcelable parcelableOut(out AidlTest.TestParcelable p);
27 AidlTest.TestParcelable parcelableInOut(inout AidlTest.TestParcelable p);
28
29 AidlTest.TestParcelable listParcelableLonger(
30 inout List<AidlTest.TestParcelable> list, int index);
31 int listParcelableShorter(
32 inout List<AidlTest.TestParcelable> list, int index);
33
34 boolean[] booleanArray(in boolean[] a0, out boolean[] a1, inout boolean[] a2);
35 char[] charArray(in char[] a0, out char[] a1, inout char[] a2);
36 int[] intArray(in int[] a0, out int[] a1, inout int[] a2);
37 long[] longArray(in long[] a0, out long[] a1, inout long[] a2);
38 float[] floatArray(in float[] a0, out float[] a1, inout float[] a2);
39 double[] doubleArray(in double[] a0, out double[] a1, inout double[] a2);
40 String[] stringArray(in String[] a0, out String[] a1, inout String[] a2);
41 AidlTest.TestParcelable[] parcelableArray(in AidlTest.TestParcelable[] a0,
42 out AidlTest.TestParcelable[] a1,
43 inout AidlTest.TestParcelable[] a2);
44
45 void voidSecurityException();
46 int intSecurityException();
47 }
复制代码
这里咱们只关注三个方法:spa
AidlTest.TestParcelable是一个继承了Parcelable接口的类,该类中的方法主要为了完成两件事:翻译
全部的跨进程通讯都是由client端发起的,所以咱们须要重点关注Java文件中生成的Proxy类,它是client进程获取到的用于通讯的Java对象所属的类。code
463 @Override public android.os.AidlTest.TestParcelable parcelableIn(android.os.AidlTest.TestParcelable p) throws android.os.RemoteException 464 {
465 android.os.Parcel _data = android.os.Parcel.obtain();
466 android.os.Parcel _reply = android.os.Parcel.obtain();
470 android.os.AidlTest.TestParcelable _result;
468 try {
469 _data.writeInterfaceToken(DESCRIPTOR);
470 if ((p!=null)) {
471 _data.writeInt(1);
472 p.writeToParcel(_data, 0);
473 }
474 else {
475 _data.writeInt(0);
476 }
477 boolean _status = mRemote.transact(Stub.TRANSACTION_parcelableIn, _data, _reply, 0);
478 if (!_status && getDefaultImpl() != null) {
479 return getDefaultImpl().parcelableIn(p);
480 }
481 _reply.readException();
482 if ((0!=_reply.readInt())) {
483 _result = android.os.AidlTest.TestParcelable.CREATOR.createFromParcel(_reply);
484 }
485 else {
486 _result = null;
487 }
488 }
489 finally {
490 _reply.recycle();
491 _data.recycle();
492 }
493 return _result;
494 }
复制代码
为了将参数传输给对端进程,须要将其先写入一个Parcel对象,也就是上面代码的472行。以后经过mRemote.transact进行真正的跨进程通讯。
Binder经过内存拷贝的方式传输数据,所以对端进程拿到的并非此进程中的TestParcelable对象,而是照着它的模子拷贝的一份“镜像”。被“in”修饰的参数p只有一个做用:在对端进程中产生一个p的“镜像”。此后该“镜像”的数据不管如何变化也影响不到p。
495 @Override public android.os.AidlTest.TestParcelable parcelableOut(android.os.AidlTest.TestParcelable p) throws android.os.RemoteException 496 {
497 android.os.Parcel _data = android.os.Parcel.obtain();
498 android.os.Parcel _reply = android.os.Parcel.obtain();
499 android.os.AidlTest.TestParcelable _result;
500 try {
501 _data.writeInterfaceToken(DESCRIPTOR);
502 boolean _status = mRemote.transact(Stub.TRANSACTION_parcelableOut, _data, _reply, 0);
503 if (!_status && getDefaultImpl() != null) {
504 return getDefaultImpl().parcelableOut(p);
505 }
506 _reply.readException();
507 if ((0!=_reply.readInt())) {
508 _result = android.os.AidlTest.TestParcelable.CREATOR.createFromParcel(_reply);
509 }
510 else {
511 _result = null;
512 }
513 if ((0!=_reply.readInt())) {
514 p.readFromParcel(_reply);
515 }
516 }
517 finally {
518 _reply.recycle();
519 _data.recycle();
520 }
521 return _result;
522 }
复制代码
能够看到502行以前_data中只写入了DESCRIPTOR(用于对端进程接收数据后进行校验),所以被“out”修饰的参数p并不会传输给对端进程。
而当对端进程返回数据时,全部返回的数据都会序列化地排列在_reply中。首先是508行从_reply中读取数据并建立返回对象(该对象是方法返回值)。接着是514行从_reply中读取数据并保存到参数p中,这就至关于对端进程来更新本进程的数据。
p中本来的数据并不会传递给对端进程,但从该方法返回后,它里面的数据将会被对端进程更新。所以你能够把它看做是第二个返回值,这也是之因此用“out”修饰的缘由。
523 @Override public android.os.AidlTest.TestParcelable parcelableInOut(android.os.AidlTest.TestParcelable p) throws android.os.RemoteException 524 {
525 android.os.Parcel _data = android.os.Parcel.obtain();
526 android.os.Parcel _reply = android.os.Parcel.obtain();
527 android.os.AidlTest.TestParcelable _result;
528 try {
529 _data.writeInterfaceToken(DESCRIPTOR);
530 if ((p!=null)) {
531 _data.writeInt(1);
532 p.writeToParcel(_data, 0);
533 }
534 else {
535 _data.writeInt(0);
536 }
537 boolean _status = mRemote.transact(Stub.TRANSACTION_parcelableInOut, _data, _reply, 0);
538 if (!_status && getDefaultImpl() != null) {
539 return getDefaultImpl().parcelableInOut(p);
540 }
541 _reply.readException();
542 if ((0!=_reply.readInt())) {
543 _result = android.os.AidlTest.TestParcelable.CREATOR.createFromParcel(_reply);
544 }
545 else {
546 _result = null;
547 }
548 if ((0!=_reply.readInt())) {
549 p.readFromParcel(_reply);
550 }
551 }
552 finally {
553 _reply.recycle();
554 _data.recycle();
555 }
556 return _result;
557 }
复制代码
看完了“in”和“out”各自的含义,“inout”的含义也就不言自明了。参数p中的数据即会传递给对端数据,又会被对端返回的数据给更新。这和本地调用传入引用参数的状况是一致的:传入的参数既可以让方法体内感知到它的数据,方法体内对于传入参数内部数据的改动也可让外界知道。