Android 手写Binder 教你理解android中的进程间通信

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 手写Binder 教你理解android中的进程间通信相关的知识,希望对你有一定的参考价值。

关于Binder,我就不解释的太多了,网上一搜资料一堆,但是估计还是很多人理解的有困难。今天就教你如何从 app层面来理解好Binder。

其实就从我们普通app开发者的角度来看,仅仅对于android应用层的话,Binder就是客户端和服务端进行通信的媒介。

AIDL就是我们理解Binder 最好的事例。

我们都知道 我们写好aidl 文件以后,开发工具 会自动帮我们生成好代码。实际上 我们最终apk里面 是只有这些代码的,我们写的aidl文件

是不会被打包进去的,也就是说aidl文件 实际上 就是我们用来 生成 实际binder代码用的。所以 我们只要能够分析好,ide自动帮我们生成的

代码,就可以自己手写binder,从而在app层面上真正理解binder的用法和含义 以及原理。

首先我先来定义一个实体类:Person.java

 1 package com.example.administrator.writebindercodeexample;
 2 
 3 import android.os.Parcel;
 4 import android.os.Parcelable;
 5 
 6 /**
 7  * Created by Administrator on 2016/1/27.
 8  */
 9 public class Person implements Parcelable {
10 
11     private String name;
12 
13     public void setName(String name) {
14         this.name = name;
15     }
16 
17     public void setGender(int gender) {
18         this.gender = gender;
19     }
20 
21     public int getGender() {
22         return gender;
23     }
24 
25     public String getName() {
26         return name;
27     }
28 
29     private int gender;
30 
31     @Override
32     public int describeContents() {
33         return 0;
34     }
35 
36     @Override
37     public void writeToParcel(Parcel dest, int flags) {
38         dest.writeString(this.name);
39         dest.writeInt(this.gender);
40     }
41 
42     public Person() {
43     }
44 
45     protected Person(Parcel in) {
46         this.name = in.readString();
47         this.gender = in.readInt();
48     }
49 
50     public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
51         public Person createFromParcel(Parcel source) {
52             return new Person(source);
53         }
54 
55         public Person[] newArray(int size) {
56             return new Person[size];
57         }
58     };
59 }

注意看  我们这个person 类 是实现了android自带的序列化接口的,所以 如果你要在aidl里使用这个类,那你必须要额外在aidl里生命下 这个类。

1 // Person.aidl.aidl
2 package com.example.administrator.writebindercodeexample;
3 
4 // Declare any non-default types here with import statements
5 parcelable Person;
 1 // IPersonManager.aidl
 2 package com.example.administrator.writebindercodeexample;
 3 
 4 // Declare any non-default types here with import statements
 5 import com.example.administrator.writebindercodeexample.Person;
 6 interface IPersonManager {
 7     List<Person> getPersonList();
 8     //关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说
 9     void addPerson(in Person person);
10 }

好,然后给你们看一下 文件结构:

技术分享

好 这里就是一个典型的 应用aidl 技术的 一个例子,我们现在 让studio 编译这个project,然后看看生成的binder代码。 把这份binder代码 分析好了,我们以后就可以不借助ide 来自己手写binder了。

我们来看看 生成的代码在哪里:

技术分享

