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 }
然后写一个接口IDogManager
1 package com.example.administrator.writebindercodeexample; 2 3 import android.os.IBinder; 4 import android以上是关于Android 手写Binder 教你理解android中的进程间通信的主要内容,如果未能解决你的问题,请参考以下文章