在Android中运行的应用程序都是通过以下三种方式来层层深入的:
- App -> Runtime Service ->lib
- App -> Runtime Service ->NativeService -> lib
- App -> Runtime Service ->NativeDaemon -> lib
下面就分别来分析这三种方式,我们还是采用流程图的方式来为大家展示。
App -> Runtime Service -> lib方式对应的流程图如图1所示。
图1 App ->Runtime Service -> lib方式的流程图
通过图1我们可以看出,在Android平台上,应用程序首先是在应用程序层通过Binder IPC调用应用程序框架层的Runtime Service,然后再通过JNI与运行库中的原生服务绑定,并动态地加载Hal库,进而调用Linux内核层的Kernel Driver。为了便于大家更好地理解,我们通过一个实例(Location Manager)来分析该流程,如图2所示:
图2 LocationManager调用流程
以上就是第一种方式的调用过程,接下来我们再了解一下第二种方式App -> Runtime Service ->Native Service -> lib是如何调用的。这种方式通常被Android的原生服务所采用,同样先看一下调用流程图,如图3所示。
图3 App ->Runtime Service ->Native Service -> lib方式的流程
图3为我们展示了Android原生服务的调用流程,可以看出,与第一种方式相比,只多了一个通过IPC机制调用原生服务并进行动态装载的过程。以下为一个Audio的例子,如图4所示。
图4 Audio原生服务的调用流程
从图4可以看出,应用程序调用了应用程序框架层的MediaPlayer,然后调用系统运行库底层的MediaPlayer。这里MediaPlayer又分别调用了Media Framework和AudioFlinger,而后通过AudioFlinger调用指定的库(libaudio.so),最后才调用到Kernel Driver。
下面来看一下最后一种方式“App-> Runtime Service ->Native Daemon -> lib”,如图5所示。这种方式通常用于守护进程的连接。
图5 原生守护进程的调用流程
从图5可以看出,这种方式比原生服务的调用更简单,它直接通过JNI绑定原生服务,再通过sockets调用守护进程进行动态加载。下面来看一个简单的例子,电话管理(Telephony Manager)的调用就是这样一个原生的守护进程调用,其流程如图6所示。
图6 TelephonyManager的调用流程
这个调用的过程非常简单。