目录
1.每日一句
时间总让人成长,但从不指明方向
2. 作者简介
🏡个人主页:XiaoChen_Android
📚学习专栏:Android
🕒发布日期:2022/9/19
本文是基于Android开发艺术探索中IPC机制所写,就当做做笔记啦
3.Binder简介
直观来说,Binder是Android中的一个类,它实现了IBinder接口
3.1 从三个角度看Binder
- 从IPC角度来看,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;
- 从Android Framework角度来看,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等等)和相应ManagerService的桥梁;
- 从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当binderService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和AIDL的服务
Android开发中,Binder主要应用在Service中,包括AIDL和Messenger,其中普通的Service中的Binder不涉及进程间通信,所以比较简单,无法触及Binder的核心,而Messenger的底层其实就是AIDL,所以这里选择用AIDL来分析Binder的工作机制。
为了分析Binder的工作机制,我们需要新建一个AIDL示例,SDK会自动为我们生产AIDL所对应的Binder类,然后我们就可以分析Binder的工作过程。
1.第一步,新建三个文件,Book.java、Book.aidl、IBookManager.aidl
package com.ryg.chapter_2.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private int bookid;
private String bookname;
public Book(int id, String name) {
this.bookid = id;
this.bookname = name;
}
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);
}
}
// IBookManager.aidl
package com.ryg.chapter_2.aidl;
// Declare any non-default types here with import statements
import comcom.ryg.chapter_2.aidl.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
// Book.aidl
package com.ryg.chapter_2.aidl;
// Declare any non-default types here with import statements
parcelable Book;
尽管Book类已经和IBookManager位于相同的包中,但是在后者仍然需要导入前者类,这就是AIDL的特殊之处,如果没导入则会有下面的结果
2.第二步,编译成功后,我们需要在java(generated)包中找到一个IBookManager.java类,这个类就是我们需要根据系统生成的Binder类来分析Binder的工作原理
代码如下:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.ryg.chapter_2.aidl;
public interface IBookManager extends android.os.IInterface
{
/** Default implementation for IBookManager. */
public static class Default implements com.ryg.chapter_2.aidl.IBookManager
{
@Override public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList() throws android.os.RemoteException
{
return null;
}
@Override public void addBook(com.ryg.chapter_2.aidl.Book book) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ryg.chapter_2.aidl.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.ryg.chapter_2.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ryg.chapter_2.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.ryg.chapter_2.aidl.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.ryg.chapter_2.aidl.IBookManager))) {
return ((com.ryg.chapter_2.aidl.IBookManager)iin);
}
return new com.ryg.chapter_2.aidl.IBookManager.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.ryg.chapter_2.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.ryg.chapter_2.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.ryg.chapter_2.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.ryg.chapter_2.aidl.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;
}
@Override public java.util.List<com.ryg.chapter_2.aidl.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.ryg.chapter_2.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.ryg.chapter_2.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.ryg.chapter_2.aidl.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);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.ryg.chapter_2.aidl.IBookManager sDefaultImpl;
}
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 static boolean setDefaultImpl(com.ryg.chapter_2.aidl.IBookManager impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.ryg.chapter_2.aidl.IBookManager getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.ryg.chapter_2.aidl.Book book) throws android.os.RemoteException;
}
为了方便分析,读者可以按alt + 7来显示改java类的类结构图,如图所示:
可以看到系统自动生成的这个类,本身是个接口,同时他还继承了IInterface这个接口,所有可以在Binder中传输的接口都需要继承IInterface。
首先一眼看过去,这个类的逻辑很混乱,其实不然。首先它声明了两个抽象方法getBookList和addBook,这两个方法刚好就是我们在IBookManager.aidl类中生命的两个方法,同时还声明了两个整型的id分别用于标识着两个方法,这两个id用于标识在transact过程中客户端所请求的到底是哪个方法
接着它声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,反之则走transact过程,这个逻辑由Stub的内部代理类Proxy来完成,这么看来,IBookManager这个接口的核心实现就是它的内部类Stub和Stub的内部代理类Proxy
3.2 类方法的含义
1.DESCRIPTOR
Binder的唯一标识,一般用当前Binder的类名表示,比如本例中的"com.ryg.chapter_2.aidl.IBookManager"
2.asInterface(android.os.IBinder obj)
用于将服务端的Binder对象转换成客户端所需的AIDL接口类型对象,这种转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象
3.asBinder()
此方法用于返回当前的Binder对象
4.onTransact
这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求,远程请求最终会交由此方法处理。
- 服务端通过code确定客户端请求的目标方法是什么
- 服务端从data中取出目标方法所需的参数(如果目标方法有参数)
- 目标方法执行完毕后,向reply中写入返回值(如果目标方法有返回值)
5.Proxy#getBookList()
此方法运行在客户端,内部实现:首先创建该方法所需要的输入型Parcel对象_data,输出型Parcel对象_reply和返回对象List;接着调用transact方法发起远程过程调用(RPC)请求,同时当前线程挂起;然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果_result,最后返回_result
6.addBook(com.ryg.chapter_2.aidl.Book book)
此方法运行在客户端上,执行过程和getBookList()是一样的,由于此方法没有返回值,所以无需从_reply中取出取出_result。
3.3 Binder的几点注意
1、客户端发起远程请求时,当前进程会被挂起直至服务端进程返回数据,所以如果远程方法是耗时的,那么就不能在UI线程发起此请求;
2、由于服务端的Binder方法是运行在Binder池中的,所以Binder方法应该采取同步的方法去实现,因为它已经运行在一个线程中了
我们思考这样一种场景,当Binder运行在服务端进程的时候进程由于某些原因异常终止,服务端的Binder会发生连接断裂(Binder死亡),导致远程调用失败,从而影响客户端功能。为了解决这个问题,Binder提供了两个非常重要的配对方法linkToDeath和unlinkToDeath,通过linkToDeath我们可以为Binder设置死亡代理,当Binder发生死亡的时候我们会收到通知,这个时候我们就可以通过一些恰当的方式重新发起连接请求从而恢复连接。我们要如何设置死亡代理呢?
首先,声明一个接口IBinder.DeathRecipient并实现binderDied()方法,当Binder死亡的时候系统会回调binderDied()方法,这个时候我们可以移除之前绑定的binder代理并重新绑定远程服务
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (bookManager == null) {
return;
}
bookManager.asBinder().unlinkToDeath(deathRecipient, 0);
bookManager = null;
//这里重新绑定远程Service
}
};
然后,客户端绑定远程服务成功后,给binder设置死亡代理
try {
bookManager = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
这样我们就成功了设置了死亡代理,另外通过Binder的方法isBinderAlive也可以判断binder是否死亡。
推荐阅读:IPC机制(一)---基础知识