现在的位置: 首页 > 综合 > 正文

自定向下分析Binder 之 Binder Model(1)

2018年02月15日 ⁄ 综合 ⁄ 共 15709字 ⁄ 字号 评论关闭

Java层的Binder对象模型:

IBinder

IBinder是Binder通信机制中的核心部分(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 Binder.  

The key IBinder API is transact() matched by 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 transact() does not return until the target has returned from 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.  

The data sent through transact() is a 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.  

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.  

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.  

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: 

  •  The transact() method will throw a RemoteException exception if you try to call it on an IBinder whose process no longer exists. 

  •  The pingBinder() method can be called, and will return false if the remote process no longer exists. 

  •  The linkToDeath() method can be used to register a IBinder.DeathRecipient with the IBinder, which will be called when its containing process goes away.  

总结一下IBnder接口:

1、IBnder是轻量级RPC通信机制中的核心部分,它定义了Binder RPC通信的抽象协议。

2、不能直接使用,在实现Binder机制时可继承Binder类。

3、RPC调用过程是同步的,即:调用一个远程方法后会堵塞等待RPC调用返回。这里保证了本地调用和远程调用的一致性。

4、Binder的客户端进程和服务进程之间通过Parcel传输数据。

5、每个实现了Binder机制的进程中会存在一个交互线程池。

6、Binder跨进程调用同样支持递归调用(像本地调用一样)。

Binder的每个跨进程调用都会对应一个唯一的表示,在通过adil生成Binder代码时,编号会自动在FIRST_CALL_TRANSACTION开始递加,但最大值不能超过LAST_CALL_TRANSACTION

下面是IBnder中的核心方法transact的接口定义:

@/frameworks/base/core/java/android/os/IBinder.java

    /**
     * Perform a generic operation with the object.
     * 
     * @param code The action to perform.  This should
     * be a number between {@link #FIRST_CALL_TRANSACTION} and
     * {@link #LAST_CALL_TRANSACTION}.
     * @param data Marshalled data to send to the target.  Must not be null.
     * If you are not sending any data, you must create an empty Parcel
     * that is given here.
     * @param reply Marshalled data to be received from the target.  May be
     * null if you are not interested in the return value.
     * @param flags Additional operation flags.  Either 0 for a normal
     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
     */
    public boolean transact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException;

注意这里的flags的取值,当取值为FLAG_ONEWAY时表示不需要返回值。


Binder

Base class for a remotable object, the core part of a lightweight remote procedure call mechanism defined by IBinder. This class is an implementation of IBinder that provides the standard support creating a local implementation of such an object.  

Most developers will not implement this class directly, instead using the aidl 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.

总结一下Binder类:

1、Binder机制中的核心类,在Java中实现Binder机制时通常继承自Binder,而不能直接继承IBinder。

2、在实现Binder机制时可以选择直接继承IBnder接口,也可以选择通过aidl来辅助实现。这二者本质上是一样的。

下面看一下Binder中的一些主要方法:

1)首先看一下Binder 的构造方法,代码如下:

@/frameworks/base/core/java/android/os/Binder.java

    /**
     * Default constructor initializes the object.
     */
    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());
            }
        }
    }

在Binder构造函数中做了两件事:

A、调用init方法,init函数是一个native方法,将Binder由Java层与C层连接起来,关于init在后面分析。

B、在设定了FIND_POTENTIAL_LEAKS的值为ture后,检查当前Binder对象是否会存在疑似内存泄露的地方,这些地方包括:匿名内部类、成员类(非静态)、本地静态类。

      之所以认为这些地方可能会存在内存泄露是因为,这些类作为内部类(Binder类)会默认包含一个队外部类的引用,从而导致外部类释放不掉。

2)下面是attachInterface方法:

    /**
     * Convenience method for associating a specific interface with the Binder.
     * After calling, queryLocalInterface() will be implemented for you
     * to return the given owner IInterface when the corresponding
     * descriptor is requested.
     */
    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

attachInterface方法用于将特定的IInterface与Binder联系起来。回到我们前面的例子:

说明一下attachInterface方法:

A、这里的mOwner成员变量保存IInterface对象;mDescriptor成员变量保存IInterface的描述(名字)。

