Android操作系统知识详解
这篇博文主要是介绍Android操作系统中,个人汇总的比较重要的一些知识点,在Android的系统四层结构中(有些地方分为五层),作为应用开发的程序员,我们常常可能只关注最上层的应用层,但是其实在我们操作或者使用某些系统接口时,其实都无时无刻涉及到系统其它层级的知识点,所以如果要做到“知其然,知其所以然”,深入理解系统其它层的知识,对提高我们开发的视野也是很有帮助的,可能这就是别人常说的上帝视角吧,哈哈。
知识点汇总:
一:背景
二:Android系统知识点汇总
三:应用程序层知识点详解
四:应用程序框架层知识点详解
五:系统运行库层知识点详解
六:Linux内核层知识点详解
七:Android操作系统的演进
八:扩展阅读
一:背景
Android 是Google开发的基于Linux平台的开源手机操作系统。它包括操作系统、用户界面和应用程序 —— 移动电话工作所需的全部软件,而且不存在任何以往阻碍移动产业创新的专有权障碍。Android采用WebKit浏览器引擎,具备触摸屏、高级图形显示和上网功能,用户能够在手机上查看电子邮件、搜索网址和观看视频节目等,比iPhone等其他手机更强调搜索功能,界面更强大,可以说是一种融入全部Web应用的单一平台。
Android系统架构图:
Android的系统架构和其操作系统一样,采用了分层的架构。从架构图看,android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。
Android系统架构图:(五层结构)
第一层:应用程序层
Android会同一系列核心应用程序包一起发布,该应用程序包包括email客户端,SMS短消息程序,日历,地图,浏览器,联系人管理程序等,所有的应用程序都是使用JAVA语言编写的。
第二层:应用程序框架层
开发人员也可以完全访问核心应用程序所使用的API框架,该应用程序的架构设计简化了组件的重用,任何一个应用程序都可以发布它的功能块并且任何其它的应用程序都可以使用其所发布的功能块(不过得遵循框架的安全性限制)。
同样,该应用程序重用机制也使用户可以方便的替换程序组件,这一层的是由Java代码编写的,可以称为Java Framework。
隐藏在每个应用后面的是一系列的服务和系统, 其中包括:
第三层:系统运行库层
模块一:C/C++程序库
Android 包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过 Android 应用程序框架为开发者提供服务。
模块二:Android 运行时库
Android 包括了一个核心库,该核心库提供了JAVA编程语言核心库的大多数功能。
每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。Dalvik被设计成一个设备可以同时高效地运行多个虚拟系统。 Dalvik虚拟机执行(.dex)的Dalvik可执行文件,该格式文件针对小内存使用做了优化。同时虚拟机是基于寄存器的,所有的类都经由JAVA编译器编译,然后通过SDK中 的 "dx" 工具转化成.dex格式由虚拟机执行。
Dalvik虚拟机依赖于linux内核的一些功能,比如线程机制和底层内存管理机制。
第四层:硬件抽象层(HAL)
硬件抽象层是位于操作系统内核与硬件电路之间的接口层,其目的在于将硬件抽象化,为了保护硬件厂商的知识产权,它隐藏了特定平台的硬件接口细节,为操作系统提供虚拟硬件平台,使其具有硬件无关性,可在多种平台上进行移植。 从软硬件测试的角度来看,软硬件的测试工作都可分别基于硬件抽象层来完成,使得软硬件测试工作的并行进行成为可能。通俗来讲,就是将控制硬件的动作放在硬件抽象层中。
第五层:Linux 内核层
Android 的核心系统服务依赖于 Linux 2.6 内核,如安全性,内存管理,进程管理, 网络协议栈和驱动模型。Linux 内核也同时作为硬件和软件栈之间的抽象层。 (Android 5.0内核层改为Linux 3.0)
二:Android系统知识点汇总
知识点一:应用的安装过程详解
安装流程:
1、启动由packageinstaller.apk应用处理安装及卸载过程的界面,点击安装。
2、通过Binder与PackageManagerService通信,需要系统服务提供apk的安装服务。
3、复制APK安装包到data/app目录下,解压并扫描安装包,把dex文件(Dalvik字节码)保存到dalvik-cache目录,并data/data目录下创建对应的应用数据目录。
4、完成安装,发送Intent.ACTION_PACKAGE_ADDED广播。
应用安装涉及到如下几个目录:
1、data/app ---------------用户程序安装的目录,安装时把apk文件复制到此录。 2、data/data ---------------存放应用程序的数据。
3、data/dalvik-cache--------将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)。
知识点二:应用的启动过程
过程概况:
1、Launcher所在进程通过binder发送消息给system_server进程;
2、system_server进程调用Process.start()方法向Zygote进程发出创建新进程请求。关于system server在上面的系统启动流程中有提到,它是zygote的第一个子进程。
3、zygote进程fork自身,开启一个Linux进程和一个主线程。ZygoteInit.main()方法来实例化Activity Thread对象,并最终返回新进程的pid给ActivityManagerService。
4、ActivityThread随后依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环。
5、通过ActivityThread把新建的进程和Application绑定,然后加载app的classes到内存中。
6、启动 Activity。
启动过程图解:
知识点三:系统的启动过程与核心进程的作用
Android系统启动流程,如下所示。
1、启动电源以及系统启动 当电源按下时引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序Bootloader到RAM,然后执行。
2、引导程序BootLoader 引导程序BootLoader是在Android操作系统开始运行前的一个小程序,它的主要作用是把系统OS拉起来并运行。
3、Linux内核启动 内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。当内核完成系统设置,它首先在系统文件中寻找init.rc文件,并启动init进程。
4、init进程启动 初始化和启动属性服务,并且启动Zygote进程。
5、Zygote进程启动 创建JavaVM并为JavaVM注册JNI,创建服务端Socket,启动SystemServer进程。
6、SystemServer进程启动 启动Binder线程池和SystemServiceManager,并且启动各种系统服务。 7.Launcher启动 被SystemServer进程启动的ActivityManagerService会启动Launcher,Launcher启动后会将已安装应用的快捷图标显示到界面上。
核心进程:
1、Init进程描述:
当初始化内核之后,就会启动一个相当重要的祖先进程,也就是init进程,在Linux中所有的进程都是由init进程直接或间接fork出来的。init进程负责创建系统中最关键的几个核心daemon(守护)进程,尤其是zygote和servicemanager。前者是android启动的第一个dalvik 虚拟机,它将负责启动Java世界的进程;后者是BInder通信的基础。另外,它还提供了property service(属性服务),类似于windows系统的注册表服务。
2、Zygote进程(系统两大核心进程之一)描述:
这是一个Android平台的非常基础的进程. 这个进程初始化了第一个VM, 并且预加载了framework和众多App所需要的通用资源. 然后它开启一个Socket接口来监听请求, 根据请求孵化出新的VM来管理新的App进程. 一旦收到新的请求, Zygote会基于自身预先加载的VM来孵化出一个新的VM创建一个新的进程,Zygote 的中文意思是受精卵,从这个意思里也可以看出 Zygote 进程是用来分裂复制(fork)的,实际上所有的 App 进程都是通过对 Zygote 进程的 Fork 得来的。
3、SystemService进程(系统两大核心进程之一)描述:
这个进程在整个的Android中非常重要,它和Zygote进程一样,是Android Framework层的两大重要进程。系统里面重要的服务都是在这个进程里面开启的,例如ActivityManagerService, WindowsManagerService, PackageManagerService等等都是由这个SystemServer fork出来的。
4、SystemServiceManager进程描述:
SystemServiceManager管理各个service,用SystemServiceManager启动service的时候,会把service加入自己的链表。并且调用service的onStart函数,在SystemServer中启动的各种服务,在ServiceManager中注册的是用于进程间通信的。
5、MediaManager进程描述:
是整个Android中media部分的核心和灵魂。几乎所有与多媒体播放相关的内容都放在这里。包括了音视频的编解码以及显示输出,分别是创建各个子模块的对象实例,例如:AudioFlinger,MediaPlayerService,CameraService,AudioPolicyService等。
6、Android系统守护进程守护进程: 一直在后台运行的进程。 在init.rc中定义了很多系统的守护进程:
1、uevent:负责相应uevent事件,创建设备节点文件。
2、console:包含常用的shell命令、如ls、cd等。
3、adbd:abd的守护进程。
4、servicemanager:binder的服务总管,负责binder服务的注册和查找。
5、vold:负责完成系统USB存储卡等扩展存储自动挂载的守护进程。
6、netd:Android 网络守护进程。
7、debuggerd:负责异常退出的诊断。如果侦测到程序崩溃,debuggerd将把崩溃时的进程状态信息输出到文件和串口中,供开发人员分析和调试使用。
8、ril-deamon:手机底层的通信系统的守护进程。
9、surfaceflinger:负责合成系统所有显示图层的服务进程。
10、media:系统多媒体部分的守护进程,包含了audio、mediaplayer以及camera 等系统服务。
11、bootanim:播放开机动画的进程。
12、installd:Android的安装服务守护进程。
13、lmkd:lowmemorykiller的守护进程,Java层的LowMemoryKiller最终都是由lmkd来完成。
知识点四:进程之间的通信方式(暂不包含Binder)
管道:管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信,在创建时分配一个page大小的内存,缓存区大小比较有限。
命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点,信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信。
共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥,无须复制,共享缓冲区直接附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决。
套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字,作为更通用的接口,传输效率低,主要用于不同机器或跨网络的通信。
内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段,常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数),不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等。
用于进程间通讯(IPC)的四种不同技术: 共享内存,临界区,管道,消息
1. 消息传递(管道,FIFO,posix和system v消息队列)
2. 同步(互斥锁,条件变量,读写锁,文件和记录锁,Posix和System V信号灯)
3. 共享内存区(匿名共享内存区,有名Posix共享内存区,有名System V共享内存区)
4. 过程调用(Solaris门,Sun RPC)
知识点五:系统服务有哪些,分别实现的主要功能
描述:java系统服务由SystemServer系统进程启动,它分为:核心平台服务和硬件服务。
一:核心平台服务
一般不会与应用程序进行交互,他们是Android Framework运行所必须的服务。
AMS(Activity Manager Service):
是Android中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度,管理所有activity的生命周期与堆栈等工作,其职责与操作系统中的进程管理和调度模块相类似,因此它在Android中非常重要。
WMS(Window Manager Service):
Android系统中的窗口也是以堆栈的形式组织在WindowManagerService服务中的,其中,Z轴位置较低的窗口位于Z轴位置较高的窗口的下面,其位于surface Flinger之上,将要绘制到机器画面上的内容传递给Surface Flinger。
PMS(Package Manager Service):
用来管理所有的package信息,包括安装、卸载、更新以及解析AndroidManifest.xml以组织相应的数据结构,加载apk文件(android包文件)的信息,提供信息显示系统中设置了哪些包,以及加载了哪些包,这些数据结构将会被PMS、ActivityMangerService等等service和application使用到。
硬件服务:提供了一系列的API,用于控制底层的硬件。
AlarmManagerService:在特定时间后运行指定的应用程序,就像定时器。
ConnectivityService:提供有关网络当前状态的信息。
LocationService:提供终端当前的位置信息。
PowerService:设备电源管理。
SensorService:提供Android中各种传感器(磁力感应器,加速度传感器等)的感应值。 TelephonyService:提供话机状态及电话服务。 WifiService:控制无线网络连接,如AP搜索,连接列表管理等。
知识点六:如何调用C/C++编写的程序库
JNI介绍:
定义:Java Native Interface,即 Java本地接口。
作用: 使得Java与本地其他类型语言(如C、C++)交互,即在 Java代码 里调用 C、C++等语言的代码 或 C、C++代码调用 Java 代码,因为 Java 具备跨平台的特点,所以Java 与 本地代码交互的能力非常弱,采用 JNI特性增强 Java 与本地代码交互的能力。
特别注意: JNI是 Java 调用 Native 语言的一种特性。 JNI 是属于 Java 的,与 Android 无直接关系。
代码实现步骤:
1、在Java中声明Native方法(即需要调用的本地方法)。
2、编译上述 Java源文件javac(得到 .class文件)。
3、通过javah 命令导出JNI的头文件(.h文件)。
4、使用Java调用需要交互的本地代码实现在 Java中声明的Native方法,如Java 需要与C++ 交互,那么就用C++实现Java的Native方法。
5、编译.so库文件。
6、通过Java命令执行Java程序,最终实现Java调用本地代码。
NDK介绍:
定义:Native Development Kit,是Android的一个工具开发包,NDK是属于 Android 的,与Java并无直接关系。
作用:快速开发C、 C++的动态库,并自动将so和应用一起打包成 APK即可通过 NDK在Android中使用JNI与本地代码(如C、C++)交互。
使用场景:在Android的场景下使用JNI,即 Android开发的功能需要本地代码(C/C++)实现。
NDK特点:
NDK与JNI关系:
知识点七:JVM,DVM,ART虚拟机的演进
什么是虚拟机:虚拟机是指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。
Android中虚拟机的使用:我们都知道android程序的运行是基于java语言的,也就是说运行android程序需要类似在JVM的虚拟机之上运行的,那让我们来了解一下JVM。
什么是JVM:JVM本质上就是一个软件,是计算机硬件的一层软件抽象,在这之上才能够运行Java程序,JAVA在编译后会生成类似于汇编语言的JVM字节码,与C语言编译后产生的汇编语言不同的是,C编译成的汇编语言会直接在硬件上跑,但JAVA编译后生成的字节码是在JVM上跑,需要由JVM把字节码翻译成机器指令,才能使JAVA程序跑起来。
为什么google要在JVM基础上重新开发DVM?
JVM虚拟机可以运行在服务器上,PC上,也可以运行在移动设备上,并根据不同的运行硬件会设计出不用的JVM虚拟机,为了能针对移动设备设计特有的硬件环境,使应用程序尽可能流畅的运行,在设计虚拟机需要重点关注哪些要点呢?1、如何用更少的内存来运行程序。(节约内存) 2、如何用更少的CPU处理资源来运行程序。(节约CPU资源) 3、在程序生成,运行的过程中,如何用更少的存储空间使程序也能正常运行。
google在DVM的优化:
优化点一:CLASS变为DEX
JAVA虚拟机运行的是JAVA字节码,Dalvik虚拟机运行的是Dalvik字节码,JAVA程序经过编译,生成JAVA字节码保存在class文件中,JVM通过解码class文件中的内容来运行程序。而DVM运行的是Dalvik字节码,所有的Dalvik字节码由JAVA字节码转换而来,并被打包到一个DEX(Dalvik Executable)可执行文件中,DVM通过解释DEX文件来执行这些字节码 , Dalvik可执行文件体积更小:为了减小执行文件的体积,安卓使用Dalvik虚拟机,SDK中有个dx工具负责将JAVA字节码转换为Dalvik字节码,dx工具对JAVA类文件重新排列,将所有JAVA类文件中的常量池分解,消除其中的冗余信息,重新组合形成一个常量池,所有的类文件共享同一个常量池,使得相同的字符串、常量在DEX文件中只出现一次,Dex这么设计可以使文件更紧凑,减少拢余,还可以共享资源,进而减少文件大小。
JVM虚拟机与DVM虚拟机的执行文件的区别:
优化点二:基于栈变为基于寄存器
JAVA虚拟机基于栈结构,程序在运行时虚拟机需要频繁的从栈上读取写入数据,这个过程需要更多的指令分派与内存访问次数,会耗费很多CPU时间,Dalvik虚拟机基于寄存器架构,数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式要快很多。
优化点三: 每一个DVM实例都是一个独立的进程空间
每一个Android应用都允许在自己的DVM实例中。每一个DVM实例都是一个独立的进程空间,所有Android的线程都对应Linux的线程,DVM可以更多地依赖操作系统的线程调度和管理机制,不同应用都是用不同的Linux用户运行以最大程度保护用户应用程序的安全性和独立性。
google为什么后期又推出ART虚拟机替代DVM虚拟机?
通过上面的描述,我们大概了解了谷歌在开始设计虚拟机的时候,在JVM的基础上做了各种优化,从而有了DVM虚拟机,那在android 4.4之后,为什么谷歌使用了新的ART虚拟机,ART虚拟机又有了什么新的优点来适应移动设备环境。 Android操作系统已经成熟,Google的Android团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的Dalvik运行时,Google开发者已经花了两年时间开发更快执行效率更高更省电的ART,替代Dalvik运行时。
ART代表Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART则完全改变了这套做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。
那么从DVM变为ART会有那方面的优缺点呢?
优点:
1、系统性能的显著提升。
2、应用启动更快、运行更快、体验更流畅、触感反馈更及时。
3、更长的电池续航能力。
4、支持更低的硬件。
缺点:
1、更大的存储空间占用,可能会增加10%-20%。
2、更长的应用安装时间。
知识点八:Android系统的进程层级
描述:Android中,同一个应用的所有组件在默认情况下都运行在同一个进程中,但也可以通过修改manifest文件中的android:process属性来指定该组件要运行中那个进程,也可以让不同应用的组件们运行在同一个进程中,当然这些应用要共享一个用户ID,并且有相同的数字证书。
Android可能在某个时刻决定关闭一个进程,当决定要关闭那些进程的时候,系统会衡量每个进程与用户的紧密程度,这时候就跟Android中进程的级别有关了。像一个具有可见的activity的进程要比那些activity都是不可见的进程拥有更高的等级,更不容易被系统杀死。
Android的进程等级有那些呢?
前台进程:(foreground process)
用户当前正在做的事情需要这个进程。如果满足下面的条件之一,一个进程就被认为是前台进程:
1、这个进程拥有一个正在与用户交互的Activity(这个Activity的onResume()方法被调用)。
2、这个进程拥有一个绑定到正在与用户交互的activity上的Service。
3、这个进程拥有一个前台运行的Service(service调用了方法startForeground()).
4、这个进程拥有一个正在执行其任何一个生命周期回调方法(onCreate(),onStart(),或onDestroy())的Service。
5、这个进程拥有正在执行其onReceive()方法的BroadcastReceiver。
通常,在任何时间点,只有很少的前台进程存在。它们只有在达到无法调合的矛盾时才会被杀--如内存太小而不能继续运行时。通常,到了这时,设备就达到了一个内存分页调度状态,所以需要杀一些前台进程来保证用户界面的反应。
可见进程:(visible process)
没有任何前台组件,但是仍然能影响用户在屏幕上看到东西。一个进程满足下面任何一个条件都被认为是可视的:
1、寄宿着一个不是前台的活动,但是它对用户仍可见(它的onPause()方法已经被调用)。举例来说,这可能发生在,如果一个前台活动在一个对话框(其他进程的)运行之后仍然是可视的,比如输入法的弹出时。
2、寄宿着一个服务,该服务绑定到一个可视的活动。
服务进程(Service process):
是一个运行着一个用startService()方法启动的服务,并且该服务并没有落入上面2种分类。虽然服务进程没有直接关系到任何用户可见的,它们通常做用户关心的事(诸如在后台播放mp3或者从网络上下载数据),因此系统保持它们运行,除非没有足够内存来保证所有的前台进程和可视进程。
后台进程(Background process):
是一个保持着一个当前对用户不可视的活动(已经调用Activity对象的onStop()方法)(如果还有除了UI线程外其他线程在运行话,不受影响)。这些进程没有直接影响用户体验,并且可以在任何时候被杀以收回内存用于一个前台、可视、服务进程。一般地有很多后台进程运行着,因此它们保持在一个LRU(least recently used,即最近最少使用,如果您学过操作系统的话会觉得它很熟悉,跟内存的页面置换算法LRU一样。)列表以确保最近使用最多的活动的进程最后被杀。
空进程(Empty process):
是一个没有保持活跃的应用程序组件的进程。保持这个进程可用的唯一原因是作为一个cache以提高下次启动组件的速度。系统进程杀死这些进程,以在进程cache和潜在的内核cache之间平衡整个系统资源。
总结:
1、一个进程的排名因为其他进程依赖它而上升。一个进程服务其它进程,它的排名从不会比它服务的进程低。例如,进程A中的一个内容提供者服务进程B中的一个客户,或者进程A中的一个服务绑定到进程B中的一个组件,进程A总是被认为比进程B重要。
2、因为一个服务进程排名比后台活动的进程排名高,一个活动启动一个服务来初始化一个长时间运行操作,而不是简单地衍生一个线程——特别是如果操作很可能会拖垮活动(例如出现ANR)。这方面的例子是在后台播放音乐和上传相机拍摄的图片到一个网站。使用服务保证操作至少有“服务进程”的优先级,无论活动发生什么情况。
3、Android修改了Linux内核里标准的OOM Killer,取而代之是一个叫LowMemKiller的驱动,触发Out Of Memory事件的不再是Linux内核里的Notifier,而由Android系统进程来驱动。像我们前面说明的,在Android里负责管理进程生成与Activity调用栈的会是这个系统进程,这样在遇到系统内存不够(可以直接通过查询空闲内存来得到)时,就触发Low Memory Killer驱动来杀死进程来释放内存。
知识点九:SandBox机制
描述:沙箱(sandbox)是为执行中的程序提供隔离环境的一种安全机制。它通过严格控制执行的程序所访问的资源,以确保系统的安全。
在Android系统中,应用(通常)都在一个独立的沙箱中运行,即每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。Dalvik经过优化,允许在有限的内存中同时高效地运行多个虚拟机的实例,并且每一个Dalvik应用作为一个独立的Linux进程执行。Android这种基于Linux的进程“沙箱”机制,是整个安全设计的基础之一。
Android从Linux继承了已经深入人心的类Unix进程隔离机制与最小权限原则,同时结合移动终端的具体应用特点,进行了许多有益的改进与提升。具体而言,进程以隔离的用户环境运行,不能相互干扰,比如发送信号或者访问其他进程的内存空间。
因此,Android沙箱的核心机制基于以下几个概念:
1、标准的Linux进程隔离
2、大多数进程拥有唯一的用户ID(UID)
3、严格限制文件系统权限。
应用程序在独立的进程:
应用程序进程之间,应用程序与操作系统之间的安全性由Linux操作系统的标准进程级安全机制实现。在默认状态下,应用程序之间无法交互,运行在进程沙箱内的应用程序没有被分配权限,无法访问系统或资源。因此,无论是直接运行于操作系统之上的应用程序,还是运行于Dalvik虚拟机的应用程序都得到同样的安全隔离与保护,被限制在各自“沙箱”内的应用程序互不干扰,对系统与其他应用程序的损害可降至最低。Android应用程序的“沙箱”机制如下图 1,互相不具备信任关系的应用程序相互隔离,独自运行,箭头访问是禁止的。
应用程序在同一个进程(共享UID):
在很多情况下,源自同一开发者或同一开发机构的应用程序,相互间存在信任关系。Android系统提供一种所谓共享UID(SharedUserID)机制,使具备信任关系的应用程序可以运行于同一进程空间。通常 ,这种信任关系由应用程序的数字签名确定,并且需要应用程序在manifest文件中使用相同的UID。
不同的应用程序可以运行在相同的进程中。对于此方法,首先必须使用相同的私钥签署这些应用程序,然后必须使用 manifest 文件给它们分配相同的 Linux 用户 ID,这可以通过用相同的值/名定义 manifest 属性 android:sharedUserId 来做到。通过sharedUserId,拥有同一个User id的多个APK安装包可以配置成运行在同一个进程中.所以默认就是可以互相访问任意数据. 也可以配置成运行成不同的进程, 同时可以访问其他APK的数据目录下的数据库和文件.就像访问本程序的数据一样。这样就为同一个机构开发的不同App之间的数据共享,提供了便利。
应用程序在同一个进程(共享UID):
知识点十:进程的保活与拉活
实现思想:
1.保活:通过提高进程优先级,降低进程被杀死的概率
2.拉起:进程被杀死后,进行拉起
保活的手段:
一:利用 Activity 提升权限:监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。通过该方案,可以使进程的优先级在屏幕锁屏时间由4提升为最高优先级1。
二:利用 Notification 提升权限:Android 中 Service 的优先级为4,通过 setForeground 接口可以将后台 Service 设置为前台 Service,使进程的优先级由4提升为2,从而使进程的优先级仅仅低于用户当前正在交互的进程,与可见进程优先级一致,使进程被杀死的概率大大降低。
三:提升service优先级:在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。
四:当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些。
五:Application加上Persistent属性。
拉活的手段:
一:利用系统广播拉活(注册高频率广播接收器,唤起进程),例如:开机广播,网络变化,文件挂载,屏幕亮灭,锁屏解锁,应用安装卸载。
二:双进程相互唤起。
三:onDestroy方法里重启service:service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service。
四:利用第三方应用广播拉活:通过反编译第三方 Top 应用,如:手机QQ、微信、支付宝、UC浏览器等,以及友盟、信鸽、个推等 SDK,找出它们外发的广播,在应用中进行监听,这样当这些应用发出广播时,就会将我们的应用拉活。
五:利用系统Service机制拉活:将 Service 设置为 START_STICKY,利用系统机制在 Service 挂掉后自动拉活。
六:利用Native进程拉活:1、利用 Linux 中的 fork 机制创建 Native 进程,在 Native 进程中监控主进程的存活,当主进程挂掉后,在 Native 进程中立即对主进程进行拉活。 2、在 Android 中所有进程和系统组件的生命周期受 ActivityManagerService 的统一管理。而且,通过 Linux 的 fork 机制创建的进程为纯 Linux 进程,其生命周期不受 Android 的管理。
七、利用 JobScheduler 机制拉活:Android5.0 以后系统对 Native 进程等加强了管理,Native 拉活方式失效。系统在 Android5.0 以上版本提供了 JobScheduler 接口,系统会定时调用该进程以使应用进行一些逻辑操作。
八:利用账号同步机制拉活:Android 系统的账号同步机制会定期同步账号进行,该方案目的在于利用同步机制进行进程的拉活。
九:依靠第三方:根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送;其他手机可以考虑接入腾讯信鸽或极光推送与小米推送做 A/B Test。
知识点十一:Jobscheduler使用
背景:Android 5.0系统以前,在处理一些特定情况下的任务,或者是为了应用的保活,我们通常是使用了Service常驻后台来满足我们的需求。当达到某个条件时触发该Service来进行相应任务的处理。或者仅仅是为了我们自己的应用不被系统回收销毁。这样做在满足了自己应用的需求的同时也消耗了部分硬件性能。对用户的体验上,和Android系统环境上都有不利的影响。而且在其它地方,大多数开发者都认为在后台永驻进程,是获取用户的隐私,是不合法的。然而在我国也许是因为开发商的需求大,迫切想要达到自己的目标,使用永驻的进程可以完成用户行为分析和推送等其它后台的业务。因此在开发的模式上采取了极端的个体主义思想。 Android 5.0系统以后,Google为了优化Android系统,提高使用流畅度以及延长电池续航,加入了在应用后台/锁屏时,系统会回收应用,同时自动销毁应用拉起的Service的机制。同时为了满足在特定条件下需要执行某些任务的需求,google在全新一代操作系统上,采取了Job (jobservice & JobInfo)的方式,即每个需要后台的业务处理为一个job,通过系统管理job,来提高资源的利用率,从而提高性能,节省电源。这样又能满足APP开发商的要求,又能满足系统性能的要求。Jobscheduler由此应运而生。
适用场景:
需要在Android设备满足某种场合才需要去执行处理数据:
一:应用具有可以推迟的非面向用户的工作(定期数据库数据更新)
二:应用具有当插入设备时希望优先执行的工作(充电时才希望执行的工作备份数据)
三:需要访问网络或 Wi-Fi 连接时需要进行的任务(如向服务器拉取内置数据)
四:希望作为一个批次定期运行的许多任务(s)
衍生应用:
一:使用JobScheduler进行开机自启动。
二:Android服务保活-JobScheduler拉活。
三:Android进程保活的一般套路。
知识点十二:Binder机制
描述:Binder基于Client-Server通信模式,传输过程只需一次拷贝,为发送端添加UID/PID身份,既支持实名Binder也支持匿名Binder,安全性高,简单高效,再加上其面向对象的设计思想,独特的接收缓存管理和线程池管理方式,成为Android进程间通信的中流砥柱。
Binder机制的优点:
优点详解:
性能的角度:数据拷贝次数:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存。
稳定性的角度:Binder是基于C/S架构的,简单解释下C/S架构,是指客户端(Client)和服务端(Server)组成的架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;从这稳定性角度看,Binder架构优越于共享内存。
安全的角度:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份,传统IPC只能由用户在数据包里填入UID/PID,这个标记完全是在用户空间控制的,没有放在内核空间,因此有被恶意篡改的可能;而Android作为一个开放的开源体系,拥有非常多的开发平台,App来源甚广,因此手机的安全显得额外重要;对于普通用户,绝不希望从App商店下载偷窥隐射数据、后台造成手机耗电等等问题,传统Linux IPC无任何保护措施,完全由上层协议来确保。
Binder机制涉及到的四个角色:
Binder机制通信的过程描述:
Binder机制通信的过程图解:
知识点十三:Android权限机制
背景:默认情况下没有任何应用有权限去执行对其他应用、操作系统、用户有不利影响的操作。这是一个核心的设计理念。记住这句话对后面的权限管理可以很好的理解。 正是由于这样的设计理念,默认情况下应用不能去读写用户的私有数据(比如Email和Contacts),不能去读写其他App的文件,不能执行网络访问,不能保持设备始终唤醒等等。
Security Architecture(安全体系结构):
因为每一个Android 应用都是在一个进程沙盒中运行的,应用必须明确分享的资源和数据,通过申明需要的额外权限这种方式(这些额外权限不由基本沙盒提供)。应用静态的声明这些权限(在Manifest里面),然后Android系统会请求用户同意这些权限。
Android应用沙盒不依赖于创建应用的技术,特别的是Dalvik虚拟机并不是安全边界的,所有的应用都可以运行native code(比如参见NDK),所有类型的应用-Java,native,hybird,都是以同样的方式封装在沙盒内并且相互之间是同样的安全等级。
Application signing(应用签名):
所有的Apk文件都必须由他的开发者使用私有的签名证书签名,这个证书是开发者身份的唯一标识,这个证书是由开发者自己生成的开放式的证书,用于自己签名应用。证书的目的是标识应用的身份,这样可以让系统知道是该允许还是拒绝应用访问签名级别的权限(signature-level permissions),以及允许还是拒绝应用所请求的给予相同Linux身份来作为不同的应用。
User IDs and File Access(用户ID和文件访问):
在安装一个app package的时候,android系统会给每一个package一个独立的Linux user ID。这个User ID在这个应用在当前设备的生命周期内都是固定不变的,在不同的设备,相同的package的用户ID可能各不相同,但可以确定的是在一台设备上一个package的用户ID是固定不变的。
因为安全执行是发生在进程层面的,两个不同的package不能运行在相同的进程中,他们会被作为不同的Linux用户来运行。
但是你可以在manifest中使用sharedUserId属性来指定不同的package有相同的User ID,这样这两个不同的package将会被视为相同的APP,会有相同的User ID和文件权限。
当然为了保证安全,只有两个APP签名一致且申明了相同的sharedUserId才会被给予相同的User ID。
七:Android操作系统的演进
先来看看Android系统的发展过程,从2008年发布Android 1.0系统,直到2019年即将发布Android 10.0系统,下面列举些重要的时间节点。
2003年10月,Andy Rubin团队创办Android公司。
2005年8月,谷歌收购Android公司,Andy Rubin担任谷歌工程部副总裁继续负责Android项目。
2008年9月,谷歌正式发布Android 1.0系统。
2011年1月,Android系统设备的用户总数达到了1.35亿,成为智能手机领域占有量第一的系统。
2011年8月,Android手机占据全球智能机市场48%份额,并在亚太地区市场占据统治地位,终结了Symbian系统的霸主地位,跃居全球第一。 2012年1月,谷歌Android Market已有10万开发者,推出超过40万应用。
2013年11月,Android 4.4正式发布,系统更智能、UI更现代。
2013年到2018年,这个阶段安卓进入飞速发展期,被升级的有摄像头、内存、机身、芯片等,原来的3.5寸小屏已退出历史舞台,全面屏、刘海屏、水滴屏已成为当下主流屏幕方案。
一:从Android 1.0发展到Android 4.0,系统各项功能和特性迭代到一个较完善的阶段;
二:Android 4.1系统,Google开展了黄油计划(Project Butter),为了让Android系统摆脱UI交互上的严重滞后感,希望能像“黄油”一样顺滑。 核心原理是系统框架中的渲染和动画统一采用垂直同步技术(VSYNC),以及三重缓冲技术(Triple Buffer),让滑动、翻页等操作更加一致与顺滑。
三:Android 4.4系统,Google开展了瘦身计划(Project Svelte),力求降低安卓系统的内存使用,解决低端机型升级难的问题,让Android 4.4可正常运行在所有Android手机,从而减少安卓系统继续碎片化。UI设计上,支持新的“沉浸式模式”,用户界面由过去的黑色与蓝色为主的色调转向带有透明度的浅色系,视觉语言变得更加明亮与现代化。
四:Android 5.0系统,Google开展了伏特计划(Project Volta),力求提升续航能力,这方面Google落后于业界厂商,厂商直面用户对续航尤为迫切,往往系统资源管控更为严格。另外,系统采用全新的ART,抛弃Dalvik虚拟机,大幅提升运行效率。UI设计上,使用全新的扁平化Material Design设计风格,更加清新与质感的设计,统一Android设备的外观和使用体验。
五:Android 6.0系统,Google引入新的运行时权限,让用户能够更好地了解和控制权限;引入了Doze模式,进一步提升电池续航能力。UI设计上,新增夜间模式,大幅改进通知栏,让通知更简洁。
六:Android 7.0系统,引入新的JIT编译器,对AOT编译器的补充,可节省存储空间和加快更新速度;进一步优化Doze唤醒机制;UI设计上,支持分屏功能;
七:Android 8.0系统,Google开展了计划(Project Treble),重新架构Android,将安卓系统框架与Vendor层解耦,力求彻底解决安卓碎片化这一老大难的问题,这是安卓系统架构最大的变化。系统层面加强对后台服务、广播、位置的管控限制。UI设计上,改进通知栏,智能文本选择和自动填充功能。
八:Android 9.0系统,引入神经网络API,采用机器学习的思路来预测用户使用习惯来做省电优化,继续强化Treble计划;文件系统(sdcardf/F2FS)持续提升;私有API的限制进一步规范化Android生态,强化隐私和安全,硬件安全性模块以及统一生物识别身份验证界面。 UI设计上,新的手势导航,加强支持刘海屏,UI搜索界面使用到机器学习,AI正在逐步强化Android系统。
九:Android 10.0系统,Google开展了主线计划(Project Mainline),相关模块(Modules)不允许厂商直接修改,只能由Google应用商店来更新升级,强化用户隐私、系统安全与兼容性。支持脸部生物识别。
系统演进趋势:
每个Android大版本的更新迭代前行,历经10余年,在用户体验、流畅性、续航、安全、隐私、机器学习等方面都取得较大的改进。图中是每个大版本中最具代表性的特征标记在图中,并不代表着该版本全部特征,同样专项计划也不是只在某一个版本执行,比如续航和性能优化,每一个版本都在持续改进中,Treble计划也一直在迭代至今。
八:扩展阅读
1、https://blog.csdn.net/qq_35114086/article/details/52614740(Android系统架构的详细解析——很全面)
2、https://www.cnblogs.com/hejing-swust/articles/7821968.html(Android系统和linux内核的关系详解)
3、https://blog.csdn.net/itachi85/article/details/54695046/(Android系统架构与系统源码目录)
4、https://www.jiangshuaijie.com/androidyuan-ma/(Android系统源码目录解析)
5、https://blog.csdn.net/yippeelyl/article/details/77076085(Android的5个进程等级)
6、https://blog.csdn.net/ccjhdopc/article/details/52893738(Android 应用程序启动过程分析)
7、https://blog.csdn.net/lpjishu/article/details/50781415(Android系统服务详解-android学习之旅)
8、https://www.jianshu.com/p/38859f881888(Android:JNI 与 NDK到底是什么?)