本文源码分析基于Android Q
这是一篇短文,用于阐述一个简单却又容易被人忽略的知识点。
毛主席在实践论中告诉我们:“你要知道梨子的滋味,你就得变革梨子,亲口吃一吃。”这句话翻译成程序员的行话就是:“Read the fucking source code.”
AIDL中inout概念的理解同样如此,很多人认识不清晰的直接原因就是没有亲自看过经过aidl-gen生成的Java文件。我想了想,可能有两个原因阻碍了人们去直接“变革”AIDL:
理解in/out/inout最简单的方式不是旁征博引、大段阐述,而是找一个AIDL生成的java文件来直接感受它。
这里我们选择的AIDL文件是IAidlTest.aidl。
frameworks/base/core/tests/coretests/src/android/os/IAidlTest.aidl
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 } 复制代码
这里我们只关注三个方法:
AidlTest.TestParcelable是一个继承了Parcelable接口的类,该类中的方法主要为了完成两件事:
所有的跨进程通信都是由client端发起的,因此我们需要重点关注Java文件中生成的Proxy类,它是client进程获取到的用于通信的Java对象所属的类。
out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frameworks/base/core/tests/coretests/src/android/os/IAidlTest.java
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。
out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frameworks/base/core/tests/coretests/src/android/os/IAidlTest.java
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”修饰的原因。
out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frameworks/base/core/tests/coretests/src/android/os/IAidlTest.java
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中的数据即会传递给对端数据,又会被对端返回的数据给更新。这和本地调用传入引用参数的情况是一致的:传入的参数既能够让方法体内感知到它的数据,方法体内对于传入参数内部数据的改动也可以让外界知道。