详解Binder类

跳过AIDL

AIDL的使用中我们详细的介绍了AIDL的简单使用,从使用来看,我们定义一个.aidl文件,然后编译器通过这个文件帮助我们生成了一个类,其实这个类我们完全可以自己去Code,要自己Code我们就需要详细了解它

.aidl文件生成的类

仍然使用AIDL的使用中的例子来分析,我们先看看编译器帮我我们生成的文件,客户端和服务端生成的类是一模一样的,我们只需要看其中一个就行:

// IConnect.java
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/jucongyuan/Documents/Android/Apps/AIDLClient/app/src/main/aidl/com/jucongyuan/IConnect.aidl
 */
package com.jucongyuan;

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

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

        /**
         * Cast an IBinder object into an com.jucongyuan.IConnect interface,
         * generating a proxy if needed.
         */
        public static com.jucongyuan.IConnect asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.jucongyuan.IConnect))) {
                return ((com.jucongyuan.IConnect) iin);
            }
            return new com.jucongyuan.IConnect.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_getStr: {
                    data.enforceInterface(DESCRIPTOR);
                    com.jucongyuan.Student _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.jucongyuan.Student.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    com.jucongyuan.Student _result = this.getStr(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.jucongyuan.IConnect {
            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 com.jucongyuan.Student getStr(com.jucongyuan.Student s) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.jucongyuan.Student _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((s != null)) {
                        _data.writeInt(1);
                        s.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_getStr, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.jucongyuan.Student.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getStr = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public com.jucongyuan.Student getStr(com.jucongyuan.Student s) throws android.os.RemoteException;
}

看上去挺复杂的,而我们的任务就是要学会自己写这个类,而不借助.aidl,要自己写我们就需要明白这个类里面到底做了啥,它的结构如下:

接下来开始分析IConnect.java了,IConnect.java是一个接口,定义了一个接口方法getStr(),而这个就是我们IConnect.aidl定义的,IConnect还继承至android.os.IInterface,它就一个方法:

package android.os;

public interface IInterface
{
    public IBinder asBinder();
}

IConnect.java中有一个静态抽象内部类Stub,他继承自android.os.Binder,并且实现了IConnectandroid.os.Binder实现了IBinder接口,因此它是一个IBinder(这点需要明确,后面会有重要用处),换句话说,Stub既是一个IBinder,也是一个IInterfaceStub有两个静态常量DESCRIPTORTRANSACTION_getStr,另外有一个构造方法和三个一般方法,其中的asBinderonTransact方法分别是IInterfaceBinder中方法的实现,而asInterface方法主要是用来返回一个IConnect实例,另外Stub还有一个内部类Proxy,他也实现了IConnect,因此他也是一个IInterface,而我们可以认为它也是一个IBinder,因为它是Stub的内部类,因此会持有Stub对象。在Proxy中实现了getStr()方法。IConnect.java这个结构分析完了,接下来我们就来看看IConnect.java这个类怎么被使用的,它的使用在客户端中,先看ClientActivity.java获取远程服务:

// ClientActivity.java
package com.jucongyuan.aidlclient.activity;
// 省略一堆import
public class ClientActivity extends Activity {
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                connect = IConnect.Stub.asInterface(service);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ……
        Intent intent = new Intent();
        intent.setAction(IConnect.class.getName());
        intent.setClassName("com.jucongyuan.aidlserver", "com.jucongyuan.aidlserver.service.ServerService");
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
        // ……
    }
}

bindService方法开始,这个方法是在干什么呢,它就是在给ServiceManager打电话(Android Binder分析一文中已经对Binder的流程做了分析,查阅后可以明白打电话具体含义),它要联系的是com.jucongyuan.aidlserver.service.ServerService这个Service,联系上以后,通过ServiceConnectiononServiceConnected回调方法返回一个IBinder类对象,IBinder是一个接口,然后通过StubasInterface()方法,返回了一个IConnect对象:

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

这个方法中,重要的就是obj.queryLocalInterface方法,它判断了我们的obj(它是一个IBinder)是远程的还是和当前进程属于同一个进程,如果是同一个进程,则直接返回一个IConnect,否则,返回一个代理Proxy,这个代理里面的getStr()实现就是通过这个远程IBinder来调用相应方法:

@Override
public com.jucongyuan.Student getStr(com.jucongyuan.Student s) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    com.jucongyuan.Student _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        if ((s != null)) {
            _data.writeInt(1);
            s.writeToParcel(_data, 0);
        } else {
            _data.writeInt(0);
        }
        mRemote.transact(Stub.TRANSACTION_getStr, _data, _reply, 0);
        _reply.readException();
        if ((0 != _reply.readInt())) {
            _result = com.jucongyuan.Student.CREATOR.createFromParcel(_reply);
        } else {
            _result = null;
        }
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

这里面就是一些数据序列化和反序列的操作,其实就是在和远程服务来进行数据通信,因此我们要通过Binder通信的数据,必须要实现持久化接口

IConnect.java总结

到此,IConnect.java的功能已大致清晰

  1. Stub有一个asInterface()方法,它可以查询我们的服务是本进程还是其他进程的
  2. 如果是本地的,则直接返回本地接口对象
  3. 如果不是本地的,则通过Binder来和其他进程通信

Binder是怎么进程不同进程间通信的呢?注意mRemote引用,它的类型其实是一个BinderProxy,它是Binder的内部类:

final class BinderProxy implements IBinder {
    public native boolean pingBinder();
    public native boolean isBinderAlive();

    public IInterface queryLocalInterface(String descriptor) {
        return null;
    }

    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
        return transactNative(code, data, reply, flags);
    }

    public native String getInterfaceDescriptor() throws RemoteException;
    public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
    public native void linkToDeath(DeathRecipient recipient, int flags)
            throws RemoteException;
    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);

    public void dump(FileDescriptor fd, String[] args) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeFileDescriptor(fd);
        data.writeStringArray(args);
        try {
            transact(DUMP_TRANSACTION, data, reply, 0);
            reply.readException();
        } finally {
            data.recycle();
            reply.recycle();
        }
    }

    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeFileDescriptor(fd);
        data.writeStringArray(args);
        try {
            transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
        } finally {
            data.recycle();
            reply.recycle();
        }
    }

    public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
            String[] args, ResultReceiver resultReceiver) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeFileDescriptor(in);
        data.writeFileDescriptor(out);
        data.writeFileDescriptor(err);
        data.writeStringArray(args);
        resultReceiver.writeToParcel(data, 0);
        try {
            transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
            reply.readException();
        } finally {
            data.recycle();
            reply.recycle();
        }
    }

    BinderProxy() {
        mSelf = new WeakReference(this);
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            destroy();
        } finally {
            super.finalize();
        }
    }

    private native final void destroy();

    private static final void sendDeathNotice(DeathRecipient recipient) {
        if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
        try {
            recipient.binderDied();
        }
        catch (RuntimeException exc) {
            Log.w("BinderNative", "Uncaught exception from death notification",
                    exc);
        }
    }

    final private WeakReference mSelf;
    private long mObject;
    private long mOrgue;
}

注意transact()方法调用了transactNative()方法,transactNative()是一个本地方法,这个方法就是Binder驱动在做一些具体的事了,我们知道Binder驱动是我们实现进程间通信的基本,它处于内核中,Clien和Server就是通过它来进行通信的,在Android中,基于Binder的进程间通信有Messenger、ContentProvider,还有我们每个应用进程和ActivityManagerServiced通信等等,它们只是在Binder的基础上再进行进一步封装,实现某一个更具体的进程间通信功能

坚持原创分享,您的支持将鼓励我不断前行!