本文学习自高焕堂老师的Android从程序员到架构师之路系列教学视频
33 - 认识进程(Process)与IPC架构a
1.什么是IPC通信
IPC(Inter-Process Communication)通信,是跨越两个不同进程(Process)之通信。一般而言,一个Android应用程序里的各组件(如Activity、Service等)都在同一个进程里执行。这种在同一进程内的通信,又称为短程通信,意味着,两个Activity在同一个进程(Process)里执行
相对地,远程(Remote)通信的意思是:两个组件(如Activity或Service)分别在不同的进程里执行;两者之间是IPC通信,又称远程通信
IPC通信效率
1.1当我们启动某一支应用程序(App)时,Android系统里的Zygote服务孵化(Fork)一个新进程(Process)给它,然后将它(该App)加载到这个新诞生的进程里
1.2基于Linux的安全限制,以及进程的基本特性(例如,不同进程的地址空间是独立的),如果两个类(或其对象)在同一个进程里执行时,两者沟通方便也快速。但是,当它们分别在不同的进程里执行时,两者沟通就属于IPC跨进程沟通了,不如前者方便,也慢些
2 复习:Android的进程概念
2.1一个进程是一个独立的执行空间,不会被正在其它进程里的程序所侵犯。这种保护方法是Android的重要安全机制。于是,得先认识进程的内涵,才能进一步了解跨进程IPC机制
2.2在Android的进程里,有一个虚拟机(Virtual Machine,简称VM)的对象,可执行Java代码,也引导JNI本地程序的执行,实现Java与C/C++之间的沟通
每一个进程在诞生时,都会诞生一个主线程(Main Thread),以及诞生一个Looper类的对象和一个MQ(Message Queue)数据结构。每当主线程作完事情,就会去执行Looper类。此时,不断地观察MQ的动态。
主线程最主要的工作就是处理UI画面的事件(Event),每当UI事件发生时,Android框架会丢信息(Message)到MQ里。主线程看到MQ有新的信息时,就取出信息,然后依据信息内容而去执行特定的函数。执行完毕,就再继续执行Looper类,不断地观察MQ的动态。
刚才说明了,当两个类都在同一个进程里执行时,两者之间的沟通,只要采取一般的函数调用(Function Call)就行了,既快速又方便。一旦两个类分别在不同的进程里执行时,两者之间的沟通,就不能采取一般的函数调用途径了,只好采取IPC沟通途径。
34 - 认识进程(Process)与IPC架构b
3.设定IPC通信-- 使用AndroidManifest.xml文件
在Android框架里,一个应用(程序)套件(Application Package)通常含有多个Java类(Class),这些类可以在同一个进程(Process)里执行;也可以在不同的进程里执行 。 通常,一个进程只会摆一支App。但是一支App可占用多个进程。例如有一个App的AndroidManifest.xml
文件内容如下:
<activity android:name=".FirstActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name
="android.intent.category.LAUNCHER" />
</intent-filter> </activity>
<activity android:name=".LoadActivity">
</activity>
<service android:name=".LoadService" android:process=":remote" >
<intent-filter>
<action android:name
="com.misoo.pkm.REMOTE_SERVICE" />
</intent-filter></service>
</manifest>
其中,FirstActivity和LoadActivity两个类别会加载预设的进程里。而LoadService则会加载于名为“remote”的独立进程里。
并且,由进程#1的主线程去执行FirstActivity和LoadActivity(的函数)。而由进程#2的主线程去执行LoadService。上图也可简化而表达如下图
在Android的各进程里,都有一个VM对象,执行Java代码,也引导JNI本地程序的执行,实现Java与C/C++程序之间的沟通。
35 - 认识进程(Process)与IPC架构c
4 IPC的IBinder接口-- 定义与实现
大家都知道,当两个类都在同一个进程里执行时,两者之间的沟通,只要采取一般的函数调用(Function Call)就行了,既快速又方便。一旦两个类分别在不同的进程里执行时,两者之间的沟通,就不能采取一般的函数调用途径了。只好采取IPC沟通途径。
Android框架的IPC沟通仰赖单一的IBinder接口。此时Client端调用IBinder接口的transact()函数,透过IPC机制而调用到远方(Remote)的onTransact()函数。
在Android的源代码里,Java层的IBinder接口是定义于IBinder.java代码文档里。此程序文件如下
frameworks\base\core\java\android\os\IBinder.java
public interface IBinder {
public boolean transact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException;
}
IBinder接口定义了一些函数,可以让Client程序可以进行跨进程的調用(当然也能支持同进程的短程調用)。其中,最主要的一个函数就是:transact()函数。于此,以图形来表达IBinder接口与transact()函数之间的关系。如下述的UML图
基于这个IBinder.java定义档,我们就可以开发类别来实作(Implement)它,然后提供给其它App来調用了。在Android的框架里,也撰写了Binder基类和BinderProxy类别来实作 IBinder接口。
frameworks\base\core\java\android\os\IBinder.java
public class Binder implements IBinder {
public Binder() {
init();
......
}
public final boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
......调用onTransact
boolean r = onTransact(code, data, reply, flags);
return r;
}
private boolean execTransact(int code, long dataObj, long replyObj, int flags) {
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
......
res = onTransact(code, data, reply, flags);
......
return res;
}
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException
private native final void init();
Binder基类的主要函数是
transact()函数 — 用来实作IBinder的transact()函数接口。
execTransact()函数 — 其角色与transact()函数是相同的,只是这是用来让C/C++本地程序来調用的
onTransact()函数 — 这是一个抽象函数,让应用子类来覆写(Override)的。上述的transact()和execTransact()两者都是調用onTransact()
函数来实现反向調用(IoC, Inversion of Control)的。
init()函数 — 这是一个本地(Native)函数,让JNI模块来实现这个函数。Binder()构造函数(Constructor)会調用这个init()本地函数。
// Binder.java
// ………
final class BinderProxy implements IBinder {
private int mObject;
// ..........
BinderProxy() {
// .........
}
public native boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
private int mObject;
}
当我们看到类别名称是 XXXProxy时,就自然会联想到它是摆在Client进程里,担任Service端的分身(Proxy)。
由于跨进程沟通时,并不是从Java层直接沟通的,而是透过底层的Binder Driver驱动来沟通的,所以Client端的Java类别(如Activity)必须透过BinderProxy分身的IBinder接口,转而調用JNI本地模块来衔接到底层Binder Driver驱动服务,进而調用到正在另一个进程里执行Service。
当Client透过IBinder接口而調用到BinderProxy的transact()函数,就調用到其 JNI本地模块的transact()函数,就能进而衔接到底层Binder Driver驱动服务了。
在上图里,从JNI本地模块拉了一条红色虚线,表示这并非直接的通信途径。也就是,实际上是透过底层Binder Driver驱动才調用到BBinder的IBinder接口。如下图:
5 使用IBinder接口
- IPC通信的三步骤
- 短程通信vs. 远程通信