android之binder学习攻克

Android插件化基础--Binder机制

2018-05-11  本文已影响0人  风咏而归

Binder是一套基于CS的架构。下面以一个极简的例子来学习Binder

1.首先定义一个IMedia.aidl文件。
interface IMedia {
    boolean start();
    boolean stop();
}
2.然后IDE会帮我们自动生成一个IMedia.java文件
public interface IMedia extends android.os.IInterface {

        public static abstract class Stub extends android.os.Binder implements com.cmeiyuan.pluginstudy.IMedia {
                
                public android.os.IBinder asBinder(){
                    return this;
                }
                
                public static com.cmeiyuan.pluginstudy.IMedia asInterface(android.os.IBinder obj) {
                    ...
                }
                
                public boolean onTransact(...){
                    ...
                    start();
                    ...
                }
                
                private static class Proxy implements com.cmeiyuan.pluginstudy.IMedia {
                    ...
                }
        }
    }
    public boolean start() throws android.os.RemoteException;
}

IMedia是一个继承于android.os.IInterface的接口,它的内部类Stub实现了android.os.IInterfaceasBinder()方法,直接返回了Sub类实例。我们继续看Sub类的asInterface()方法

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

这个方法的作用是将远程Binder对象转换为方便使用的IMedia接口对象,这个远程Binder对象是我们bindService()时返回的。首选通过obj.queryLocalInterface(DESCRIPTOR)查询是否有本地接口对象,这种情况是当Server端和Client端处于同一进程,没有必要进行多进程通信。如果没有本地接口对象,那么直接new一个Stub.Proxy(obj)实例返回。我们来看一下这个Proxy类的具体实现

private static class Proxy implements com.cmeiyuan.pluginstudy.IMedia {
    private android.os.IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }
    
    @Override
    public boolean start() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        boolean _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);
            _reply.readException();
            _result = (0 != _reply.readInt());
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

这个Proxy类是Stub类的内部类,其实就是对obj的一层封装(代理),那么我们通过这个代理类,就很容易和Server端进行通信了。具体实现也很简单,就是调用远程Binder对象mRemotetransact()方法,将数据发送给Server端。

Stub是一个抽象类,继承于Binder类,实现了父类的onTransact()方法,onTransact()方法的作用是执行Server端的操作,并将操作的结果返回给Client端。一般情况,我们需要写一个子类继承于Stub类,然后实现IMedia的接口方法,这些方法会被父类onTransact()方法调用到。

3.总结一下多进程通信过程:

(1)通过bindService()得到远程Binder对象obj
(2)通过IMedia.Stub.asInterface(obj)得到一个远程代理类Sub.Proxy对象mediaRemoteProxy
(3)调用mediaRemoteProxy.start()方法,事实上是调用了其内部远程Binder对象mRemotetransact()方法,将数据发送给Server
(4)服务端收到Client端发送过来的数据时onTransact()会被调用,有一个子类继承于IMedia.Stub类,实现了start()方法,onTransact()被调用时,自然调用到了子类里的start()方法。换言之,Server端的操作被Client调用执行了。

4.start()方法是在哪个线程被执行的
private IMedia.Stub stub = new IMedia.Stub() {
    @Override
    public boolean start() throws RemoteException {
        Log.d("cmy", "media start:" + Thread.currentThread().getId());
        return true;
    }
};

通过调试程序发现start()方法被执行在名为binder1binder2的线程中,而不是主线程。这也解释了系统的ActivityManagerService向应用进程发送消息时,需要使用H类把消息转发到UI线程,而且也必须这么做,因为UI线程调用了Looper.loop()开启了循环,线程是被阻塞的。

上一篇下一篇

猜你喜欢

热点阅读