最后我们来看一下 这个生成的代码 是啥样的:

  1 /*
  2  * This file is auto-generated.  DO NOT MODIFY.
  3  * Original file: C:\\Users\\Administrator\\WriteBinderCodeExample\\app\\src\\main\\aidl\\com\\example\\administrator\\writebindercodeexample\\IPersonManager.aidl
  4  */
  5 package com.example.administrator.writebindercodeexample;
  6 public interface IPersonManager extends android.os.IInterface
  7 {
  8 /** Local-side IPC implementation stub class. */
  9 public static abstract class Stub extends android.os.Binder implements com.example.administrator.writebindercodeexample.IPersonManager
 10 {
 11 private static final java.lang.String DESCRIPTOR = "com.example.administrator.writebindercodeexample.IPersonManager";
 12 /** Construct the stub at attach it to the interface. */
 13 public Stub()
 14 {
 15 this.attachInterface(this, DESCRIPTOR);
 16 }
 17 /**
 18  * Cast an IBinder object into an com.example.administrator.writebindercodeexample.IPersonManager interface,
 19  * generating a proxy if needed.
 20  */
 21 public static com.example.administrator.writebindercodeexample.IPersonManager asInterface(android.os.IBinder obj)
 22 {
 23 if ((obj==null)) {
 24 return null;
 25 }
 26 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
 27 if (((iin!=null)&&(iin instanceof com.example.administrator.writebindercodeexample.IPersonManager))) {
 28 return ((com.example.administrator.writebindercodeexample.IPersonManager)iin);
 29 }
 30 return new com.example.administrator.writebindercodeexample.IPersonManager.Stub.Proxy(obj);
 31 }
 32 @Override public android.os.IBinder asBinder()
 33 {
 34 return this;
 35 }
 36 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
 37 {
 38 switch (code)
 39 {
 40 case INTERFACE_TRANSACTION:
 41 {
 42 reply.writeString(DESCRIPTOR);
 43 return true;
 44 }
 45 case TRANSACTION_getPersonList:
 46 {
 47 data.enforceInterface(DESCRIPTOR);
 48 java.util.List<com.example.administrator.writebindercodeexample.Person> _result = this.getPersonList();
 49 reply.writeNoException();
 50 reply.writeTypedList(_result);
 51 return true;
 52 }
 53 case TRANSACTION_addPerson:
 54 {
 55 data.enforceInterface(DESCRIPTOR);
 56 com.example.administrator.writebindercodeexample.Person _arg0;
 57 if ((0!=data.readInt())) {
 58 _arg0 = com.example.administrator.writebindercodeexample.Person.CREATOR.createFromParcel(data);
 59 }
 60 else {
 61 _arg0 = null;
 62 }
 63 this.addPerson(_arg0);
 64 reply.writeNoException();
 65 return true;
 66 }
 67 }
 68 return super.onTransact(code, data, reply, flags);
 69 }
 70 private static class Proxy implements com.example.administrator.writebindercodeexample.IPersonManager
 71 {
 72 private android.os.IBinder mRemote;
 73 Proxy(android.os.IBinder remote)
 74 {
 75 mRemote = remote;
 76 }
 77 @Override public android.os.IBinder asBinder()
 78 {
 79 return mRemote;
 80 }
 81 public java.lang.String getInterfaceDescriptor()
 82 {
 83 return DESCRIPTOR;
 84 }
 85 @Override public java.util.List<com.example.administrator.writebindercodeexample.Person> getPersonList() throws android.os.RemoteException
 86 {
 87 android.os.Parcel _data = android.os.Parcel.obtain();
 88 android.os.Parcel _reply = android.os.Parcel.obtain();
 89 java.util.List<com.example.administrator.writebindercodeexample.Person> _result;
 90 try {
 91 _data.writeInterfaceToken(DESCRIPTOR);
 92 mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
 93 _reply.readException();
 94 _result = _reply.createTypedArrayList(com.example.administrator.writebindercodeexample.Person.CREATOR);
 95 }
 96 finally {
 97 _reply.recycle();
 98 _data.recycle();
 99 }
100 return _result;
101 }
102 //关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说
103 
104 @Override public void addPerson(com.example.administrator.writebindercodeexample.Person person) throws android.os.RemoteException
105 {
106 android.os.Parcel _data = android.os.Parcel.obtain();
107 android.os.Parcel _reply = android.os.Parcel.obtain();
108 try {
109 _data.writeInterfaceToken(DESCRIPTOR);
110 if ((person!=null)) {
111 _data.writeInt(1);
112 person.writeToParcel(_data, 0);
113 }
114 else {
115 _data.writeInt(0);
116 }
117 mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
118 _reply.readException();
119 }
120 finally {
121 _reply.recycle();
122 _data.recycle();
123 }
124 }
125 }
126 static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
127 static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
128 }
129 public java.util.List<com.example.administrator.writebindercodeexample.Person> getPersonList() throws android.os.RemoteException;
130 //关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说
131 
132 public void addPerson(com.example.administrator.writebindercodeexample.Person person) throws android.os.RemoteException;
133 }

