阅读 282

Android之Binder底层原理

什么是Binder

Binder是Android中特有的一种跨进程通讯的方式。但我们在平时的开发过程中,可能很少用的。而Binder的整个体系结构又尤为复杂,一般很难通过网上的一两篇博客,就能把Binder吃透,我们需要通过源码及Binder的一些架构原理,来进行研究。后面的章节我们将主要通过3个部分来由浅至深来了解Binder。首先我们先看在实际的开发中怎么来实现Binder通讯,接着分析Binder框架的原理,最后结合源码进行分析。

为什么感觉Binder很陌生?

  1. 项目业务简单,不涉及多进程通讯
  2. 涉及多进程通讯,只简单用AIDL,没深入了解

为什么要学习Binder?

Binder作为Android核心的跨进程通讯方式。如果我们要研究Android的源码,Binder是一道需要跨过去的坎。我们都知道系统的各种服务运行在SystemServer进程中,我们应用与系统的各种交互都涉及到跨进程通讯。

例如最简单的启动一个Activity、启动一个Service。到例如使用系统的网络、硬件、等各种Service,其实都涉及到跨进程通讯。只是系统为我们做好了各种封装调用而已。

所以如果你只希望一直停留在应用层的业务开发,其实你可能一直永远都不知道Binder,但是一旦你开始了解Android的源码,那么你总会与Binder相遇。

Android为什么使用Binder作为主要进程间通讯机制?

  1. 安全性:Binder机制从协议本身就支持对通信双方做身份校检,安全性高。传统的进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的,都可以进行伪造
  2. 性能:socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信,Binder其实通过Binder驱动在内核区域进行了数据的传输,性能高

如何实现一个Binder通讯?

  1. 在项目中新建一个aidl

// IBookManager.aidl

package com.jd.test.myapplication;
import com.jd.test.myapplication.Book;
// Declare any non-default types here with import statements

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);

    List<Book> getBooks();

    void addBook(in Book book);
}

复制代码
  1. 创建一个在独立进程的Service
<service android:name=".BookManagerService"
            android:process=":remote"/>
            
复制代码

public class BookManagerService extends Service {
    private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1,"Android"));
        mBookList.add(new Book(2,"IOS"));
    }

    private Binder mBinder=new IBookManager.Stub(){
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public List<Book> getBooks() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

复制代码
  1. 另外一个进程,启用远程的Service,并调用接口方法,进行通讯
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent=new Intent(this,BookManagerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);

    }
 private ServiceConnection mConnection=new ServiceConnection() {
     @Override
     public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        IBookManager manager=IBookManager.Stub.asInterface(iBinder);
         try {
             List<Book> books=manager.getBooks();
             System.out.println("books:"+books);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
     }

     @Override
     public void onServiceDisconnected(ComponentName componentName) {

     }
 };
}
复制代码

只能说so easy。Android提供了优秀的API,使得我们可以很方便的通过AIDL实现进程间的通讯。貌似我们实现了进程间通讯,但是连Binder的身影都没看到,这也就是上面的Binder对很多童鞋都很陌生的原因。

Binder的原理

我们定义了AIDI后,默认系统都会生成一个集成了IInterface的接口的类,eclipse默认是在gen目录下。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: G:\\Source\\Demo\\MyApplication\\app\\src\\main\\aidl\\com\\jd\\test\\myapplication\\IBookManager.aidl
 */
package com.jd.test.myapplication;
// Declare any non-default types here with import statements

public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jd.test.myapplication.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.jd.test.myapplication.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.jd.test.myapplication.IBookManager interface,
 * generating a proxy if needed.
 */
public static com.jd.test.myapplication.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.jd.test.myapplication.IBookManager))) {
return ((com.jd.test.myapplication.IBookManager)iin);
}
return new com.jd.test.myapplication.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
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getBooks:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.jd.test.myapplication.Book> _result = this.getBooks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.jd.test.myapplication.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.jd.test.myapplication.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.jd.test.myapplication.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;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.util.List<com.jd.test.myapplication.Book> getBooks() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.jd.test.myapplication.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.jd.test.myapplication.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.jd.test.myapplication.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_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public java.util.List<com.jd.test.myapplication.Book> getBooks() throws android.os.RemoteException;
public void addBook(com.jd.test.myapplication.Book book) throws android.os.RemoteException;
}

