Android Binder学习笔记

Android的Binder是一种跨进程的通信机制,和TCP机制很像,其实也可以抽象的进行分层,可以把Binder想象成一种跨进程通信的协议。

学习Binder机制最好是以aidl为例,因为sdk会帮助我们将aidl文件生成利用Binder机制通信的代码,所以本篇以aidl为例来记录一下Binder机制的学习过程。

在学习之前,和Binder机制相关的几个类需要介绍一下,主要有Binder, IBinder, IInterface,Binder是IBinder的实现类,IInterface是与Binder相对应的,下面通过源码的注释,简单的看一下,其实源码的注释都很详细,通过看注释,可以知道类的大概功能。

首先看下Binder,public class Binder implements IBinder,Binder继承IBinder接口,Binder类的注释如下:

/**
 * Base class for a remotable object, the core part of a lightweight
 * remote procedure call mechanism defined by {@link IBinder}.
 * This class is an implementation of IBinder that provides
 * standard local implementation of such an object.
 *
 * <p>Most developers will not implement this class directly, instead using the
 * <a href="{@docRoot}guide/components/aidl.html">aidl</a> tool to describe the desired
 * interface, having it generate the appropriate Binder subclass.  You can,
 * however, derive directly from Binder to implement your own custom RPC
 * protocol or simply instantiate a raw Binder object directly to use as a
 * token that can be shared across processes.
 *
 * <p>This class is just a basic IPC primitive; it has no impact on an application's
 * lifecycle, and is valid only as long as the process that created it continues to run.
 * To use this correctly, you must be doing so within the context of a top-level
 * application component (a {@link android.app.Service}, {@link android.app.Activity},
 * or {@link android.content.ContentProvider}) that lets the system know your process
 * should remain running.</p>
 *
 * <p>You must keep in mind the situations in which your process
 * could go away, and thus require that you later re-create a new Binder and re-attach
 * it when the process starts again.  For example, if you are using this within an
 * {@link android.app.Activity}, your activity's process may be killed any time the
 * activity is not started; if the activity is later re-created you will need to
 * create a new Binder and hand it back to the correct place again; you need to be
 * aware that your process may be started for another reason (for example to receive
 * a broadcast) that will not involve re-creating the activity and thus run its code
 * to create a new Binder.</p>

可以看到Binder主要用于跨进程的通信,是IBinder接口的本地实现。
那么在看下IBinder接口:public interface IBinder

/**
 * Base interface for a remotable object, the core part of a lightweight
 * remote procedure call mechanism designed for high performance when
 * performing in-process and cross-process calls.  This
 * interface describes the abstract protocol for interacting with a
 * remotable object.  Do not implement this interface directly, instead
 * extend from {@link Binder}.
 * 
 * <p>The key IBinder API is {@link #transact transact()} matched by
 * {@link Binder#onTransact Binder.onTransact()}.  These
 * methods allow you to send a call to an IBinder object and receive a
 * call coming in to a Binder object, respectively.  This transaction API
 * is synchronous, such that a call to {@link #transact transact()} does not
 * return until the target has returned from
 * {@link Binder#onTransact Binder.onTransact()}; this is the
 * expected behavior when calling an object that exists in the local
 * process, and the underlying inter-process communication (IPC) mechanism
 * ensures that these same semantics apply when going across processes.
 * 
 * <p>The data sent through transact() is a {@link Parcel}, a generic buffer
 * of data that also maintains some meta-data about its contents.  The meta
 * data is used to manage IBinder object references in the buffer, so that those
 * references can be maintained as the buffer moves across processes.  This
 * mechanism ensures that when an IBinder is written into a Parcel and sent to
 * another process, if that other process sends a reference to that same IBinder
 * back to the original process, then the original process will receive the
 * same IBinder object back.  These semantics allow IBinder/Binder objects to
 * be used as a unique identity (to serve as a token or for other purposes)
 * that can be managed across processes.
 * 
 * <p>The system maintains a pool of transaction threads in each process that
 * it runs in.  These threads are used to dispatch all
 * IPCs coming in from other processes.  For example, when an IPC is made from
 * process A to process B, the calling thread in A blocks in transact() as
 * it sends the transaction to process B.  The next available pool thread in
 * B receives the incoming transaction, calls Binder.onTransact() on the target
 * object, and replies with the result Parcel.  Upon receiving its result, the
 * thread in process A returns to allow its execution to continue.  In effect,
 * other processes appear to use as additional threads that you did not create
 * executing in your own process.
 * 
 * <p>The Binder system also supports recursion across processes.  For example
 * if process A performs a transaction to process B, and process B while
 * handling that transaction calls transact() on an IBinder that is implemented
 * in A, then the thread in A that is currently waiting for the original
 * transaction to finish will take care of calling Binder.onTransact() on the
 * object being called by B.  This ensures that the recursion semantics when
 * calling remote binder object are the same as when calling local objects.
 * 
 * <p>When working with remote objects, you often want to find out when they
 * are no longer valid.  There are three ways this can be determined:
 * <ul>
 * <li> The {@link #transact transact()} method will throw a
 * {@link RemoteException} exception if you try to call it on an IBinder
 * whose process no longer exists.
 * <li> The {@link #pingBinder()} method can be called, and will return false
 * if the remote process no longer exists.
 * <li> The {@link #linkToDeath linkToDeath()} method can be used to register
 * a {@link DeathRecipient} with the IBinder, which will be called when its
 * containing process goes away.
 * </ul>
 * 
 * @see Binder
 */