看上去呢,杂乱无章, 但其实也就是100多行,所以 我调整了一下 这个代码的顺序 ,你们可以看的更清楚,同时也增加了注释:

 

  1 package com.example.administrator.aidlmessagetest;
  2 
  3 //为了让大家看的更清楚 我把生成的binder代码 给拷贝到另外一个工程下面了,并且用ide 给他format
  4 //所以包名和我们一开始前面的代码都不一样,大家理解意思就行。
  5 
  6 
  7 //从前面几行就能看出来 生成的代码是一个 interface ,只不过这个interface是 android.os.IInterface 的子类!
  8 public interface IPersonManager extends android.os.IInterface {
  9 
 10     //并且这个接口里 有一个静态的抽象类Stub(注意这个名字是固定的 永远都是Stub 不会是其他)
 11     //并且这个Stub是Binder的子类,并且实现了IPersonManager 这个接口
 12     public static abstract class Stub extends android.os.Binder implements com.example.administrator.aidlmessagetest.IPersonManager {
 13         //这个东西就是唯一的binder标示 可以看到就是IPersonManager的全路径名
 14         private static final java.lang.String DESCRIPTOR = "com.example.administrator.aidlmessagetest.IPersonManager";
 15 
 16         /**
 17          * 这个就是Stub的构造方法,回顾一下 我们如果写好aidl文件以后 写的service里面 是怎么写的?
 18          *
 19          * private final IPersonManager.Stub mBinder = new IPersonManager.Stub() {}
 20          * 我们都是这么写的 对吧~~所以想想我们的service里面的代码 就能辅助理解 这里的代码了
 21          */
 22         public Stub() {
 23             this.attachInterface(this, DESCRIPTOR);
 24         }
 25 
 26 
 27         //这个方法 其实就做了一件事,如果是同一个进程,那么就返回Stub对象本身
 28         //如果不是同一个进程,就返回Stub.Proxy这个代理对象了
 29         public static com.example.administrator.aidlmessagetest.IPersonManager asInterface(android.os.IBinder obj) {
 30             if ((obj == null)) {
 31                 return null;
 32             }
 33             android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
 34             //如果是同1个进程,也就是说进程内通信的话 我们就返回括号内里的对象
 35             if (((iin != null) && (iin instanceof com.example.administrator.aidlmessagetest.IPersonManager))) {
 36                 return ((com.example.administrator.aidlmessagetest.IPersonManager) iin);
 37             }
 38             //如果不是同一进程,是2个进程之间相互通信,那我们就得返回这个Stub.Proxy 看上去叫Stub 代理的对象了
 39             return new com.example.administrator.aidlmessagetest.IPersonManager.Stub.Proxy(obj);
 40         }
 41 
 42         //返回当前对象
 43         @Override
 44         public android.os.IBinder asBinder() {
 45             return this;
 46         }
 47 
 48         //只有在多进程通信的时候 才会调用这个方法 ,同一个进程是不会调用的。
 49 
 50         //首先 我们要明白 这个方法 一般情况下 都是返回true的,也只有返回true的时候才有意义,如果返回false了 就代表这个方法执行失败,
 51         //所以我们通常是用这个方法来做权限认证的,其实也很好理解,既然是多进程通信,那么我们服务端的进程当然不希望谁都能过来调用
 52         //所以权限认证是必须的,关于权限认证的代码 以后我再讲 先略过。
 53 
 54         //除此之外 ,onTransact 这个方法 就是运行在Binder线程池中的,一般就是客户端发起请求,然后android底层代码把这个客户端发起的
 55         //请求 封装成3个参数 来调用这个onTransact方法,第一个参数code 就代表客户端想要调用服务端 方法的 标志位。
 56         //其实也很好理解 服务端可能有n个方法 每个方法 都有一个对应的int值来代表,这个code就是这个int值,用来标示客户端想调用的服务端的方法
 57         //data就是方法参数,reply就是方法返回值。都很好理解
 58 
 59         //其实隐藏了很重要的一点,这个方法既然是运行在binder线程池中的,所以在这个方法里面调用的服务器方法也是运行在Binder线程池中的,
 60         //所以我们要记得 如果你的服务端程序 有可能和多个客户端相联的话,你方法里使用的那些参数 必须要是支持异步的,否则的话
 61         //值就会错乱了!这点一定要记住!结论就是Binder方法 一定要是同步方法!!!!!!
 62         @Override
 63         public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
 64             switch (code) {
 65                 case INTERFACE_TRANSACTION: {
 66                     reply.writeString(DESCRIPTOR);
 67                     return true;
 68                 }
 69                 case TRANSACTION_getPersonList: {
 70                     data.enforceInterface(DESCRIPTOR);
 71                     java.util.List<com.example.administrator.aidlmessagetest.Person> _result = this.getPersonList();
 72                     reply.writeNoException();
 73                     reply.writeTypedList(_result);
 74                     return true;
 75                 }
 76                 case TRANSACTION_addPerson: {
 77                     data.enforceInterface(DESCRIPTOR);
 78                     com.example.administrator.aidlmessagetest.Person _arg0;
 79                     if ((0 != data.readInt())) {
 80                         _arg0 = com.example.administrator.aidlmessagetest.Person.CREATOR.createFromParcel(data);
 81                     } else {
 82                         _arg0 = null;
 83                     }
 84                     this.addPerson(_arg0);
 85                     reply.writeNoException();
 86                     return true;
 87                 }
 88             }
 89             return super.onTransact(code, data, reply, flags);
 90         }
 91 
 92         //注意这里的Proxy 这个类名也是不变的,从前文我们知道 只有在多进程通信的情况下  才会返回这个代理的对象
 93         private static class Proxy implements com.example.administrator.aidlmessagetest.IPersonManager {
 94             private android.os.IBinder mRemote;
 95 
 96             Proxy(android.os.IBinder remote) {
 97                 mRemote = remote;
 98             }
 99 
100             @Override
101             public android.os.IBinder asBinder() {
102                 return mRemote;
103             }
104 
105             public java.lang.String getInterfaceDescriptor() {
106                 return DESCRIPTOR;
107             }
108 
109 
110             //这里我们一共有2个方法 一个getPersonList 一个addPerson 我们就分析一个方法就可以了
111             //并且要知道 这2个方法运行在客户端!!!!!!!!!!!!!!!!
112             //首先就是创建了3个对象_data 输入对象,_reply输出对象,_result返回值对象
113             //然后把参数信息 写入到_data里,接着就调用了transact这个方法 来发送rpc请求,然后接着
114             //当前线程挂起, 服务端的onTransace方法才被调用,调用结束以后 当前线程继续执行,直到
115             //从_reply中取出rpc的返回结果 然后返回_reply的数据
116 
117             //所以这里我们就要注意了,客户端发起调用远程请求时,当前客户端的线程就会被挂起了,
118             //所以如果一个远程方法 很耗时,我们客户端就一定不能在ui main线程里在发起这个rpc请求,不然就anr了。
119             @Override
120             public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException {
121                 android.os.Parcel _data = android.os.Parcel.obtain();
122                 android.os.Parcel _reply = android.os.Parcel.obtain();
123                 java.util.List<com.example.administrator.aidlmessagetest.Person> _result;
124                 try {
125                     _data.writeInterfaceToken(DESCRIPTOR);
126                     mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
127                     _reply.readException();
128                     _result = _reply.createTypedArrayList(com.example.administrator.aidlmessagetest.Person.CREATOR);
129                 } finally {
130                     _reply.recycle();
131                     _data.recycle();
132                 }
133                 return _result;
134             }
135 
136             //你看自动生成binder代码的时候 连你的注释也一起拷贝过来了。。。。。是不是很有趣
137             //关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说
138 
139             @Override
140             public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException {
141                 android.os.Parcel _data = android.os.Parcel.obtain();
142                 android.os.Parcel _reply = android.os.Parcel.obtain();
143                 try {
144                     _data.writeInterfaceToken(DESCRIPTOR);
145                     if ((person != null)) {
146                         _data.writeInt(1);
147                         person.writeToParcel(_data, 0);
148                     } else {
149                         _data.writeInt(0);
150                     }
151                     mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
152                     _reply.readException();
153                 } finally {
154                     _reply.recycle();
155                     _data.recycle();
156                 }
157             }
158         }
159 
160         static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
161         static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
162     }
163 
164     public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException;
165 //关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说
166 
167     public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException;
168 }

