标签:
通常情况下,作为一个android开发者不会直接接触到Binder,但Binder作为ipc机制最关键的一个环节,我们很有必要去了解他。其实在不知不觉中,大家肯定和Binder打过交道,比如我们bindService的时候,客户端会获取到一个远程服务器发送回来的Binder对象,通过操作这个对象我们可以获取服务端的数据或者执行某些服务端的操作。再比如,我么在获取各种系统服务的时候,Binder是作为serviceManager连接各种manager(windowManager.....)的桥梁。接下来会为大家讲解到几个涉及到Binder通信相关的类,我们有必要先了解相关的类和接口,才能在后续解读aidl工具为我们生成的代码时不至于迷糊不清,这几个类和接口分别是Parcel,Parcelable,IInterface,Binder,Ibinder。
IInterface接口是所有涉及到Binder接口的基类,通常情况下,当我们定义一个继承了Binder类的子类的时候,该子类一定要实现一个继承了IInterface接口的接口(有的时候也可以直接用子类去实现IIterface接口)。IInterface接口里边只有一个待实现方法asBinder方法,用于返回与当前接口相关联的Binder的对象。这样太抽象不好理解,让我们跟着步骤一个一个来:
首先我们看看IInterface接口的源码:
//这是任何实现Binder的子类必须实现的接口 public interface IInterface { //用来获取与当前接口关联的Binder对象 public IBinder asBinder(); }
public interface IBookManager extends IInterface { public static final String DESCRIPTOR = "com.hy.blog.iBookManager"; //用来辨别被远程客户端调用的方法,数值必须介于Binder.FIRST_CALL_TRANSACTION到Binder.LAST_CALL_TRANSACTION //一般一个方法一个常量标志,这里不懂没关系,后边还会继续详解 public static final int REMOTE_ADD_BOOK = Binder.FIRST_CALL_TRANSACTION + 0; public static final int REMOTE_GET_BOOK = Binder.FIRST_CALL_TRANSACTION + 1; List<Book> getBook(); void addBook(Book book); }
public class BookManager extends Binder implements IBookManager { //这里会有一些具体的实现,此处省略,后续内容会继续展开。关键要理解实现IInterface接口目的在于实现asBinder方法将当前对象返回给客户端 @Override public IBinder asBinder() { return this; } }
从上面可以得出我们要实现一个Binder子类就必须去实现IInterface接口。
Parcel是一种包含数据或者对象引用、可以在IBInder之间传输的消息容器,Parcel包含了大量的针对不同类型数据在进程间进行读写的方法。并且通过Parcel可以在远程进程通信交流的时候将远程进程的IBinder代理对象与本地的IBinder对象绑定起来。Parcel并不是一种多用途的序列化机制,它只是被设计来作为一种高性能IPC通信的传输机制,并且包含在Parcel里面的数据对象除了基本类型以外必须要实现Parcelable接口。因此Parcel数据不适合用来做持久化存储,因为对任何在Parcel里面的数据进行改变都可能会导致之前保存在Parcel里面的数据变得不可读。
Parcel提供了大量的接口用来读取不同类型的数据,它们主要可以分为六大块,下面会讲解常用的四大块:
1、基本类型函数:
这是最基本的函数,都是用来读取基本数据类型的,后面提到的其他类型的读写都是基于这些基本函数来操作的,我们需要注意的一点是,Parcel的数据读写是按照Cpu字节次序来读写的。函数如下:
public final void writeByte(byte val) | public final byte readByte() |
public final void writeInt(int val) | public final int readInt() |
public final void writeFloat(float val) | public final float readFloat() |
public final void writeDouble(double val) | public final double readDouble() |
public final void wrtieLong(long val) | public final long readLong() |
public final void writeString (String val) | public final String readString() |
2、基本类型的数组函数:
相比基本类型的读写函数,基本类型的数组读写函数多了一个createxxxArray方法(xxx可以替代任意基本类型,比如int),其中createxxxArray函数用来创建一个新的数组并将读取的内容放进新的数组里面然后返回,readxxxArray方法是用来将读取的数据放在一个已存在的数组里面并返回。函数如下:
3、实现了Parcelable接口的对象:
Parcelable是用来从Parcel读取数据的一种非常高效率的机制。你可以用readParcelable方法和readParcelableArray方法读取数据,用writeParcelable和writeParcelableArray来将数据写入Parcel。这两个方法会将相应的类的类型和数据一同写入Parcel里面,所以在读取数据的时候必须传入当前的线程的类的加载器,用来寻找合适的类来重构Parcelable对象。
还有一些更高效的读写Parcelable对象的方法,writeTypeObject,writeTypeList,writeTypeArray,readTypeObject,createTypeList,createTypeArray......这些方法在写入数据的时候并不需要把原始对象的类的信息写进Parcel,相反,在读取数据的时候只要传递相应的Parcelable.CREATOR对象就可以将数据从Parcel里面还原。如果仅仅是读写单个Parcelable对象,使用Parcelable.writeToParcel和Parcelable.CREATOR.createFromParcle是最高效的。
4、Bundle对象:
Bundle是一种键值对形式的非常安全的数据容器,它在读写数据有非常好的表现,并且从Parcel里面恢复数据是可以很好的避免类型转换的错误,读写Bundle数据的方法如下:writeBundle和readBundle。
IBinder是一种远程对象接口,是一种轻量级的并且高性能的远程调用机制。这个接口描述了一些进行远程交流的对象的基本守则,但是当我们进行远程对象的交流的时候,不要直接实现IBinder接口,而应该继承BInder类,因为这个类为远程通信做了基本的实现,我们只需要重写部分方法来到达我们的目的即可。对于IBinde需要知道以下几点:
1、IBinder接口最重要的方法是transact()方法,这个方法允许你通过调用本地IBinder对象来调用远程对象的Binde对象。注意这个方法是个同步方法,即本地调用IBinder对象的transact方法时不会立即返回结果,必须等待远程Binder对象执行完毕后才会返回结果,因此如果在这个过程中涉及到耗时操作,我们就不要在主线程去调用transact方法,不然会造成ARN。通过IBinder,可以让我们在调用远程对象的时候跟执行本地对象一样,没有任何区别。
2、transact方法传送的数据是通过Parcel传送的,其中除了数据内容之外会包含一些中介数据,这些数据用来在缓冲区保存IBinder对象的引用,这样即使在与远程进程进行交流的时候,仍然可以保存IBinder对象的引用。这样就确保了IBinder对象可以发送给不同的进程。
3、系统给每一个进程分配了一个事物管理的线程池,用来调度所有的远程进程的调用。比如A进程在自己的调用线程里调用了transact方法,然后该线程就会阻塞起来并将该事物发给B进程进行处理,然后B进程就会从自己的事务管理线程池里面拿出可用线程来进行处理,直至B进程处理完毕将结果返回给A进程,A进程才会继续执行。
4、Binder系统支持进程间的迭代调用,因为进程是优先执行Binder.OnTransact()方法的。比如A进程调用了B进程的Binder.onTransact()方法,然后在B进程又调用了A进程的Binder.onTransact方法,A进程会优先执行Binder.onTransact方法。
5、当我们进行远程对象调用的时候,因为需要阻塞自己的线程,所以在调用的时候必须判断远程进程的BInder对象是否存在,我们可以通过以下三个方法来判断:
①、调用transact方法时,如果抛出了RemoteException异常就说明远程对象不存在。
②、调用pingBinder方法,如果返回false也表示远程对象不存在。
③、通过linkToDeath方法注册一个DeathRecipient对象,如果远程对象不存在,那么DeathRecipient对象会被回调。
这个类是远程对象的基类,它实现了IBinder的接口并提供了一些标准的实现。所以如果我们要实现一个远程对象必须继承Binder而不是去实现IBinder接口。通常来说,作为一个开发者并不会直接去和Binder打交道,因为通过aidl工具,系统可以为我们生成我们所期望的Binder子类。但是,如果你想,你仍然可以自己实现Binder并定义远程通信的协议,然后实例化它并传给远程对象调用。
Binder对象进程键通信的基础,通常来说他并不会影响application的生命周期,但是Binder对象只有在进程运行的时候才能存在,所以我们必须保持进程是运行状态。一般来说进程可优先级可分为五个等级:
第一级:前台进程:即该进程存在正在与用户交互的组件。
第二级:可见进程:即该进程拥有可见状态的组件。
第三级:服务进程:即该进程拥有service组件。
第四级:后台进程:即该进程的组件正在执行onStop方法,处于不可见状态。
第五级:空进程:即进程什么都没有。
当系统因为内存不足决定回收哪个线程时,第五级是最容易被回收的,第一级是最难的(除非达到了使用虚拟内存的情况,否则不会回收前台进程),因此我们使用Binder对象,最好是基于四大组件运行,这样才会是进程处于可运行状态,Binder对象才不会被杀死。
通过上面的介绍,可以知道要实现一个可以远程调用的对象,必须继承Binder类(不要直接使用IBinder接口,因为Binder类为远程通信做了最基本的实现),同时要实现IInterface接口,在远程调用过程中,数据是通过Parcel来传递的所以要求传递的对象必须实现Parcelable接口。下面会通过两个例子来引导大家开发一个可供远程调用的对象。
首先,不用aidl工具,我们自己来写一个可供远程调用的远程对象。涉及到一个继承了Parcelable接口的Book类,因为要在进程间传递的对象必须要实现Parcelable接口。代码如下:
package com.example.myy.blog.binder; import android.os.Parcel; import android.os.Parcelable; /** * Created by Myy on 2016/7/12. */ public class Book implements Parcelable { private String name; private int id; public Book(String name, int id) { this.name = name; this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } private Book(Parcel in) { this.name = in.readString(); this.id = in.readInt(); } /** * 必须实现且名称必须是CREATOR */ public static final Creator<Book> CREATOR = new Creator<Book>() { /** * 将数据反序列化 * @param in */ @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; /** * 通常情况下返回0,当对象数据含有文件描述符的时候返回1 * * @return */ @Override public int describeContents() { return 0; } /** * 将数据序列化,flags通常为0,为1的时候表示当前对象需要做为返回值返回不能立即释放资源 * * @param dest * @param flags */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(id); } }其中每个方法的用途和特殊的地方都有注释,读者请认真阅读。然后我们需要写一个继承了IInterface接口的接口,写这个接口的用意在于将一些基本的常量标志(一般一个方法一个常量标志)和基本的方法写出来。IBookManager代码如下:
package com.example.myy.blog.binder; import android.os.Binder; import android.os.IInterface; import java.util.List; /** * 任何binder接口都必须继承IInterface接口 * Created by Myy on 2016/7/12. */ public interface IBookManager extends IInterface { public static final String DESCRIPTOR = "com.hy.blog.iBookManager"; //用来辨别远程客户端调用的方法,数值必须介于Binder.FIRST_CALL_TRANSACTION到Binder.LAST_CALL_TRANSACTION //一般一个方法一个常量标志 public static final int REMOTE_ADD_BOOK = Binder.FIRST_CALL_TRANSACTION + 0; public static final int REMOTE_GET_BOOK = Binder.FIRST_CALL_TRANSACTION + 1; List<Book> getBook(); void addBook(Book book); }其中DESCRIPTOR用来标识当前的Binder接口,通常作为进程间获取目标BInder对象的标志,所以使用我们的包名(可自定义任意字符串)即可。最后我们需要实现IBookManager方法,实现一些用于远程交流的基本方法。代码如下:
package com.example.myy.blog.binder; import android.os.Binder; import android.os.IBinder; import android.os.IInterface; import android.os.Parcel; import android.os.RemoteException; import java.util.ArrayList; import java.util.List; /** * Created by Myy on 2016/7/12. */ public class BookManager extends Binder implements IBookManager { private List<Book> books = new ArrayList<>(); /** * 给binder接口绑定token */ public BookManager() { this.attachInterface(this, DESCRIPTOR); } @Override public List<Book> getBook() { return books; } @Override public void addBook(Book book) { books.add(book); } @Override public IBinder asBinder() { return this; } public static IBookManager asInterface(IBinder binder) { if (binder == null) return null; IInterface iInterface = binder.queryLocalInterface(DESCRIPTOR); if ((iInterface != null) && (iInterface instanceof IBookManager)) return (IBookManager) iInterface; return new Proxy(binder); } @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case REMOTE_ADD_BOOK: data.enforceInterface(DESCRIPTOR); Book book = null; if (data.readInt() != 0) { book = Book.CREATOR.createFromParcel(data); this.addBook(book); reply.writeNoException(); return true; } else { reply.writeException(new NullPointerException("参数为空")); return false; } case REMOTE_GET_BOOK: data.enforceInterface(DESCRIPTOR); List<Book> list = null; list = this.getBook(); reply.writeNoException(); reply.writeTypedList(list); return true; } return super.onTransact(code, data, reply, flags); } public static class Proxy implements IBookManager { private IBinder remote; public Proxy(IBinder binder) { remote = binder; } @Override public List<Book> getBook() { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); List<Book> list = null; data.writeInterfaceToken(DESCRIPTOR); try { remote.transact(REMOTE_GET_BOOK, data, reply, 0); reply.readException(); list = reply.createTypedArrayList(Book.CREATOR); } catch (RemoteException e) { e.printStackTrace(); } finally { data.recycle(); reply.recycle(); } return list; } @Override public void addBook(Book book) { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(DESCRIPTOR); book.writeToParcel(data, 0); try { remote.transact(REMOTE_ADD_BOOK, data, reply, 0); reply.readException(); } catch (RemoteException e) { e.printStackTrace(); } finally { data.recycle(); reply.recycle(); } } @Override public IBinder asBinder() { return remote; } public static String getInterfaceDescriptor() { return DESCRIPTOR; } } }
1、
public BookManager() { this.attachInterface(this, DESCRIPTOR); }这个方法用于给当前的Binder对象添加标签,用于后续系统查找目标Binder对象。
2、
public IBinder asBinder() { return this; }这是IInterface接口的方法,目的是将当前对象返回。
3、
public static IBookManager asInterface(IBinder binder) { if (binder == null) return null; IInterface iInterface = binder.queryLocalInterface(DESCRIPTOR); if ((iInterface != null) && (iInterface instanceof IBookManager)) return (IBookManager) iInterface; return new Proxy(binder); }用来获取Binder对象的接口,当当前Binder对象在本地进程查不到指定标记的Binder接口时,就说明这个binder对象是一个远程对象,所以应该将本地的一个代理Binder对象返回给远程客户端。
4、
@Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case REMOTE_ADD_BOOK: data.enforceInterface(DESCRIPTOR); Book book = null; if (data.readInt() != 0) { book = Book.CREATOR.createFromParcel(data); this.addBook(book); reply.writeNoException(); return true; } else { reply.writeException(new NullPointerException("参数为空")); return false; } case REMOTE_GET_BOOK: data.enforceInterface(DESCRIPTOR); List<Book> list = null; list = this.getBook(); reply.writeNoException(); reply.writeTypedList(list); return true; } return super.onTransact(code, data, reply, flags); }这是Binder中最重要的回调方法,当客户端执行了transact方法时,就会回调对应的远程Binder对象的onTransact方法。其中code是用来区分当前客户端所调用的方法,data是输入参数,这个参数不能为NULL,即使你不需要传递任何参数你也应该传递一个空的Parcel对象。reply是含有远程客户端执行接口的Parcel对象,flags是附加的执行远程操作的标志,0表示正常的远程调用,1表示不需要返回值的one-way调用。
5、data.enforceInterface(DESCRIPTOR)用于说明当前的parcel对象是和制指定了DESCRIPTOR接口相关联的。writeNoException说明当前操作没有出现异常。
6、代理对象Proxy
public static class Proxy implements IBookManager { private IBinder remote; public Proxy(IBinder binder) { remote = binder; } @Override public List<Book> getBook() { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); List<Book> list = null; data.writeInterfaceToken(DESCRIPTOR); try { remote.transact(REMOTE_GET_BOOK, data, reply, 0); reply.readException(); list = reply.createTypedArrayList(Book.CREATOR); } catch (RemoteException e) { e.printStackTrace(); } finally { data.recycle(); reply.recycle(); } return list; } @Override public void addBook(Book book) { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(DESCRIPTOR); book.writeToParcel(data, 0); try { remote.transact(REMOTE_ADD_BOOK, data, reply, 0); reply.readException(); } catch (RemoteException e) { e.printStackTrace(); } finally { data.recycle(); reply.recycle(); } } @Override public IBinder asBinder() { return remote; } public static String getInterfaceDescriptor() { return DESCRIPTOR; } }这是BookManager的远程代理对象,当客户端是远程调用时,返回的就是当前的代理Binder对象。data.writeInterfaceToken用于说明给当前的数据加上中介数据用来标识data是给含有DESCRIPTOR标志的Binder接口的参数。remote.transact方法的参数和onTransact方法是一样的,当执行到transact方法,当前线程会阻塞,并调用远程对象的onTransact方法。注意parcel对象不再使用时必须调用recycle方法进行释放资源,以免造成内存泄漏。
package com.example.myy.blog.AIDL; import android.os.Parcel; import android.os.Parcelable; /** * Created by Myy on 2016/7/13. */ public class User implements Parcelable { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } protected User(Parcel in) { this.name = in.readString(); this.age = in.readInt(); } public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); } }为了便于大家参考,笔者的文件结构如下图所示:
// User.aidl package com.example.myy.blog.AIDL; parcelable User;UserManager.adil用于声明远程接口应该有的基本方法,在此文件中,任何类别都要使用完整的包名路径引入,即使在同一个包里面。在方法中in表示当前的是输入参数,out表示的当前参数是输出参数,除了基本类型之外,其它Parcelable对象都必须指明in,out。代码如下:
// UserManager.aidl package com.example.myy.blog.AIDL; import com.example.myy.blog.AIDL.User; import java.util.List; interface UserManager { List<User> getUser(); void addUser(in User user); }接着我们clean一下项目,之后在如下所示目录就可以找到系统为我们生成的远程对象:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: E:\\ascode\\Blog\\app\\src\\main\\aidl\\com\\example\\myy\\blog\\AIDL\\UserManager.aidl */ package com.example.myy.blog.AIDL; public interface UserManager extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.myy.blog.AIDL.UserManager { private static final java.lang.String DESCRIPTOR = "com.example.myy.blog.AIDL.UserManager"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.myy.blog.AIDL.UserManager interface, * generating a proxy if needed. */ public static com.example.myy.blog.AIDL.UserManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.myy.blog.AIDL.UserManager))) { return ((com.example.myy.blog.AIDL.UserManager) iin); } return new com.example.myy.blog.AIDL.UserManager.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_getUser: { data.enforceInterface(DESCRIPTOR); java.util.List<com.example.myy.blog.AIDL.User> _result = this.getUser(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addUser: { data.enforceInterface(DESCRIPTOR); com.example.myy.blog.AIDL.User _arg0; if ((0 != data.readInt())) { _arg0 = com.example.myy.blog.AIDL.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addUser(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.myy.blog.AIDL.UserManager { 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.myy.blog.AIDL.User> getUser() 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.myy.blog.AIDL.User> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.example.myy.blog.AIDL.User.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addUser(com.example.myy.blog.AIDL.User user) 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 ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public java.util.List<com.example.myy.blog.AIDL.User> getUser() throws android.os.RemoteException; public void addUser(com.example.myy.blog.AIDL.User user) throws android.os.RemoteException; }我们可以发现,系统生成的对象在结构上并没有我们自己写的那么清晰。但根本思路都是一样的。都是实现IInterface接口,继承Binder类,通过parcel传递Parcelable对象。
标签:
原文地址:http://blog.csdn.net/qq_25722767/article/details/51895992