Android binder
来源:互联网 发布:拆分系统源码下载 编辑:程序博客网 时间:2024/06/06 02:32
Binder在Android的重要性就不说了。看了很多文章,自己想总结一番,发现还是太菜,有那么多大牛已经说的很好,且都有系统性讲解。所以本篇参照《Android开发艺术探索》,重要记录下binder的使用,感受下其在IPC中的媒介作用,本篇作为笔记使用。
推荐文章
- 罗升阳Android进程间通信(IPC)机制Binder简要介绍和学习计划
- Gityuan Binder系列—开篇
- 图文详解 Android Binder跨进程通信的原理
- Android Binder 完全解析(一)概述
实在看不下去,就看第3篇吧。
这里借用一张图。
明白几点:
1. 各个进程相互隔离,数据不可共享。此处指的时用户空间。
2. 每个进程可分为用户空间、内核空间。用户空间数据不可共享,内核空间可以。
3. 1/2/3步骤的虚线表示我们开发中感受到的流程,也是Android想实现的CS流程。实际过程并不能直接联系。
4. Client、Server、Service Manager在用户空间,Binder驱动在内核空间。
5. 用户可以实现的时应用层内容,Client、Server;Service Manager和Binder是Android平台已经实现好了的。
下面我们看下Android中怎样使用Binder实现IPC。
< Android开发艺术探索 >
直观来说,Binder是Android中的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,他的设备驱动是/dev/binder,该通信方式linux中没有;从Android Framework 角度来说,Binder是ServiceManager 连接各种Manager(ActivityManager、WindowManager等)和相应ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当binderService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务和数据,这里的服务包括普通服务和AIDL服务。
在Android开发中,Binder一般用在service,而一般的service的Binder并不涉及进程间通信,这里我们主要分析AIDL。
在Android studio 中创建aidl
AIDL是Android中定义的接口语言,用它能实现IPC跨进程通信。
studio中生成aidl还是很方便的,使用自动生成即可。
之后,studio会自动为我们在main下创建一个aidl文件夹,里面的包名还是跟项目包名一致,看图。
此时的IBookManager里面还没有东西,basicTypes只是一个举例,看英文解释(举例一些在AIDL中可以作为参数和返回值的基本类型),其实哪能就这些,还玩儿不玩儿了,可参考官方说明Android 接口定义语言 (AIDL)。
interface IBookManager { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);}
分别在aild的对象包名下面,创建Book.java、Book.aidl,修改IBookManager.aidl,代码如下。
Book
package com.breezehan.ipc;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable { private int bookId; private String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } protected Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); }}
Book.aidl
// Book.aidlpackage com.breezehan.ipc;parcelable Book;
IBookManager.aidl
// IBookManager.aidlpackage com.breezehan.ipc;import com.breezehan.ipc.Book;interface IBookManager { List<Book> getBookList(); void addBook(in Book book);}
然后我们make project,就能在build中看到系统为我们生成的完整的IBookManager
注意,注意!有人说,毛线啊,我都报错了好不!
那是啊,没有错才有鬼呢。
因为默认我们的java文件都是在main->java下面的,你写在了adil下面,又没告诉studio,那肯定找不到了。
需要我们在module的build.gradle中设置。
android { compileSdkVersion 25 buildToolsVersion "25.0.3" defaultConfig { *** } sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/aidl'] } } }
分析IBookManager
上面三个类Book实现parcelable接口,Book.aidl是因为adil的特殊情况需要添加;IBookManager是一个接口,此处虽然和Book在同一个包中,但是仍需import,也是aidl特殊之处。
系统会根据IBookManager中的信息(比如里面我们添加了两个方法)生成对应的Binder类,看一下build->generated->source->aidl->com.breezehan.ipc
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\android_work\\workspace_studio\\AndroidKnowledgeSummary\\ipc\\src\\main\\aidl\\com\\breezehan\\ipc\\IBookManager.aidl */package com.breezehan.ipc;public interface IBookManager extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.breezehan.ipc.IBookManager { // 1.Binder的唯一标识,一般使用当前类名表示,带包名。 private static final java.lang.String DESCRIPTOR = "com.breezehan.ipc.IBookManager"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } // 2. 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这里转换时区分进程的,如果客户端和服务端处于同一进程,那么返回的就是Stub对象本身,否则返回的就是系统封装后的Stub.Proxy对象。 /** * Cast an IBinder object into an com.breezehan.ipc.IBookManager interface, * generating a proxy if needed. */ public static com.breezehan.ipc.IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.breezehan.ipc.IBookManager))) { return ((com.breezehan.ipc.IBookManager) iin); } return new com.breezehan.ipc.IBookManager.Stub.Proxy(obj); } // 3.返回当前binder对象 @Override public android.os.IBinder asBinder() { return this; } // 4.这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会被系统底层封装后交由此方法来处理。服务端通过code判断客户端请求的方法是哪个,接着从data取出目标方法所需参数(有参数的话),然后执行目标方法。当目标方法执行完毕后,就像reply中写入返回值(目标方法有返回值的话)。另外,onTransact有个boolean的返回值,当是false的时候,客户端的请求会失败,因此我们可以利用这个特性来做权限验证,毕竟我们也不希望随便一个进程就能调用我们的服务。 @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_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.breezehan.ipc.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.breezehan.ipc.Book _arg0; if ((0 != data.readInt())) { _arg0 = com.breezehan.ipc.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.breezehan.ipc.IBookManager { 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; } // 5.这个方法运行在客户端,当客户端远程调用此方法时:首先创建本方法所需的输入型Parcel对象_data、输出型Parcel对象_reply以及返回值对象List;然后把该方法的参数信息写入_data中(如果有参数的话);接着调用transact方法发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务端的onTransact方法调用,知道RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果;然后返回_reply中的数据。 @Override public java.util.List<com.breezehan.ipc.Book> getBookList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.breezehan.ipc.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.breezehan.ipc.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(com.breezehan.ipc.Book book) 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 ((book != null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public java.util.List<com.breezehan.ipc.Book> getBookList() throws android.os.RemoteException; public void addBook(com.breezehan.ipc.Book book) throws android.os.RemoteException;}
上面代码是自动生成的,这里只是格式化了一下。IBookManager本身是一个接口,这里又继承了IInterface接口(IInterface里面只有一个asBinder方法),但是想要在Binder中传输的接口都需要继承IInterface。
1. 里面有两个方法getBookList和addBook,这是我们在IBookManager.aidl文件中声明的。
2. 同时此处声明了一个内部类Stub,它是一个Binder,跨进程时,通过代理proxy的transact,最终在onTransact中根据不同id(不同id对应IBookManager的方法)执行相应的不同方法;同一个进程时,直接执行IBookManager中的方法。
两点需要注意:
1. 客户端发起远程请求时,会挂起一直到服务端返回数据,所以如果一个远程方法时很耗时的,不要在UI线程中发起此远程请求;
2. 由于服务端的Binder方法已经在Binder线程池,所以不管Binder方法是否耗时都应该采取同步的方式,因为已经在一个线程中了。下面是Binder机制图(来自Android开发艺术探索):
自己实现Binder
如果我们想要实现完整的IPC,需要一个Service
public class BookService extends Service{ private List<Book> mBookList = new ArrayList<>(); private IBinder binder = new IBookManager.Stub() { @Override public List<Book> getBookList() throws RemoteException { return mBookList; } @Override public void addBook(Book book) throws RemoteException { mBookList.add(book); } }; @Nullable @Override public IBinder onBind(Intent intent) { return binder; }}
当然,清单文件中需配置
<service android:name=".BookService" android:process=":remote" />
调用
private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager iBookManager = IBookManager.Stub.asInterface(service); try { iBookManager.addBook(new Book(1, "Java编程思想")); iBookManager.addBook(new Book(2, "Android开发艺术探索")); List<Book> bookList = iBookManager.getBookList(); Log.d(TAG, "onServiceConnected:" + bookList.toString()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindService(new Intent(this, BookService.class), mServiceConnection, Context.BIND_AUTO_CREATE); }
上面即是一个完整的IPC过程。
使用AIDL,我们不用关心很多东西,系统会帮助规范生成一系列的内容,那么我们能不能不借助AIDL,直接使用binder做到IPC呢。
先看service中binder的使用
private IBinder binder = new IBookManager.Stub() { @Override public List<Book> getBookList() throws RemoteException { return mBookList; } @Override public void addBook(Book book) throws RemoteException { mBookList.add(book); } };
所以我们可以实现一个Stub的对象,并且实现了IBookManager的方法;总体上,一个Stub,一个IBookManager就可以完事儿了。其他还一样,如Service的onBind。
手动实现Binder步骤
(1)、声明一个AIDL性质的接口,只需要继承IInterface即可。
public interface IUserManager extends IInterface { public static final String DESCRIPTOR = "com.breezehan.ipc.IUserManager"; public final static int TRANSACTION_getUserList = IBinder.FIRST_CALL_TRANSACTION + 0; public final static int TRANSACTION_addUser = IBinder.FIRST_CALL_TRANSACTION + 1; public List<User> getUserList() throws RemoteException; public void addUser(User user) throws RemoteException;}
看,是不是感觉像回事儿,当前此处 两个方法对象的id常量,我们仿照系统自动生成的写法,就是一个规则。无论多少方法,我们类似添加即可。
其中的public、static、final其实不明写,接口中这些都是默认的。
(2)实现Stub类,和Stub类中的Proxy代理类,不过此处写出来和系统生成几乎是一样的。
public class UserManagerImpl extends Binder implements IUserManager { private List<User> implList = new ArrayList<>(); public UserManagerImpl() { this.attachInterface(this, DESCRIPTOR); } public static IUserManager asInterface(IBinder obj) { if (obj == null) { return null; } IInterface iIn = obj.queryLocalInterface(DESCRIPTOR); if (iIn != null && iIn instanceof IUserManager) { return (IUserManager) iIn; } return new UserManagerImpl.Proxy(obj); } @Override public List<User> getUserList() throws RemoteException { //逻辑代码 return implList; } @Override public void addUser(User user) throws RemoteException { //逻辑代码 implList.add(user); } @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_addUser: data.enforceInterface(DESCRIPTOR); User _arg0; if (0 != data.readInt()) { _arg0 = User.CREATOR.createFromParcel(data); } else { _arg0 = null; } addUser(_arg0); reply.writeNoException(); return true; case TRANSACTION_getUserList: data.enforceInterface(DESCRIPTOR); List<User> _result = getUserList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } return super.onTransact(code, data, reply, flags); } static class Proxy implements IUserManager { private IBinder mRemote; public Proxy(IBinder remote) { this.mRemote = remote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public List<User> getUserList() throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); List<User> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(TRANSACTION_getUserList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(User.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addUser(User user) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if (user != null) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(TRANSACTION_addUser, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public IBinder asBinder() { return mRemote; } }}
完整代码参考手动实现Binder
结语
搞了半天,自己写的跟系统生成的差不多吗。手动去写,加深下Binder的理解,也知道AIDL并不是实现Binder的必须,只不过是系统的快速实现。原理都一样。
当Binder服务端因为某些原因异常终止,客户端会调用失败,最关键的是我们不知道两者连接是否已经断裂。这就需要给Binder设置一个死亡代理,当Binder停止时,客户端会收到一个通知,这时就可以做一些操作。
private IBookManager iBookManager;private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { if (iBookManager != null) { iBookManager.asBinder().unlinkToDeath(deathRecipient, 0); iBookManager = null; //重新连接或者其他操作 } }};private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iBookManager = IBookManager.Stub.asInterface(service); try { service.linkToDeath(deathRecipient, 0); iBookManager.addBook(new Book(1, "Java编程思想")); iBookManager.addBook(new Book(2, "Android开发艺术探索")); List<Book> bookList = iBookManager.getBookList(); Log.d(TAG, "onServiceConnected:" + bookList.toString()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { }};
- Android Binder -什么是binder
- Android Binder
- Android Binder
- android Binder
- Android-binder
- android Binder
- Android--Binder
- android binder
- Android Binder
- android binder
- android: binder
- 【Android】Binder
- Android BInder
- android binder
- android Binder
- Android Binder
- android binder
- Android Binder
- 配置Webpack-dev-server+初步了解react中元素、组件、事件、props传递
- 单例模式和多线程
- iOS PDF使用CATiledLayer展示
- 第08讲 mysql添加数据
- The SetStack Computer集合栈计算器(stack的运用)
- Android binder
- 501. Find Mode in Binary Search Tree
- java算法学习笔记--线性表篇
- 7-17数组下
- Struts2学习笔记(1)
- vim中自己容易忘记的命令
- ssh框架注解方式持久层无法注入sessionFactory解决方法
- 第09讲mysql修改数据
- HTML块级元素