Android重要流程总结


一. Android系统启动流程是什么?(提示:BootLoader -> Linux内核 init进程 -> Zygote进程 –> SystemServer进程 –> 各种系统服务 –> 应用进程)

Android系统启动的核心流程如下:

  1. 启动电源以及系统启动:当电源按下时引导芯片从预定义的地方(固化在ROM)开始执行,加载引导程序BootLoader到RAM,然后执行,开始拉起Linux OS。
    引导程序BootLoader:BootLoader是在Android系统开始运行前的一个小程序,主要用于把系统OS拉起来并运行。
  2. Linux内核启动:当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。当其完成系统设置时,会先在系统文件中寻找init.rc文件,并启动init进程。
    总结一下,init进程启动后做了哪几件事:
    (1)创建和挂载启动所需要的文件和目录
    (2)初始化和启动属性服务。
    (3)解析init.rc配置文件,并且启动了Zygote进程
  3. init进程启动:初始化和启动属性服务,并且启动Zygote进程。
    最后再总结一下Zygote进程启动中做了几件事:
    1.创建AndroidRuntime并调用其start方法,启动Zygote进程。
    2.创建Java虚拟机并为Java虚拟机注册JNI方法。
    3.通过JNI调用ZygoteInit的main函数进入Zygote的java框架层。
    4.通过registerZygoteSocket方法创建服务端Socket,并通过runSelectLoop方法等待AMS的请求来创建新的应用程序进程。
    5.启动SystemServer。
  4. Zygote进程启动:创建JVM并为其注册JNI方法,创建服务器端Socket,启动SystemServer进程。
    最后总结一下SystemServer进程:
    1.启动Binder线程池
    2.创建了SystemServiceManager(用于对系统服务进行创建、启动和生命周期管理)
    3.启动了各种服务
  5. SystemServer进程启动:启动Binder线程池和SystemServiceManager,并且启动各种系统服务。
  6. Launcher启动:被SystemServer进程启动的AMS会启动Launcher,Launcher启动后会将已安装应用的快捷图标显示到系统桌面上。

Android系统启动流程之init进程启动

Android系统启动流程之Zygote进程启动

Android系统启动流程之SystemServer进程启动

Android系统启动流程之Launcher进程启动


二. APP打包流程

image.png
.apk文件

.apk文件其实就是一个压缩文件,把文件的后缀改成.zip就可以用windows解压软件解压了,解压后里面的文件如下:

image

上图里面都是什么文件:

  • lib 文件夹里面存放的是so动态链接库,so动态链接库是不需要在做apk打包时一系列压缩处理的。

  • META-INF 签名文件夹,里面存放三个文件,有两个是对资源文件做的SHA1 hash处理,一个是签名和公钥证书。

  • res 资源文件夹,里面还会分animator,anim,color,drawable,layout,menu和raw这几个文件夹。

  • AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件。它位于整个项目的根目录,描述了package中暴露的组件(activities, services, 等等),他们各自的实现类,各种能被处理的数据和启动位置。 除了能声明程序中的Activities, ContentProviders, Services, 和Intent Receivers,还能指定permissions和instrumentation(安全控制和测试)。这个文件是很重要的,里面有我们的Android四大组件和申请的权限。

  • classes.dex Android平台上的可执行文件,Android虚拟机Dalvik支持的字节码文件格式Google在新发布的Android平台上使用了自己的Dalvik虚拟机来定义, 这种虚拟机执行的并非Java字节码, 而是另一种字节码: dex格式的字节码。在编译Java代码之后,通过Android平台上的工具可以将Java字节码转换成Dex字节码。虽然Google称Dalvik是为了移动设备定做的,但是业界很多人认为这是为了规避向sun申请Javalicense。这个DalvikVM针对手机程式/CPU做过最佳化,可以同时执行许多VM而不会占用太多Resource。classes.dex也是由java的class文件重新编排而来,我们也可以通过反编译工具把dex文件转换成class文件。如果做了拆包那么会有classes1.dex,classes2.dex ...多个classes.dex文件。

  • resources.arsc 这个文件记录了所有的应用程序资源目录的信息,包括每一个资源名称、类型、值、ID以及所配置的维度信息。我们可以将这个resources.arsc文件想象成是一个资源索引表,这个资源索引表在给定资源ID和设备配置信息的情况下,能够在应用程序的资源目录中快速地找到最匹配的资源。

打包工具

image.png

apk打包的7个步骤

1、aapt打包资源文件,生成R.java文件:

使用aapt来打包res资源文件,生成R.java、resources.arsc和res文件,R.java文件是所有res资源的id列表,R.java是我们在编写代码的时候会用到的,我们经常R.drawable.icon之类的来引用工程中的资源文件。

image

resources.arsc也是清单文件,但是resources.arsc跟R.java区别是非常大的,R.java里面只是id值列表,而且里面的id值不重复。但是我们drawable-hdpi、drawable-xdpi或者drawable-xxdpi这些不同分辨率的文件夹存放的图片的名称和id是一样的,在运行的时候是怎么根据设备的分辨率来选择对应分辨率的图片的呢?这就要靠我们的resources.arsc文件了,resources.arsc里面会对所有的资源id进行组装,在apk运行时获取资源的时候会根据设备的情况获得不同的资源。

resources.arsc文件的作用就是通过一样的ID,根据不同的配置索引到最佳的资源显示在UI中。

R.java是我们在写代码时候引用res资源的id表,resources.arsc是程序在运行时候的用到的资源表。R.java是程序员读的,resources.arsc是机器读的!
appt在打包资源文件之前会检测AndroidManifest.xml的合法性,对res目录下的资源子目录进行处理,处理的内容包括资源文件名的合法性,这就是有时候为什么我们的资源命名有问题的时候Android Studio会直接报错无法编译!另外xml文件内容都会被编译成 二进制的,这也是我们无法直接打开apk解压后其中的xml文件。

2、处理aidl文件,生成相应的java文件:

AIDL (Android Interface Definition Language), Android接口定义语言,Android提供的IPC (Inter Process Communication,进程间通信)的一种独特实现。这个阶段处理.aidl文件,生成对应的Java接口文件。

3、编译工程源代码,生成相应的class文件:

通过Java Compiler编译R.java、Java接口文件、Java源文件,生成.class文件。如果有配置混淆的话,会编译成混淆的class文件,方便源代码被偷看。

4、转换所有的class文件,生成class.dex文件:

Android系统的Dalvik虚拟机的可执行文件为DEX格式,程序运行所需的class.dex就是在这一步生成的,使用到的工具为dx,它位于android-sdk\platform-tools目录下,dx工具主要的工作是将java字节码转换为Dalvik字节码、压缩常量池、消除冗余信息等。

5、打包生成apk文件:

将classes.dex、resources.arsc、res文件夹(res/raw资源被原装不动地打包进APK之外,其它的资源都会被编译或者处理)、Other Resources(assets文件夹)、AndroidManifest.xml打包成apk文件。

友情提示:
res/raw和assets的相同点:两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
res/raw和assets的不同点:

  1. res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
  2. res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹
6、对apk文件进行签名:

对apk进行签名,可以进行Debug和Release 签名。Debug签名是Android Studio默认的,Release 签名是需要我们自己配置的。

7、对签名后的apk文件进行对对齐处理:

release mode 下使用 aipalign进行align,即对签名后的apk进行对齐处理。
Zipalign是一个android平台上整理APK文件的工具,它对apk中未压缩的数据进行4字节对齐,对齐后就可以使用mmap函数读取文件,可以像读取内存一样对普通文件进行操作。如果没有4字节对齐,就必须显式的读取,这样比较缓慢并且会耗费额外的内存。

在 Android SDK 中包含一个名为 “zipalign” 的工具,它能够对打包后的 app 进行优化。 其位于 SDK 的 build-tools 目录下, 例如: D:\Develop\Android\sdk\build-tools\23.0.2\zipalign.exe


三. APK安装流程

Apk安装的四种方式:
1.系统应用安装:没有安装界面,在开机时自动完成。
2.网络下载应用安装: 没有安装界面,在应用市场完成。
3.ADB命令安装: 没有安装界面,通过命令直接安装。
4.外部设备安装: 有安装界面,通过SD卡等外部设备安装,由packageInstaller处理安装逻辑。

APK安装涉及到的几个常用目录:
1.system/app : 系统自带的应用程序,获得root权限才能删除。
2.data/app : 用户程序安装目录,安装时会把apk文件复制到此目录下。
3.data/data : 存放应用程序的数据。
4.data/dalvik-cache : 将apk中的dex文件安装到该目录下(dex文件是dalvik虚拟机的可执行文件,大小约为原始apk的四分之一)。

APK安装的预备知识点:
(1)PackageManagerService是由SystemServer启动,PMS负责应用的安装、卸载、权限检查等工作;
(2)在/system/app和/data/app目录下的apk文件,PMS在启动过程中,都会扫描安装;
(3)每次开机时,PMS都会在构造函数中对指定目录下的apk进行扫描,没有安装的apk就会触发安装;

apk安装步骤

1、复制APK到/data/app目录下,解压并扫描安装包。
2、资源管理器解析APK里的资源文件。
3、解析AndroidManifest文件,将apk的权限、应用包名、apk的安装位置、版本、userID等重要信息保存在/data/system/packages.xml文件中。并在/data/data/目录下创建对应的应用数据目录。
4、然后对dex文件进行优化,并保存在data/dalvik-cache目录下。
5、将AndroidManifest文件解析出的四大组件信息注册到PackageManagerService中。
6、安装完成后,发送广播。


四. App(Activity)启动流程

加粗非斜体为启动新Activity流程

  1. 启动者Activity的 startActivity() -> startActivityForResult()。
  2. ActivityThread中 mInstrumentation 调用 execStartActivity(),将启动Activity的请求以Binder的方式发送给AMS。
  3. 如果是新启动APP,立即显示一个空白的window。
  4. AMS接收到启动请求后,交付ActivityStarter处理Intent和Flag等信息,然后再交给ActivityStackSupervisior/ActivityStack 处理Activity进栈相关流程。
  5. AMS以Socket方式请求Zygote进程fork出一个新的APP进程。
  6. 在新进程里创建ActivityThread对象,也就是应用的主线程,在主线程里开启Looper消息循环,开始处理创建Activity。
  7. 如果未创建,创建Application对象。(在ActivityThread的 main() 的 attach() 里)
 public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        ......
        Process.setArgV0("<pre-initialized>");
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        Looper.loop();
    }
  1. AMS处理完毕后回调ActivityThread中的ApplicationThread(Binder)的scheduleLaunchActivity(),此方法只是通过 H(Handler)发送一个消息给ActivityThread。
  2. ActivityThread -> handleMessage() -> performLaunchActivity(),其中调用 mInstrumentation 的 newActivity(),里面通过ClassLoader用反射加载出Activity的实例。
  3. 回调Activity的 onCreate() 方法。
  4. 加载并绘制View。(onResume()后)
重要术语

1.ActivityManagerServices,简称AMS,服务端对象,负责系统中所有Activity的生命周期。
2.ActivityThread,App的真正入口。当开启App之后,调用main()开始运行,开启消息循环队列,这就是传说的UI线程或者叫主线程。与ActivityManagerService一起完成Activity的管理工作。
3.ApplicationThread,用来实现ActivityManagerServie与ActivityThread之间的交互。在ActivityManagerSevice需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通信。
4.ApplicationThreadProxy,是ApplicationThread在服务器端的代理,负责和客户端的ApplicationThread通信。AMS就是通过该代理与ActivityThread进行通信的。
5.Instrumentation,每一个应用程序只有一个Instrumetation对象,每个Activity内都有一个对该对象的引用,Instrumentation可以理解为应用进程的管家,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作。
6.ActivityStack,Activity在AMS的栈管理,用来记录经启动的Activity的先后关系,状态信息等。通过ActivtyStack决定是否需要启动新的进程。
7.ActivityRecord,ActivityStack的管理对象,每个Acivity在AMS对应一个ActivityRecord,来记录Activity状态以及其他的管理信息。其实就是服务器端的Activit对象的映像。
8.TaskRecord,AMS抽象出来的一个“任务”的概念,是记录ActivityRecord的栈,一个“Task”包含若干个ActivityRecord。AMS用TaskRecord确保Activity启动和退出的顺序。如果你清楚Activity的4种launchMode,那么对这概念应该不陌生。

Activity启动图示

点击应用图标后会去启动应用的Launcher Activity,如果Launcer Activity所在的进程没有创建,还会创建新进程,整体的流程就是一个Activity的启动流程。


Activity启动流程

五. View加载和绘制流程

1. Activity的 attach() 中创建 PhoneWindow(Window的具体实现)。其中的 DecorView 为所有View的root view,是个包含title和content的FrameLayout。
2. setContentView() 中将布局赋给 DecorView 的 content (android.R.id.content),但此时并没有真正将view显示在window上。
3. 真正显示是在ActivityThread的 handleResumeActivity() 中,performResumeActivity()(也就是onResume())之后。
4. handleResumeActivity()中,从PhoneWindow取出decorView,通过WindowManager 的 addView() 将其加入Window上显示。
5. WindowManager 可简单当作是WMS在应用端的远程代理对象。

实际上它是个抽象类,具体实现是 WindowManagerImpl,而 WindowManagerImpl 又是委托给 WindowManagerGlobal(单例)来远程与WMS交互。

6. WindowManagerGlobal功能有:
  1. 获取WMS的远程代理对象sWindowManagerService
  2. 获取WMS的IWindowSession对象
  3. 创建ViewRootImpl对象
  4. 通过ViewRootImpl对象来addView(), removeView(), updateViewLayout()

所以最终是由 ViewRootImpl 来完成的。

ViewRootImpl 的 setView()

ViewRootImpl.setView() -> requestLayout() -> scheduleTraversals() -> doTraversal() -> performTraversals() -> performMeasure()、performLayout()、performDraw()