复制代码

这个类也就是我们在onServiceConnected中使用的接口。在这个IBookManager中我们有几个关键的类Stub、Proxy,也见到了久违的Binder。那么纠结Binder是怎么样来进行间通讯的呢?下面我们先通过一个示例图来简单描述一下该流程。

IInterface结构分析

首先变量DESCRIPTOR定义了接口和对应方法的唯一标示。因为Binder其实是一种底层的通讯方式,Google工程师将Binder包装成了一种对象的引用。所以这里的标识是告诉底层的Binder驱动,我的Binder引用标识,对应的方法标识。这样Binder驱动才能在进程间进行装换。

Proxy:实现了IBookManager接口,这个代理类是往Binder驱动里面写数据,通过调用Binder的transact方法往Binder驱动写Parcel数据。可以理解为告诉Binder驱动我们要调用什么方法

mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
复制代码

Stub:继承了Binder,实现了IBookManager接口。Binder驱动调用远程方法成功后,要回调告诉执行的结果。通过回调onTransact方法。将Binder驱动中的Parcel数据转换为我们的回调数据。

IBinder引用是什么时候注册到了Binder驱动中呢?

我们知道Stub继承了Binder,那么当Stub实例化的时候,这个时候Binder无参构造被调用,执行了一个native 的init方法。这个时候向Binder驱动注册了IBinder的引用

public Binder() {
        init();

        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Binder> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
    }

private native final void init();

复制代码

transact方法(往Binder驱动写数据)最后调用为BinderProxy代理类的transact

   public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        return transactNative(code, data, reply, flags);
    }
    
      public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
复制代码

onTransact方法 (Binder驱动回调数据)

根据定义的常量标识,解析Parcel数据,回调本地方法

 @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_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getBooks: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.jd.test.myapplication.Book> _result = this.getBooks();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.jd.test.myapplication.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.jd.test.myapplication.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
复制代码

Binder应用层源码实现流程分析

我们了解了Binder通讯的一些基础原理后,通过应用层的调用来追踪整个执行的流程。下面我们通过一个表格索引来描述Binder通讯的结构

索引 调用的类间关系 说明
1 Activity:bindService 执行服务的绑定
2 Context:bindService 基类的的服务绑定抽象方法
3 ContextImpl:bindService Context实现类的方法
4 ContextImpl:bindServiceCommon 进行一些Intent校验等,调用ActivityManagerNative.getDefault().bindService
5 ActivityManagerService:bindService 进行一些合法 非空的校验
6 ActiveServices:bindServiceLocked 创建Service,对服务进行缓存记录,同时回调了connection方法
7 ActiveServices:requestServiceBindingLocked 校验服务进程是否存在,调用ApplicationThread的scheduleBindService
8 ApplicationThread:scheduleBindService ApplicationThread是ActivityThread的内部类,该方法发送了一个Message sendMessage(H.BIND_SERVICE, s);
9 ActiviThread:handleBindService 校验Service是否存在,执行AMS的publishService方法
10 ActivityManagerService:publishService 校验及调用ActiveServices的publishServiceLocked方法
11 ActiveServices:publishServiceLocked 执行Binder的restoreCallingIdentity方法

总结

  1. 本文对Bidner做了一些整体的介绍,主要是基于应用层的流程进行分析,如果要彻底搞清楚Bidner,可能还需阅读Binder驱动的源码及Bidner的协议等
  2. Binder在Android体系中,有着非常重要的地位,是核心的IPC方式。如果希望学习Android源码,Binder是一道需要越过去的坎。

关于

欢迎关注我的个人公众号

微信搜索:一码一浮生,或者搜索公众号ID:life2code

image