美文网首页
Service-远程通信-AIDL

Service-远程通信-AIDL

作者: Lonie233 | 来源:发表于2017-03-13 22:13 被阅读16次

膜拜郭神

AIDL( Android Interface Definition Language)安卓接口定义语言,可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。但是,不推荐使用。

具体的使用方法参见郭神链接,这里只是说一点自己的理解~。

1. 这个东西很不好写。

很容易出错,基本上全是IBinder(接口)和Binder(实现前者)两个类型转化,而且还会自动生成一个接口类,虽然跟你定义的AIDL文件同名,但是在android studio 下的android目录下看不到,所以基本靠记,而且还有build之后才能有。

2.注意当你在远程服务里做一些UI上的事,比如弹个toast,会报错。

而且如果你看日志只看主进程的话,看不到,极其坑爹。也不会崩溃。日志如下

呵呵呵 1.1

看起来像是你的远程服务进程没有初始化Looper,然后你在构造方法,静态方法巴拉巴拉里写了prepare,会报主线程只允许有一个Looper,所以说白了那个错误,没有在主线程更新UI。那怎么办?两种解法,一种用handler发消息,另外一种自己写个线程,然后prepare。UI操作,Loop。(主要我也不清楚这个时候的更新UI操作在哪个线程里~比较菜,可以在评论里知道下哈)

上面这个问题感觉就是服务本来就是后台的,不需要跟用户交互,所以本来应该也没这个问题~我就是试出来的,完全可以把数据返回给主进程,让主进程去展示的。


分割线就是我


又研究了一遍,还是记下来吧

AIDL 使用过程不再阐述,自行百度,下面说下跨进程发生了啥

调用过程看起来是在客户端直接调用binder然后强转调用方法即可,但是,但是,说下隐藏点。

  1. onserviceconnected
    • 参数 IBinder 有两种类型 Binder 和 BinderProxy(没见过吧) ,其中Binder 类型是非跨进程的时候拿到的,BinderProxy 是跨进程的,然后看各自的asInterface方法(这里asinterface 需要转成你定义的接口才能调用)
onserviceConnected

IBookManager manager = IBookManager.Stub.asInterface(service)
//service 可能是Binder 类型或者是 BinderProxy,为什么是这两种类型自行百度,因为我也没看明白

Binder

    /**
     * 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)) {
            //这个owner 在构造函数里调用attch 方法设置了,所以会返回自己
            return mOwner;
        }
        return null;
    }

BinderProxy

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

调用方

        /**
         * Cast an IBinder object into an com.test.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.test.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
           //这里,看到了,如果是Binder那么能查询到就会直接返回,然后调用,如果返回null(binderproxy)那么创建一个代理类回去
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.test.aidl.IBookManager))) {
                return ((com.test.aidl.IBookManager) iin);
            }
            return new com.test.aidl.IBookManager.Stub.Proxy(obj);
        }

看了上边的几段代码应该能明确了如果 是跨进程调用的话,会返回给客户端一个服务端Binder的代理对象,这个代理对象继承自stub(binder)的实现,所以具有同样的方法,至于调用参照2.

  1. binder 代理类的调用
    客户端拿到binder (实际是个代理,客户端不知道也不care)之后,看是调用了,调用到代理的各种方法,举个栗子
@Override
public java.util.List<com.test.aidl.Book> getBookList() throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.util.List<com.test.aidl.Book> _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
        _reply.readException();
        _result = _reply.createTypedArrayList(com.test.aidl.Book.CREATOR);
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

首先看到了一大堆序列化的东西,为啥需要序列化???想想intent是怎么传递数据的?为啥intent 那么传递?因为支持跨进程啊,可以看到,实际的调用是mRemote 对象,mRemote对象是啥? 上边的asInterface 里边的最后一句

return new com.test.aidl.IBookManager.Stub.Proxy(obj);

就是它了,所以mRemote 是obj 也就是一个BinderProxy 对象,敲黑板重点来了:

  1. 这个obj(BinderProxy 对象) 是remote 进程的binder 的代理
  2. 跨进程调用的实际是 transcat 方法
  3. Stub.TRANSACTION_getBookList 这是个id,标识了要真正调用的方法

所以过程为,客户端拿到了一个代理A,这个代理A包装了一个B 的代理B+,客户端调用 A->B+ ->驱动跨进程->B->真正的方法

remote进程 的解析,并回写结果
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    switch (code) {
        case TRANSACTION_getBookList: {
            data.enforceInterface(DESCRIPTOR);
            java.util.List<com.test.aidl.Book> _result = this.getBookList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

全是代理。大概是这样 client <——> Binder驱动 <——> remote,只是Binder 驱动那层我们感知不到,所以看起来是两个进程直接通信的。
另外还有messagener 通信,据说是调用优化版,没有细看代码。以上基本就是AIDL 的整个过程了,至于绑定服务相关和驱动层还没看估计短时间也不会看了。

大神链接
Binder学习指南-Weishu's Notes

<font color=red>就是它了</font>

相关文章

网友评论

      本文标题:Service-远程通信-AIDL

      本文链接:https://www.haomeiwen.com/subject/sitxnttx.html