为了账号安全,请及时绑定邮箱和手机立即绑定

Android之Binder底层原理

标签:
Android

什么是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.aidlpackage com.jd.test.myapplication;import com.jd.test.myapplication.Book;// Declare any non-default types here with import statementsinterface 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目录下。

5ba0b0db0001017f12400286.jpg

/*
 * 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 statementspublic 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是怎么样来进行间通讯的呢?下面我们先通过一个示例图来简单描述一下该流程。

5ba0b0dc00017b7309750334.jpg

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通讯的结构

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

总结

  1. 本文对Bidner做了一些整体的介绍,主要是基于应用层的流程进行分析,如果要彻底搞清楚Bidner,可能还需阅读Binder驱动的源码及Bidner的协议等

  2. Binder在Android体系中,有着非常重要的地位,是核心的IPC方式。如果希望学习Android源码,Binder是一道需要越过去的坎。

原文链接:http://www.apkbus.com/blog-866962-63623.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消