到这里 相信大家 至少在应用层上面,就对Binder就一个很直观的理解了,对于进程间通信来说,具体的流程就分为如下几步:

1.Client 发起远程调用请求 也就是RPC 到Binder。同时将自己挂起,挂起的原因是要等待RPC调用结束以后返回的结果

2.Binder 收到RPC请求以后 把参数收集一下,调用transact方法,把RPC请求转发给service端。

3.service端 收到rpc请求以后 就去线程池里 找一个空闲的线程去走service端的 onTransact方法 ,实际上也就是真正在运行service端的 方法了,等方法运行结束 就把结果 写回到binder中。

4.Binder 收到返回数据以后 就唤醒原来的Client 线程,返回结果。至此,一次进程间通信 的过程就结束了

搞明白以后 我们就可以来尝试着 手下一下Binder:(前面我们aidl 帮我们生成的binder 是人,也就是person,那这次我们自己写的时候 就用狗吧,用DOG)

首先定义一个Dog.java: 实际上和person 一样的 所以这里暂时把代码折叠起来。

技术分享
 1 package com.example.administrator.writebindercodeexample;
 2 
 3 import android.os.Parcel;
 4 import android.os.Parcelable;
 5 
 6 /**
 7  * Created by Administrator on 2016/1/27.
 8  */
 9 public class Dog implements Parcelable {
10 
11     public int getGender() {
12         return gender;
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public void setGender(int gender) {
20         this.gender = gender;
21     }
22 
23     public void setName(String name) {
24         this.name = name;
25     }
26 
27     private int gender;
28     private String name;
29 
30 
31     @Override
32     public int describeContents() {
33         return 0;
34     }
35 
36     @Override
37     public void writeToParcel(Parcel dest, int flags) {
38         dest.writeInt(this.gender);
39         dest.writeString(this.name);
40     }
41 
42     public Dog() {
43     }
44 
45     protected Dog(Parcel in) {
46         this.gender = in.readInt();
47         this.name = in.readString();
48     }
49 
50     public static final Parcelable.Creator<Dog> CREATOR = new Parcelable.Creator<Dog>() {
51         public Dog createFromParcel(Parcel source) {
52             return new Dog(source);
53         }
54 
55         public Dog[] newArray(int size) {
56             return new Dog[size];
57         }
58     };
59 }
View Code

然后写一个接口IDogManager

 1 package com.example.administrator.writebindercodeexample;
 2 
 3 import android.os.IBinder;
 4 import android

以上是关于Android 手写Binder 教你理解android中的进程间通信的主要内容,如果未能解决你的问题,请参考以下文章

Android开发之漫漫长途 IX——彻底掌握Binder

Android-深入理解Binder

Android-深入理解Binder

转 理解Android系统Binder机制

深入理解Binder

彻底理解 Android Binder 通信架构