B、DESCRIPTOR的取值理论来说是可以随意的,但一般取值为IInterface接口的完整类名。

C、attachInterface其实就是建立了一个mDescriptor为Key,mOwner(IInterface)为Value的键值对,以便queryLocalInterface可以通过descriptor获取IInterface。

    /**
     * Use information supplied to attachInterface() to return the
     * associated IInterface if it matches the requested
     * descriptor.
     */
    public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

3)接下来看一下transact方法,该方法是Binder进行IPC通信时传输数据的主要方法,代码如下:

    /**
     * Default implementation rewinds the parcels and calls onTransact.  On
     * the remote side, transact calls into the binder to do the IPC.
     */
    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

其中,data保存调用数据;reply保存返回数据。数据交互的具体实现逻辑在onTransact中实现,下面是Binder类中onTransact方法的默认实现:

    /**
     * Default implementation is a stub that returns false.  You will want
     * to override this to do the appropriate unmarshalling of transactions.
     *
     * <p>If you want to call this, call transact().
     */
    protected boolean onTransact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (code == INTERFACE_TRANSACTION) {
            reply.writeString(getInterfaceDescriptor());
            return true;
        } else if (code == DUMP_TRANSACTION) {
            ParcelFileDescriptor fd = data.readFileDescriptor();
            String[] args = data.readStringArray();
            if (fd != null) {
                try {
                    dump(fd.getFileDescriptor(), args);
                } finally {
                    try {
                        fd.close();
                    } catch (IOException e) {
                        // swallowed, not propagated back to the caller
                    }
                }
            }
            // Write the StrictMode header.
            if (reply != null) {
                reply.writeNoException();
            } else {
                StrictMode.clearGatheredViolations();
            }
            return true;
        }
        return false;
    }

在实际使用时,我们需要覆盖实现自己的onTransact方法,再次回到我们前面的例子,代码如下:

其中:INTERFACE_TRANSACTION、TRANSACTION_checkUidPermission这些是对某一调用的一个唯一标识,具体可参见IBinder类。

Binder客户端与服务端的数据是通过Parcel传输的,关于Parcel后面介绍。

IInterface

Base class for Binder interfaces.  When defining a new interface, you must derive it from IInterface.

总结一下IInterface接口:

1、所有自定义的Binder接口都必须继承实现IInterface。例如在前面的示例中,自定义接口ITestManager必须继承自IInterface,如下:

这里IInterface可以认为是Binder接口的一个标识,只有继承实现了IInterface,才会认为这个接口是一个Binder中的接口。

2、在IInterface中只有一个方法asBinder,在Stub和Proxy类中都有实现,目的是返回服务端Binder对象,代码如下:

@/frameworks/base/core/java/android/os/IInterface.java

