Android知识点扫盲积累

平时工作内容基于安卓系统,确并不直接参与安卓部分开发,而只是为安卓层提供natvie方法的调用界面、实现以及再往下的BSP相关的驱动开发。但是总感觉理解安卓系统的一些重要特性也是有必要的。本文仅记录自己对安卓框架原理上的理解,无引导性、无实用性。

1、framework

使用java语言做终端开发的人大致分为两类:应用开发与framework开发。应用开发总的来说是使用安卓系统提供的原材料与框架编写特定需求的app,假如某些功能framework并未提供,例如你的产品需要使用一款奇葩的传感器,原生framework没有提供对应的api来操作它,那么就需要在有人在原有框架中添加通路,向上为应用开发工程师提供传感器的操作函数java或者叫虚拟机内的函数,向下打通到c实现的针对该传感器的linux驱动调用,这些人做的就是framework开发。
Android知识点扫盲积累
上图中展示了framework提供的所有功能框架,大部分是java代码实现,肯定也都是虚拟机这个容器内的框架了。例如安卓四大组件中的服务与activity都在这里实现
Framework定义了客户端组件和服务端组件功能及接口。包含3个主要部分:服务端,客户端和Linux驱动。

一:服务端

服务端主要包含两个重要类,分别是WindowManagerService(Wms)和ActivityManagerService(Ams)。Wms的作用是为所有的应用程序分配窗口,并管理这些窗口。包括分配窗口大小,调节各窗口的叠放次序,隐藏或显示窗口。Ams的作用是管理所有应用程序中的Activity。

除此之外,在服务端还包括2个消息处理类:
1)KeyQ类:

该类为Wms的内部类,继承于KeyInputQueue类, KeyQ对象一旦创建,就立即启动一个线程,该线程会不断地读取用户的UI操作消息,比如按键、触摸屏、trackball、鼠标等,并把这些消息放到一个消息队列QueueEvent类中。

2)InputDispatcherThread类:

该类的对象一旦创建,也会立即启动一个线程,该线程会不断地从QueueEvent中取出用户消息,并进行一定的过滤(用Wms中相应函数处理),过滤后,再将这些消息发给当前活动的客户端程序中。

二:客户端

客户端主要包括以下重要类:

1)ActivityThread类:
该类为应用程序的主线程类,所有的Apk程序有且只有一个ActivityThread类,程序的入口为该类中的static main()函数。

2)Activity类:
该类为APK程序的一个最小运行单元,一个APK程序中可以包含多个Activity对象,ActivityThread主类会根据用户操作选择运行哪个Activity对象。

3)PhoneWindow类:
该类继承与Window类,同时,PhoneWindow类内部包含了一个DecorView对象。简而言之,PhoneWindow是把一个FrameLayout进行了一定的包装,并提供了一组通用的窗口操作接口。

4)Window类:
该类提供了一组通用的窗口(Window)操作API, 这里的窗口仅仅是程序层面上的,Wms所管理的窗口并不是Window类,而是一个View或者ViewGroup类,一般就是指DecorView类,即一个DecorView就是Wms所有管理的一个窗口。Window是一个abstract类型。

5)DecorView类:
该类是一个FrameLayout的子类,并且是PhoneWindow中的一个内部类。Decor的英文是Decoration, 即“修饰”的意思,DecorView就是对普通的FrameLayout进行了一定的修饰,比如添加一个通用的TitleBar, 并响应特定的按键消息等。

6)ViewRoot类:
Wms管理客户端窗口时,需要通知客户端进行某种操作,这些都是通过异步消息完成的,实现方式就是使用Handler, ViewRoot就是继承于Handler,其作用主要是接收Wms的通知。

7)W类:
该类继承于Binder, 并且是ViewRoot的一个内部类。

8)WindowManager类:
客户端要申请创建一个窗口,而具体创建窗口的任务是由Wms完成的,WindowManager类就像是一个部门经理,谁有什么需求就告诉它,由它和Wms进行交互,客户端不能直接和Wms进行交互。

2、JNI

上一节提到java在虚拟机的包装下可以跨平台运行(即虚拟机屏蔽了软硬件的差异),但是某些驱动程序必须使用c语言进行编写,那么framework就需要提供一种方法实现这一需求。当然这并不是安卓系统中需要面对的问题,任何采用java开发应用程序都需要提供一种本地库的调用机制。
JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。具体JNI技术使用的例子可以参考:

但是要注意,JNI不是什么IPC技术,无论是JNI的Java调用者还是C++世界的实现,虽然跨越了JVM(JVM内的Java与C++的内存管理截然不同),但是这都是编译的东西,整个过程只会有一个进程,在安卓系统那就是JAVA写的activity、service所依附的进程,而C++部分只是一个so的形式,调用时是被重定位进其它进程中使用的。所以说,JNI只是一种接口规范,为JAVA语言调用c++接口提供一种跨越虚拟机的路径。

3、SDK/NDK

android sdk (Android Software Development Kit, 即android软件开发工具包)可以说只要你使用java去开发Android这个东西就必须用到。他包含了SDK Manager 和 AVD Manage,对于android系统的一些开发版本的管理以及模拟器管理。它只能运行纯java程序,有了它模拟器才可以使用。

