关于Binder,我就不解释的太多了,网上一搜资料一堆,但是估计还是很多人理解的有困难。今天就教你如何从 app层面来理解好Binder。
其实就从我们普通app开发者的角度来看,仅仅对于android应用层的话,Binder就是客户端和服务端进行通信的媒介。
AIDL就是我们理解Binder 最好的事例。
我们都知道 我们写好aidl 文件以后,开发工具 会自动帮我们生成好代码。实际上 我们最终apk里面 是只有这些代码的,我们写的aidl文件
是不会被打包进去的,也就是说aidl文件 实际上 就是我们用来 生成 实际binder代码用的。所以 我们只要能够分析好,ide自动帮我们生成的
代码,就可以自己手写binder,从而在app层面上真正理解binder的用法和含义 以及原理。
首先我先来定义一个实体类:Person.java
package com.example.administrator.writebindercodeexample; import android.os.Parcel;
import android.os.Parcelable; /**
* Created by Administrator on 2016/1/27.
*/
public class Person implements Parcelable { private String name; public void setName(String name) {
this.name = name;
} public void setGender(int gender) {
this.gender = gender;
} public int getGender() {
return gender;
} public String getName() {
return name;
} private int gender; @Override
public int describeContents() {
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.gender);
} public Person() {
} protected Person(Parcel in) {
this.name = in.readString();
this.gender = in.readInt();
} public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
public Person createFromParcel(Parcel source) {
return new Person(source);
} public Person[] newArray(int size) {
return new Person[size];
}
};
}
注意看 我们这个person 类 是实现了android自带的序列化接口的,所以 如果你要在aidl里使用这个类,那你必须要额外在aidl里生命下 这个类。
// Person.aidl.aidl
package com.example.administrator.writebindercodeexample; // Declare any non-default types here with import statements
parcelable Person;
// IPersonManager.aidl
package com.example.administrator.writebindercodeexample; // Declare any non-default types here with import statements
import com.example.administrator.writebindercodeexample.Person;
interface IPersonManager {
List<Person> getPersonList();
//关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说
void addPerson(in Person person);
}
好,然后给你们看一下 文件结构:
好 这里就是一个典型的 应用aidl 技术的 一个例子,我们现在 让studio 编译这个project,然后看看生成的binder代码。 把这份binder代码 分析好了,我们以后就可以不借助ide 来自己手写binder了。
我们来看看 生成的代码在哪里:
最后我们来看一下 这个生成的代码 是啥样的:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Users\\Administrator\\WriteBinderCodeExample\\app\\src\\main\\aidl\\com\\example\\administrator\\writebindercodeexample\\IPersonManager.aidl
*/
package com.example.administrator.writebindercodeexample;
public interface IPersonManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.administrator.writebindercodeexample.IPersonManager
{
private static final java.lang.String DESCRIPTOR = "com.example.administrator.writebindercodeexample.IPersonManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.administrator.writebindercodeexample.IPersonManager interface,
* generating a proxy if needed.
*/
public static com.example.administrator.writebindercodeexample.IPersonManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.administrator.writebindercodeexample.IPersonManager))) {
return ((com.example.administrator.writebindercodeexample.IPersonManager)iin);
}
return new com.example.administrator.writebindercodeexample.IPersonManager.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_getPersonList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.administrator.writebindercodeexample.Person> _result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addPerson:
{
data.enforceInterface(DESCRIPTOR);
com.example.administrator.writebindercodeexample.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.administrator.writebindercodeexample.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.administrator.writebindercodeexample.IPersonManager
{
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 java.util.List<com.example.administrator.writebindercodeexample.Person> getPersonList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.administrator.writebindercodeexample.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.administrator.writebindercodeexample.Person.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
//关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说 @Override public void addPerson(com.example.administrator.writebindercodeexample.Person person) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person!=null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.example.administrator.writebindercodeexample.Person> getPersonList() throws android.os.RemoteException;
//关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说 public void addPerson(com.example.administrator.writebindercodeexample.Person person) throws android.os.RemoteException;
}
看上去呢,杂乱无章, 但其实也就是100多行,所以 我调整了一下 这个代码的顺序 ,你们可以看的更清楚,同时也增加了注释:
package com.example.administrator.aidlmessagetest; //为了让大家看的更清楚 我把生成的binder代码 给拷贝到另外一个工程下面了,并且用ide 给他format
//所以包名和我们一开始前面的代码都不一样,大家理解意思就行。 //从前面几行就能看出来 生成的代码是一个 interface ,只不过这个interface是 android.os.IInterface 的子类!
public interface IPersonManager extends android.os.IInterface { //并且这个接口里 有一个静态的抽象类Stub(注意这个名字是固定的 永远都是Stub 不会是其他)
//并且这个Stub是Binder的子类,并且实现了IPersonManager 这个接口
public static abstract class Stub extends android.os.Binder implements com.example.administrator.aidlmessagetest.IPersonManager {
//这个东西就是唯一的binder标示 可以看到就是IPersonManager的全路径名
private static final java.lang.String DESCRIPTOR = "com.example.administrator.aidlmessagetest.IPersonManager"; /**
* 这个就是Stub的构造方法,回顾一下 我们如果写好aidl文件以后 写的service里面 是怎么写的?
*
* private final IPersonManager.Stub mBinder = new IPersonManager.Stub() {}
* 我们都是这么写的 对吧~~所以想想我们的service里面的代码 就能辅助理解 这里的代码了
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
} //这个方法 其实就做了一件事,如果是同一个进程,那么就返回Stub对象本身
//如果不是同一个进程,就返回Stub.Proxy这个代理对象了
public static com.example.administrator.aidlmessagetest.IPersonManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//如果是同1个进程,也就是说进程内通信的话 我们就返回括号内里的对象
if (((iin != null) && (iin instanceof com.example.administrator.aidlmessagetest.IPersonManager))) {
return ((com.example.administrator.aidlmessagetest.IPersonManager) iin);
}
//如果不是同一进程,是2个进程之间相互通信,那我们就得返回这个Stub.Proxy 看上去叫Stub 代理的对象了
return new com.example.administrator.aidlmessagetest.IPersonManager.Stub.Proxy(obj);
} //返回当前对象
@Override
public android.os.IBinder asBinder() {
return this;
} //只有在多进程通信的时候 才会调用这个方法 ,同一个进程是不会调用的。 //首先 我们要明白 这个方法 一般情况下 都是返回true的,也只有返回true的时候才有意义,如果返回false了 就代表这个方法执行失败,
//所以我们通常是用这个方法来做权限认证的,其实也很好理解,既然是多进程通信,那么我们服务端的进程当然不希望谁都能过来调用
//所以权限认证是必须的,关于权限认证的代码 以后我再讲 先略过。 //除此之外 ,onTransact 这个方法 就是运行在Binder线程池中的,一般就是客户端发起请求,然后android底层代码把这个客户端发起的
//请求 封装成3个参数 来调用这个onTransact方法,第一个参数code 就代表客户端想要调用服务端 方法的 标志位。
//其实也很好理解 服务端可能有n个方法 每个方法 都有一个对应的int值来代表,这个code就是这个int值,用来标示客户端想调用的服务端的方法
//data就是方法参数,reply就是方法返回值。都很好理解 //其实隐藏了很重要的一点,这个方法既然是运行在binder线程池中的,所以在这个方法里面调用的服务器方法也是运行在Binder线程池中的,
//所以我们要记得 如果你的服务端程序 有可能和多个客户端相联的话,你方法里使用的那些参数 必须要是支持异步的,否则的话
//值就会错乱了!这点一定要记住!结论就是Binder方法 一定要是同步方法!!!!!!
@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_getPersonList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.administrator.aidlmessagetest.Person> _result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addPerson: {
data.enforceInterface(DESCRIPTOR);
com.example.administrator.aidlmessagetest.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.administrator.aidlmessagetest.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
} //注意这里的Proxy 这个类名也是不变的,从前文我们知道 只有在多进程通信的情况下 才会返回这个代理的对象
private static class Proxy implements com.example.administrator.aidlmessagetest.IPersonManager {
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;
} //这里我们一共有2个方法 一个getPersonList 一个addPerson 我们就分析一个方法就可以了
//并且要知道 这2个方法运行在客户端!!!!!!!!!!!!!!!!
//首先就是创建了3个对象_data 输入对象,_reply输出对象,_result返回值对象
//然后把参数信息 写入到_data里,接着就调用了transact这个方法 来发送rpc请求,然后接着
//当前线程挂起, 服务端的onTransace方法才被调用,调用结束以后 当前线程继续执行,直到
//从_reply中取出rpc的返回结果 然后返回_reply的数据 //所以这里我们就要注意了,客户端发起调用远程请求时,当前客户端的线程就会被挂起了,
//所以如果一个远程方法 很耗时,我们客户端就一定不能在ui main线程里在发起这个rpc请求,不然就anr了。
@Override
public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.administrator.aidlmessagetest.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.administrator.aidlmessagetest.Person.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
} //你看自动生成binder代码的时候 连你的注释也一起拷贝过来了。。。。。是不是很有趣
//关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说 @Override
public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
} static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
} public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException;
//关于这个参数in 其实你不加也是可以编译通过的,这里我就先加一下 具体参数的意义 以后会说 public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException;
}
到这里 相信大家 至少在应用层上面,就对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 一样的 所以这里暂时把代码折叠起来。
package com.example.administrator.writebindercodeexample; import android.os.Parcel;
import android.os.Parcelable; /**
* Created by Administrator on 2016/1/27.
*/
public class Dog implements Parcelable { public int getGender() {
return gender;
} public String getName() {
return name;
} public void setGender(int gender) {
this.gender = gender;
} public void setName(String name) {
this.name = name;
} private int gender;
private String name; @Override
public int describeContents() {
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.gender);
dest.writeString(this.name);
} public Dog() {
} protected Dog(Parcel in) {
this.gender = in.readInt();
this.name = in.readString();
} public static final Parcelable.Creator<Dog> CREATOR = new Parcelable.Creator<Dog>() {
public Dog createFromParcel(Parcel source) {
return new Dog(source);
} public Dog[] newArray(int size) {
return new Dog[size];
}
};
}
然后写一个接口IDogManager
package com.example.administrator.writebindercodeexample; import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException; import java.util.List; /**
* Created by Administrator on 2016/1/27.
*/
public interface IDogManager extends IInterface { static final String DESCRIPTOR = "com.example.administrator.writebindercodeexample.IDogManager";
static final int TRANSACTION_getDogList = IBinder.FIRST_CALL_TRANSACTION + 0;
static final int TRANSACTION_addDog = IBinder.FIRST_CALL_TRANSACTION + 1; public List<Dog> getDogList() throws RemoteException; public void addDog(Dog dog) throws RemoteException; }
然后写我们的binder,注意我们的binder 我这里是写的抽象类,因为你写成实体类的话 就必须要实现IDogManger里的2个方法 ,然而为了结构清晰 我们并不准备把binder 放在service里 实现。
所以这里binder 我们还是用抽象类来做,然后在service里 实现 getDogList和addDog方法即可。
package com.example.administrator.writebindercodeexample; import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException; /**
* Created by Administrator on 2016/1/27.
*/
public abstract class DogManagerImpl extends Binder implements IDogManager { public DogManagerImpl() {
this.attachInterface(this, DESCRIPTOR);
} public static com.example.administrator.writebindercodeexample.IDogManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//如果是同1个进程,也就是说进程内通信的话 我们就返回括号内里的对象
if (((iin != null) && (iin instanceof com.example.administrator.writebindercodeexample.IDogManager))) {
return ((com.example.administrator.writebindercodeexample.IDogManager) iin);
}
//如果不是同一进程,是2个进程之间相互通信,那我们就得返回这个Stub.Proxy 看上去叫Stub 代理的对象了
return new com.example.administrator.writebindercodeexample.DogManagerImpl.Proxy(obj);
} @Override
public IBinder asBinder() {
return this;
} @Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getDogList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.administrator.writebindercodeexample.Dog> _result = this.getDogList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addDog: {
data.enforceInterface(DESCRIPTOR);
com.example.administrator.writebindercodeexample.Dog _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.administrator.writebindercodeexample.Dog.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addDog(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
} private static class Proxy extends DogManagerImpl {
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 java.util.List<com.example.administrator.writebindercodeexample.Dog> getDogList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.administrator.writebindercodeexample.Dog> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(DogManagerImpl.TRANSACTION_getDogList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.administrator.writebindercodeexample.Dog.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
} @Override
public void addDog(com.example.administrator.writebindercodeexample.Dog dog) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((dog != null)) {
_data.writeInt(1);
dog.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(DogManagerImpl.TRANSACTION_addDog, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
} }
到这,我们的手写binder 就完成了,然后看看 service 以及客户端 怎么调用。
先看service:
package com.example.administrator.writebindercodeexample; import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log; import java.util.ArrayList;
import java.util.List; public class RemoteService extends Service { private List<Dog> mDogsList = new ArrayList<Dog>(); private final DogManagerImpl mBinder = new DogManagerImpl() {
@Override
public List<Dog> getDogList() throws RemoteException {
return mDogsList;
} @Override
public void addDog(Dog dog) throws RemoteException {
mDogsList.add(dog);
}
}; @Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
然后看看 启动如何在客户端bind 这个service:
private IDogManager mService; private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = DogManagerImpl.asInterface(service); } @Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
};
到这 就基本写完了,手写binder的好处就是 你可以自己在binder方法里 写一些log,能够更加深刻的认识到 Binder 作为 进程间通信 媒介的重要作用以及原理。
熟悉以后,还是用aidl 的方法 自动生成代码 最好。