/**
 * 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();
}

Parcel

Container for a message (data and object references) that can be sent through an IBinder.  A Parcel can contain both flattened data that will be unflattened on the other side of the IPC (using the various methods here for writing specific types, or the general Parcelable interface), and references to live IBinder objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel. 

Parcel is not a general-purpose serialization mechanism.  This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport.  As such, it is not appropriate to place any Parcel data in to persistent storage: changes in the underlying implementation of any of the data in the Parcel can render older data unreadable.

The bulk of the Parcel API revolves around reading and writing data of various types.  There are six major classes of such functions available.

Primitives

The most basic data functions are for writing and reading primitive data types: writeByte(byte)readByte()writeDouble(double)readDouble()writeFloat(float)readFloat()writeInt(int)readInt()writeLong(long)readLong()writeString(String)readString().  Most other data operations are built on top of these.  The given data is written and read using the endianess of the host CPU.

Primitive Arrays

There are a variety of methods for reading and writing raw arrays of primitive objects, which generally result in writing a 4-byte length followed by the primitive data items.  The methods for reading can either read the data into an existing array, or create and return a new array. These available types are:

Parcelables

The Parcelable protocol provides an extremely efficient (but low-level) protocol for objects to write and read themselves from Parcels. You can use the direct methods writeParcelable(Parcelable, int) and readParcelable(ClassLoader) or writeParcelableArray(T[], int) and readParcelableArray(ClassLoader) to write or read.  These methods write both the class type and its data to the Parcel, allowing that class to be reconstructed from the appropriate class loader when later reading.

There are also some methods that provide a more efficient way to work with Parcelables: writeTypedArray(T[], int)writeTypedList(List)readTypedArray(T[], Parcelable.Creator) and readTypedList(List, Parcelable.Creator).  These methods do not write the class information of the original object: instead, the caller of the read function must know what type to expect and pass in the appropriate Parcelable.Creator instead to properly construct the new object and read its data.  (To more efficient write and read a single Parceable object, you can directly call Parcelable.writeToParcel and Parcelable.Creator.createFromParcel yourself.)

Bundles

A special type-safe container, called Bundle, is available for key/value maps of heterogeneous values.  This has many optimizations for improved performance when reading and writing data, and its type-safe API avoids difficult to debug type errors when finally marshalling the data contents into a Parcel.  The methods to use are writeBundle(Bundle)readBundle(), and readBundle(ClassLoader).  

Active Objects

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.

Binder objects are a core facility of Android's general cross-process communication system.  The 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 writeStrongBinder(IBinder)writeStrongInterface(IInterface)readStrongBinder()writeBinderArray(IBinder[])readBinderArray(IBinder[])createBinderArray()writeBinderList(List)readBinderList(List)createBinderArrayList().

FileDescriptor objects, representing raw Linux file descriptor identifiers, can be written and 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 writeFileDescriptor(FileDescriptor)readFileDescriptor().  

Untyped Containers

A final class of methods are for writing and reading standard Java containers of arbitrary types.  These all revolve around the writeValue(Object) and readValue(ClassLoader) methods which define the types of objects allowed.  The container methods are writeArray(Object[])readArray(ClassLoader)writeList(List)readList(List, ClassLoader)readArrayList(ClassLoader)writeMap(Map)readMap(Map, ClassLoader)writeSparseArray(SparseArray)readSparseArray(ClassLoader).

说明一下:

1、flattened data(平化数据)的概念:在存储数据是,经常会将字符串、数组和路径保存在句柄(指向位于不同内存区域中指针的指针)中,因此含有这些字符串和数组的簇的存储是不连续的。将数据存入文件时,就需要在保存前将数据平化为一个字符串。数据的字符串化可使一个无序而复杂的簇中的数据不再处于不同地址而变得连续。这个过程就叫平化,平化后产生的字符串数据就是“平化数据”。

下面这一段是从网上找的的一段关于平化数据的解释:

LabVIEW将数据从其内存格式转换为一种更适于进行文件读写的格式。这种更适合读写的格式称为平化数据。

由于LabVIEW将字符串、数组和路径保存在句柄(指向位于不同内存区域中指针的指针)中,因此含有这些字符串和数组的簇的存储是不连续的。LabVIEW一般以树的形式保存数据。例如,LabVIEW将簇保存为双精度浮点数,将字符串保存为8字节的浮点数及4字节的句柄。在LabVIEW中,字符串数据与扩展精度浮点数的保存位置不相邻。因此,将簇数据写入磁盘时,LabVIEW必须从两个不同的地址获取数据。LabVIEW通过含有大量字符串、数组或路径的簇将数据保存在不同的地址。

将数据存入文件时,LabVIEW会在保存前将数据平化为一个字符串。数据的字符串化可使一个无序而复杂的簇中的数据不再处于不同地址而变得连续。从文件加载数据时,LabVIEW必须反向操作,即读取一个字符串并将其还原为非连续的内部形式。

引自:http://zone.ni.com/reference/zhs-XX/help/371361J-0118/lvconcepts/flattened_data/

2、所以,关于Parcel第一段的意思是:Parcel既可以传输平化的字符串数据,也可以传输Binder句柄。

3、Parcel不同于一般的序列化机制(Java中的Serializable),它被设计用于高效的IPC通信,因此,并不是所有的Parcel数据都可以持久化。

4、可以通过Parcel传输的数据包括六类:

  1. 基本数据类型。

  2. 基本数据类型数组。

  3. 继承实现Parcelable接口的类型。

  4. Bundle。

  5.  active objects(活动对象),这里主要包括Binder和file descriptor(文件描述符)

  6. 无类型的容器。

抱歉!评论已关闭.