IBinder中的注释包含了很多有用的信息:
1.IBinder 接口描述了呵远程对象交互的一个抽象协议
2.IBinder接口中有个很重要的接口transact接口,这个api是和Binder类中的onTransact方法相对应的。同时transact接口是同步的,这就解释了,当用aidl调用远程接口时,如果远程接口做的是耗时操作,那么需要写在子线程的原因了。
3.注释中的第三段说的意思是,transact接口中跨进程传递的数据是Parcel类型的,Parcel类型感觉上是主要为Binder通信设计的,Parcel是一个容器,这个容器不仅能包含内容,还能包含对象的引用。下面看Parcel中的一段注释
<h3>Active Objects</h3>
* <p>An unusual feature of Parcel is the ability to read and write active
* objects. For these objects the actual contents of the object is not
* written, rather a special token referencing the object is written. When
* reading the object back from the Parcel, you do not get a new instance of
* the object, but rather a handle that operates on the exact same object that
* was originally written. There are two forms of active objects available.</p>
*
* <p>{@link Binder} objects are a core facility of Android's general cross-process
* communication system. The {@link IBinder} interface describes an abstract
* protocol with a Binder object. Any such interface can be written in to
* a Parcel, and upon reading you will receive either the original object
* implementing that interface or a special proxy implementation
* that communicates calls back to the original object. The methods to use are
* {@link #writeStrongBinder(IBinder)},
* {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()},
* {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])},
* {@link #createBinderArray()},
* {@link #writeBinderList(List)}, {@link #readBinderList(List)},
* {@link #createBinderArrayList()}.</p>
*
* <p>FileDescriptor objects, representing raw Linux file descriptor identifiers,
* can be written and {@link ParcelFileDescriptor} objects returned to operate
* on the original file descriptor. The returned file descriptor is a dup
* of the original file descriptor: the object and fd is different, but
* operating on the same underlying file stream, with the same position, etc.
* The methods to use are {@link #writeFileDescriptor(FileDescriptor)},
* {@link #readFileDescriptor()}.
*

这段意思简单来说是:Parcel可以读写“活的”对象,但是Parcel中写的不是对象的内容,而是写的一个token,这个token可以标记那个对象,这样通过token就可以找到那个“活的”对象。Parcel支持两种这样的对象,一个就是咱们正在学习的Binder,一个是FileDescriptor。以Binder为例,当我们用Parcel传输Binder的时候,当读Binder的时候,我们就会判断,本地是否有这个或者的对象,如果有,说明时本地调用,则直接使用活着的对象,如果不是则实现个代理类,这个代理类来和远程的原始对象通信,这个也是Binder的机制,后面讲aidl时,再通过代码详细说下。
知道了Parcel类的用处,就基本上明白了IBinder注释中的第三段了。

4.第四段说的是,远程接口的方法是运行在Binder的线程池的。Binder有个线程池,当跨进程请求过来的时候,线程池里面的线程会启动,之后调用Binder的onTransact函数,之后onTransact调用具体的接口
5.IBinder支持跨进程递归。举例,Process A 调用Process B,这是Process B在onTransact方法中通过Binder调用Process A的接口,这时,Process A的Binder线程池回去执行相应的接口,这个Process A在本地调用是同样的效果。
6.可以通过 transact pingBinder linkToDeath等方法来判断远程的Binder是否还活着。

再说下IInterface接口,源码如下:

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}

可以看到,IInterface接口时和Binder相对应的,如果我们想通过Binder进行跨进程的调用,那么我们的接口就应该继承IInterface接口,后面说aidl的时候也会看到。

通过上面的介绍大体了解了Binder中几个基本的类,下面通过aidl来看下Binder的运行过程。

我们知道aidl是一种接口定义语言,用于android的跨进程通信,当我们定义好aidl文件之后,android的sdk会帮我们自动生成相应的Java文件,真正实现跨进程通信功能的是生成的java文件,aidl只是一个产生java文件的工作,相当于android帮我们简化了工作,所以我们主要来分析生成的java文件。

首先我们先定义一个aidl文件,这个文件很简单,定义两个接口:

package com.example.aidl;

interface ICalculate{
 int add(int a, int b);
 int del(int a, int b);
}

这时,我们在gen目录下一个对应的Java文件,如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/zxw/android_develop/eclipse_workspace/AIDLLearning/src/com/example/aidl/ICalculate.aidl
 */
package com.example.aidl;

public interface ICalculate extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.example.aidl.ICalculate {
        private static final java.lang.String DESCRIPTOR = "com.example.aidl.ICalculate";

        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.aidl.ICalculate interface,
         * generating a proxy if needed.
         */
        public static com.example.aidl.ICalculate asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.aidl.ICalculate))) {
                return ((com.example.aidl.ICalculate) iin);
            }
            return new com.example.aidl.ICalculate.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_add: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.add(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            case TRANSACTION_del: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.del(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.aidl.ICalculate {
            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 int add(int a, int b) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public int del(int a, int b) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_del, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_del = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public int add(int a, int b) throws android.os.RemoteException;

    public int del(int a, int b) throws android.os.RemoteException;
}

可以看到ICalculate接口继承了IInterface接口,根据之前的介绍可知,因为IInterface是和Binder相对应的接口,所以ICalculate就是和Binder相对应的接口。

在ICalculate接口中有个内部类Stub,这个内部类非常的重要,当客户端用bindService去bind服务的时候,server端会返回Stub实现类的实例,客户端得到binder之后,会调用asInterface静态方法,来把binder的对应IInterface接口拿到,可以看到在asInterface方法中,首先会调用Binder的queryLocalInterface方法,此方法的作用是,找到该Binder对应的接口实例,之后,判断此实例是否为本地(client端)的实例,如果是,那么就是本地调用,并没有跨进程,直接返回对应接口,如果得到的IInterface实例不是本地的对象实例,那么说明是跨进程调用的,这时会用Proxy类来进行一个包装。Proxy类继承ICalculate接口,new出对象后,将Proxy对象返回。

在跨进程调用的这种情况下,client拿到Proxy实例后,调用接口中的方法,比如add方法,在Proxy的add方法中会调用

                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

之后,而transact方法内会调用onTransact方法,在onTransact的方法内,调用了与Binder对应的接口的接口方法,这样就完成了Binder的通信。

通过上面的分析可以知道,即便我们不建立aidl文件,也是可以实现Binder通信的,只需要仿照aidl生成的java文件,调用Binder的transact方法即可。

在之前提到,客户端通过bindService,利用Binder机制来进行跨进程通信,那么bindService是怎么执行的呢,下一篇文章趁热打铁,接着分析一下bindServce的机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值