而ndk (Native Development Kit)跟sdk差不多的是它也是一个开发工具包。用它开发c/c++是很方便的。他有一个强大的编译集合。Java调C、C++(jni接口),是一些java如何调C的代码。它会把C代码编译成一个.SO的动态库,通过jni接口用java代码去调用它,有了它我们可以直接在android代码中去添加C代码。

很早以前android是只有sdk的。并没有ndk。这就意味着一旦android的开发者要使用c/c++的三方库或者需要用到c/c++就必须使用非官方的法子,用java的jni去调用c/c++。就像耍小聪明走后门一样。而ndk的出现就意味着jni调用的这种方法转正了变成官方了以后你不需要再走后面大路正面随你走。如果要操作底层直接操作内存,操作地址那不得不去使用c/c++因为java这块想做这些有点困难。所以ndk是必须需要出现的。对于android来说sdk和ndk是同种语言的2种不同时期的必须品。
基于NDK使用jni
Android知识点扫盲积累

4、Binder

安卓系统中进程间通信的最主要的方式就是IPC,每一个服务均运行在一个独立的进程中,因此是以Java实现,所以本质上来说是运行在一个独立进程的Dalvik虚拟机中。那么问题来了,开发者的App也运行在一个独立的进程空间中,如果调用到系统的服务层中的接口?答案是IPC(Inter-Process Communication),进程间通讯是和RPC(Remote Procedure Call)不一样的,实现原理也不一样。每一个系统服务在应用框架层都有一个Manager与之对应,方便开发者调用其相关功能,具体关系如下:
Android知识点扫盲积累
Android 从下而上分了内核层、硬件抽象层、系统服务层、Binder IPC 层、应用程序框架层
Android 中"应用程序框架层"以 SDK 的形式开放给开发者使用,“系统服务层” 中的核心服务随系统启动而运行,通过应用层序框架层提供的 Manager 实时为应用程序提供服务调用。系统服务层中每一个服务运行在自己独立的进程空间中,应用程序框架层中的 Manager 通过 Binder IPC 的方式调用系统服务层中的服务。
安卓的驱动核心是linux,因此Binder的实现也离不开linux驱动的协助,按照代码分层的概念来看Binder应该是这样的:
Android知识点扫盲积累
可以看到,虽然安卓系统极力在弱化进程的概念,只暴露给用户组件的使用方式,但是组件间(并不仅仅值JVM中的应用,linux下的应用程序也是如此)的通信同样属于Linux中的IPC,另外要注意到Binder属于一种偏基础的功能,后面提到的AIDL\HIDL都是基于Binder实现的高级功能。仔细观察Binder的实现,如果要通过ioctl来使用Binder相关驱动,势必涉及到JVM内接口到Native的调用,那么就会用到JNI,因此实际的Binder代码结构如下(此图仅展示参与Bindr通信的一个安卓服务进程的函数调用,当然若进程装载的是一个linux应用,则没有Java与JNI部分):
Android知识点扫盲积累
Binder详解

5、HIDL

官话:

HAL接口定义语言(简称HIDL)适用于指定HAL和其用户之间的接口的一种接口描述语言(IDL)。HIDL允许指定类型和方法调用。从更更烦的意义上来说HIDL适用于在独立编程的代码库之间通信的系统。HIDL旨在用于进程间通信(IPC)。进程之间的通信经过Binder化。对于必须与进程相关联的代码库,还可以使用直通模式。

其实HIDL目的就一个:利用binder进程通信方式,将原来通过JNI调用Vendor提供的So的方式进行解耦,任何System下的应用进程要想使用Vendor下的接口,必须通过IPC打通。这一做法的好处就是安卓Framework下的进程与Vendor提供的服务区分为完全隔离的两个进程。
Android知识点扫盲积累
早在Android提出HIDL之前,某电视厂商在Vendor的Hal与Framework之间就有添加一层中间件代码,此时的Hal调用并非简单的JNI,具体过程如下:

  1. 中间件生成的进程负责实际调用Hal接口,并且提供Binder通信中的Client与Server库。
  2. Framework下的服务则是先通过JNI到达Native层,Native层再搞一个TVService服务去dlopen中间件编译出的Binder客户端,利用Binder通信到达服务端所在进程,即中间件进程,最终完成Hal的调用

安卓引入Treble后,无论你是中间件代码还是芯片厂家代码,都被分配到了Vendor分区,System分区的进程无法直接调用Hal接口,虽然某电视厂商的设计构架中是通过原生Binder实现的,但是就连Binder的调用也被禁止了,所以只能适配最新的HIDL,将原生的Binder直接调整为HIDL封装过的Binder,各进程关系保持不变。具体HIDL的使用方法可以参考:
Binder死磕到底(四):Treble化架构

5、AIDL

类似于HIDL,AIDL也是一种接口规范,主要用于JAVA世界内进程的通信,在Treble下则是system分区内进程(进程代码位于System分区)的通信规则。实际上AIDL仍然是Binder机制的包装,安卓只是提供了一套开发环境与规则,方便JAVA程序员更加快捷的基于Binder机制完成进程间的通信。至此,AIDL别无他妙,附注一个实现例子Android AIDL使用详解Android知识点扫盲积累

6、Activity

上一篇:耗时两个礼拜,8000字安卓面试长文,附答案


下一篇:android手机图片!Android架构组件Room功能详解,重难点整理