美文网首页
IPC机制(二)——Binder

IPC机制(二)——Binder

作者: 李die喋 | 来源:发表于2019-10-08 22:59 被阅读0次

Binder是什么

Binder是Android中的一个类,它实现了IBinder接口。


image

应用层的角度在Service和其他组件绑定通信的时候就接触过,下面会从IPC角度和底层角度来分析。

Binder工作机制

SDK会自动生成Binder类

Book类

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public 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 out, int flags) {
        out.writeInt(bookId);
        out.writeString(bookName);
    }
}
  • getBookList:用于从远程服务端获取图书列表
  • addBook:用于往图书列表中添加新的图书

建立AIDL

  • Book.aidl
// Book.aidl
package com.lxy.bindertest;

parcelable Book;
  • IBookManager.aidl
// IBookManager.aidl
package com.lxy.bindertest;

import com.lxy.bindertest.Book;
interface IBookManager{
    List<Book> getBookList();
    void addBook(in Book book);
}

在建立aidl的时候,可能找不到对应系统生成的.java文件,rebuild一下就好了。

分析IBookManager

先看下整体结构:

public interface IBookManager extends android.os.IInterface{
    public static abstract class Stub extends Binder implements IBookManager{
        private static final DESCRIPTOR = "com.lxy.bindertest.IBookManager";
        
        public Stub(){
            this.attachInterface(this,DESCRIPTOR);
        }
        
        public static IBookManager asInterface(IBinder obj){
            、、、
        }
        
        public IBinder asBinder(){
            return this;
        }
        
        public boolean onTransact(int code,Parcel data,Parcel reply,int flags) throws RemoteException{
            、、、
        }
        
        private static class Proxy implements IBookManager{
            private IBinder mRemote;
            
            Proxy(IBinder remote){
                mRemote = remote;
            }
            
            public IBinder asBinder{
                return mRemote;
            }
            
            public String gerInterfaceDescriptor{
                return DESCRIPTOR;
            }
            
            public List<Book> getBookList() throws RemoteException{
                、、、
            }
            
            public void addBook(Book book) throws RemoteException{
                、、、
            }
        }
        
        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 List<Book> getBookList() throws RemoteException;
    public void addBook(Book book) throws RemoteException;
}
image

这是把每个方法提出来的结构框图。。。

这里面最主要的就是Stub类和它的内部类Proxy

IBookManager

  • 继承了IInterface接口,所有可以在Binder中传输的接口都需要继承IInterface接口。
  • 声明了两个抽象方法getBookList和addBook
  • 定义了静态内部类Stub

Stub

  • Stub继承Binder、实现IBookManager,是个静态抽象类。

Stub就是一个Binder类

DESCRIPTOR

Binder的唯一标识。一般用当前的类名表示。

asInterface(androd.os.Binder obj)

将服务端的Binder对象转换成客户端所需的AIDL接口类型对象。但这种转换是区分进程的,当客户端和服务器端在同一进程中,此方法返回服务端的Stub对象本身,否则返回系统封装后的Stub.Proxy对象。

public static com.lxy.bindertest.IBookManager asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.lxy.bindertest.IBookManager))) {
        return ((com.lxy.bindertest.IBookManager) iin);
    }
    return new com.lxy.bindertest.IBookManager.Stub.Proxy(obj);
}

asBinder

返回当前Binder对象。

  • Stub asBinder():返回当前对象
public android.os.IBinder asBinder() {
    return this;
}
  • Proxy asBinder():返回构造器传入的Binder对象
public android.os.IBinder asBinder() {
    return mRemote;
}

onTransact

这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。具体流程:

  1. 服务端通过code确定客户端所请求的目标方法是什么
  2. data装着目标方法的参数,取出参数
  3. 传入参数,调用执行code对应的方法
  4. 目标方法执行完后,向reply中写入返回值
  5. 如果返回false,客户端请求失败
@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.lxy.bindertest.Book> _result = this.getBookList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
        case TRANSACTION_addBook: {
            data.enforceInterface(descriptor);
            com.lxy.bindertest.Book _arg0;
            if ((0 != data.readInt())) {
                _arg0 = com.lxy.bindertest.Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            this.addBook(_arg0);
            reply.writeNoException();
            return true;
        }
        default: {
            return super.onTransact(code, data, reply, flags);
        }
    }
}

Proxy#getBookList

这个方法运行在客户端,当客户端远程调用此方法时,具体流程如下:

  1. 创建该方法所需的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象_result
  2. 把方法参数信息写入_data中
  3. 调用transact方法发起RPC(远程过程调用)请求,同时将线程挂起
  4. 服务端onTracsact()被调用
  5. RPC返回后,当前线程继续执行,从_reply中取出RPC过程的返回结果
  6. 返回_reply中的数据
public java.util.List<com.lxy.bindertest.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.lxy.bindertest.Book> _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
        _reply.readException();
        _result = _reply.createTypedArrayList(com.lxy.bindertest.Book.CREATOR);
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

Proxy#addBook

这个方法运行在客户端,执行过程和getBookList一样,addBook没有返回值,所以不需要从_reply中取出返回值。

注意与总结

  • 如果远程方法耗时,则不能在UI线程中发起RPC。因为当客户端发起RPC时,当前线程会被挂起直到服务端返回数据。
  • Binder方法不管是否耗时,都应该采用同步方式实现。因为Binder本来就运行在线程池中。
  • Binder工作机制图:


    image

参考文章

Binder原理剖析

相关文章

网友评论

      本文标题:IPC机制(二)——Binder

      本文链接:https://www.haomeiwen.com/subject/sgoipctx.html