学习 AIDL,这一篇文章就够了(下)

623 查看

前言

上一篇博文介绍了关于AIDL是什么,为什么我们需要AIDL,AIDL的语法以及如何使用AIDL等方面的知识,这一篇博文将顺着上一篇的思路往下走,接着介绍关于AIDL的一些更加深入的知识。强烈建议大家在看这篇博文之前先看一下上一篇博文:学习 AIDL,这一篇文章就够了(上)

注:文中所有代码均源自上一篇博文中的例子。
另:在看这篇博文之前,建议先将上一篇博文中的代码下载下来或者敲一遍,然后确定可以正常运行后再接着看。因为文中有大量对于具体代码的分析以及相关代码片段之间的跳转,如果你手头没有一份完整代码的话很容易看得一头雾水,最后浪费了你的时间也浪费了这篇博文。

正文

1,源码分析:AIDL文件是怎么工作的?

进行到上一篇文章的最后一步,我们已经学会了AIDL的全部用法,接下来让我们透过现象看本质,研究一下究竟AIDL是如何帮助我们进行跨进程通信的。

我们在上一篇提到过,在写完AIDL文件后,编译器会帮我们自动生成一个同名的 .java 文件——也许大家已经发现了,在我们实际编写客户端和服务端代码的过程中,真正协助我们工作的其实是这个文件,而 .aidl 文件从头到尾都没有出现过。这样一来我们就很容易产生一个疑问:难道我们写AIDL文件的目的其实就是为了生成这个文件么?答案是肯定的。事实上,就算我们不写AIDL文件,直接按照它生成的 .java 文件那样写一个 .java 文件出来,在服务端和客户端中也可以照常使用这个 .java 类来进行跨进程通信。所以说AIDL语言只是在简化我们写这个 .java 文件的工作而已,而要研究AIDL是如何帮助我们进行跨进程通信的,其实就是研究这个生成的 .java 文件是如何工作的。

1.1,这个文件在哪儿?

要研究它,首先我们就需要找到它,那么它在哪儿呢?在这里:

bb4fa980b57d4fc7

它的完整路径是:app->build->generated->source->aidl->debug->com->lypeer->ipcclient->BookManager.java(其中 com.lypeer.ipcclient是包名,相对应的AIDL文件为 BookManager.aidl )。在Android Studio里面目录组织方式由默认的 Android 改为 Project 就可以直接按照文件夹结构访问到它。

1.2,从应用看原理

和我一贯的分析方式一样,我们先不去看那些冗杂的源码,先从它在实际中的应用着手,辅以思考分析,试图寻找突破点。首先从服务端开始,刨去其他与此无关的东西,从宏观上我们看看它干了些啥:

可以看到首先我们是对 BookManager.Stub 里面的抽象方法进行了重写——实际上,这些抽象方法正是我们在 AIDL 文件里面定义的那些。也就是说,我们在这里为我们之前定义的方法提供了具体实现。接着,在 onBind() 方法里我们将这个 BookManager.Stub 作为返回值传了过去。

接着看看客户端:

简单的来说,客户端就做了这些事:获取 BookManager 对象,然后调用它里面的方法。

现在结合服务端与客户端做的事情,好好思考一下,我们会发现这样一个怪事情:它们配合的如此紧密,以至于它们之间的交互竟像是同一个进程中的两个类那么自然!大家可以回想下平时项目里的接口回调,基本流程与此一般无二。明明是在两个线程里面,数据不能直接互通,何以他们能交流的如此愉快呢?答案在 BookManager.java 里。

1.3,从客户端开始

一点开 BookManager.java ,我发现的第一件事是:BookManager 是一个接口类!一看到它是个接口,我就知道,突破口有了。为什么呢?接口意味着什么?方法都没有具体实现。但是明明在客户端里面我们调用了 mBookManager.addBook() !那么就说明我们在客户端里面用到的 BookManager 绝不仅仅是 BookManager,而是它的一个实现类!那么我们就可以从这个实现类入手,看看在我们的客户端调用 addBook() 方法的时候,究竟 BookManager 在背后帮我们完成了哪些操作。首先看下客户端的 BookManager 对象是怎么来的:

在这里我首先注意到的是方法的传参:IBinder service 。这是个什么东西呢?通过调试,我们可以发现,这是个 BinderProxy 对象。但随后我们会惊讶的发现:Java中并没有这个类!似乎研究就此陷入了僵局——其实不然。在这里我们没办法进一步的探究下去,那我们就先把这个问题存疑,从后面它的一些应用来推测关于它的更多的东西。

接下来顺藤摸瓜去看下这个 BookManager.Stub.asInterface() 是怎么回事: