论一个app从启动到主页面显示经历的过程?(代码片段)

author author     2022-12-24     499

关键词:

前言 (个人观点.不喜勿喷)

本部分内容是关于Android进阶的一些知识总结,涉及到的知识点比较杂,不过都 是面试中几乎常问的知识点,也是加分的点。 关于这部分内容,可能需要有一些具体的项目实践。在面试的过程中,结合具体自 身实践经历,才能更加深入透彻的描绘出来。

技术图片
顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找
https://github.com/xiangjiana/Android-MS

一、流程概述

技术图片

启动流程:

点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起 startActivity请求;
system_server进程接收到请求后,向zygote进程发送创建进程的请求;
Zygote进程fork出新的子进程,即App进程;
App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向 App进程发送scheduleLaunchActivity请求;
App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线 程发送LAUNCH_ACTIVITY消息;
主线程在收到Message后,通过发射机制创建目标Activity,并回调 Activity.onCreate()等方法。
到此,App便正式启动,开始进入Activity生命周期,执行完 onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。

上面的一些列步骤简单介绍了一个APP启动到主页面显示的过程,可能这些流程中 的一些术语看的有些懵,什么是Launcher,什么是zygote,什么是 applicationThread..... 下面我们一一介绍。

二、理论基础

1.zygote

zygote意为“受精卵“。Android是基于Linux系统的,而在Linux中,所有的进程都是 由init进程直接或者是间接fork出来的,zygote进程也不例外。

在Android系统里面,zygote是一个进程的名字。Android是基于Linux System的, 当你的手机开机的时候,Linux的内核加载完成之后就会启动一个叫“init“的进程。在 Linux System里面,所有的进程都是由init进程fork出来的,我们的zygote进程也不 例外

我们都知道,每一个App其实都是

  • 一个单独的dalvik虚拟机
  • 一个单独的进程

所以当系统里面的第一个zygote进程运行之后,在这之后再开启App,就相当于开 启一个新的进程。而为了实现资源共用和更快的启动速度,Android系统开启新进 程的方式,是通过fork第一个zygote进程实现的。所以说,除了第一个zygote进 程,其他应用所在的进程都是zygote的子进程,这下你明白为什么这个进程叫“受精 卵”了吧?因为就像是一个受精卵一样,它能快速的分裂,并且产生遗传物质一样的 细胞!

2.system_server

SystemServer也是一个进程,而且是由zygote进程fork出来的。

知道了SystemServer的本质,我们对它就不算太陌生了,这个进程是Android Framework里面两大非常重要的进程之一——另外一个进程就是上面的zygote进 程。

为什么说SystemServer非常重要呢?因为系统里面重要的服务都是在这个进程里面 开启的,比如 ActivityManagerServicePackageManagerServiceWindowManagerService等等。

3.ActivityManagerService

ActivityManagerService,简称AMS,服务端对象,负责系统中所有Activity的生命 周期。 ActivityManagerService进行初始化的时机很明确,就是在SystemServer进程开启 的时候,就会初始化ActivityManagerService

下面介绍下Android系统里面的服务器和客户端的概 念。

其实服务器客户端的概念不仅仅存在于Web开发中,在Android的框架设计中,使 用的也是这一种模式。服务器端指的就是所有App共用的系统服务,比如我们这里 提到的ActivityManagerService,和前面提到的PackageManagerServiceWindowManagerService等等,这些基础的系统服务是被所有的App公用的,当某 个App想实现某个操作的时候,要告诉这些系统服务,比如你想打开一个App,那 么我们知道了包名和MainActivity类名之后就可以打开

  Intent intent = new Intent(Intent.ACTION_MAIN); 
  intent.addCategory(Intent.CATEGORY_LAUNCHER); 
  ComponentName cn = new 
  ComponentName(packageName, className); 
  intent.setComponent(cn); startActivity(intent);

但是,我们的App通过调用startActivity()并不能直接打开另外一个App,这个方法会 通过一系列的调用,最后还是告诉AMS说:“我要打开这个App,我知道他的住址和 名字,你帮我打开吧!”所以是AMS来通知zygote进程来fork一个新进程,来开启我 们的目标App的。这就像是浏览器想要打开一个超链接一样,浏览器把网页地址发 送给服务器,然后还是服务器把需要的资源文件发送给客户端的。

知道了Android Framework的客户端服务器架构之后,我们还需要了解一件事情, 那就是我们的App和AMS(SystemServer进程)还有zygote进程分属于三个独立的进 程,他们之间如何通信呢?

App与AMS通过Binder进行IPC通信,AMS(SystemServer进程)与zygote通过 Socket进行IPC通信。后面具体介绍。

那么AMS有什么用呢?在前面我们知道了,如果想打开一个App的话,需要AMS去 通知zygote进程,除此之外,其实所有的Activity的开启、暂停、关闭都需要AMS来控制,所以我们说,AMS负责系统中所有Activity的生命周期。 在Android系统中,任何一个Activity的启动都是由AMS和应用程序进程(主要是 ActivityThread)相互配合来完成的。AMS服务统一调度系统中所有进程的 Activity启动,而每个Activity的启动过程则由其所属的进程具体来完成。

4.Launcher

当我们点击手机桌面上的图标的时候,App就由Launcher开始启动了。但是,你有 没有思考过Launcher到底是一个什么东西? Launcher本质上也是一个应用程序,和我们的App一样,也是继承自Activity packages/apps/Launcher2/src/com/android/launcher2/Launcher.java

  public final class Launcher extends Activity 
          implements View.OnClickListener, OnLongClickListener, La uncherModel.Callbacks, View.OnTouchListener 
   

Launcher实现了点击、长按等回调接口,来接收用户的输入。既然是普通的App, 那么我们的开发经验在这里就仍然适用,比如,我们点击图标的时候,是怎么开启 的应用呢?捕捉图标点击事件,然后startActivity()发送对应的Intent请求呗!是的,Launcher也是这么做的,就是这么easy!

5.Instrumentation和ActivityThread

每个Activity都持有Instrumentation对象的一个引用,但是整个进程只会存在一个 Instrumentation对象。 Instrumentation这个类里面的方法大多数和ApplicationActivity有关,这个类就是完成对ApplicationActivity初始化和生命周期的工具 类。Instrumentation这个类很重要,对Activity生命周期方法的调用根本就离不开 他,他可以说是一个大管家。

ActivityThread,依赖于UI线程。App和AMS是通过Binder传递信息的,那么 ActivityThread就是专门与AMS的外交工作的。

6.ApplicationThread

前面我们已经知道了App的启动以及Activity的显示都需要AMS的控制,那么我们便 需要和服务端的沟通,而这个沟通是双向的。
客户端-->服务端
技术图片

而且由于继承了同样的公共接口类,ActivityManagerProxy提供了与 ActivityManagerService一样的函数原型,使用户感觉不出Server是运行在本地还是 远端,从而可以更加方便的调用这些重要的系统服务。

服务端-->客户端
还是通过Binder通信,不过是换了另外一对,换成了ApplicationThreadApplicationThreadProxy
技术图片
他们也都实现了相同的接口IApplicationThread

  private class ApplicationThread extends ApplicationThreadNative 
  
  public abstract class ApplicationThreadNative extends Binder i mplements IApplicationThread
   
  class ApplicationThreadProxy implements IApplicationThread 
  

好了,前面罗里吧嗦的一大堆,介绍了一堆名词,可能不太清楚,没关系,下面结 合流程图介绍。

三、启动流程

1.创建进程

先从Launcher的startActivity()方法,通过Binder通信,调用 ActivityManagerServicestartActivity方法。
一系列折腾,最后调用startProcessLocked()方法来创建新的进程。
该方法会通过前面讲到的socket通道传递参数给Zygote进程。Zygote孵化自身。 调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid。
调用ActivityThread.main()方法,ActivityThread随后依次调用 Looper.prepareLoop()Looper.loop()来开启消息循环。

方法调用流程图如下:
技术图片
更直白的流程解释:
技术图片

①App发起进程:
当从桌面启动应用,则发起进程便是Launcher所在进程;当从某 App内启动远程进程,则发送进程便是该App所在进程。发起进程先通过binder发送 消息给system_server进程;
②system_server进程:
调用Process.start()方法,通过socket向zygote进程发送创 建新进程的请求;
③zygote进程:
在执行ZygoteInit.main()后便进入runSelectLoop()循环体内,当有 客户端连接时便会执行ZygoteConnection.runOnce()方法,再经过层层调用后fork 出新的应用进程;
④新进程:
执行handleChildProc方法,最后调用ActivityThread.main()方法。

2.绑定Application

上面创建进程后,执行ActivityThread.main()方法,随后调用attach()方法。

将进程和指定的Application绑定起来。这个是通过上节的ActivityThread对象中调用 bindApplication()方法完成的。该方法发送一个BIND_APPLICATION的消息到消息 队列中, 最终通过handleBindApplication()方法处理该消息. 然后调用 makeApplication()方法来加载App的classes到内存中。

方法调用流程图如下:
技术图片
更直白的流程解释:
技术图片

(如果看不懂AMS,ATP等名词,后面有解释)

3.显示Activity界面

经过前两个步骤之后, 系统已经拥有了该application的进程。 后面的调用顺序就是 普通的从一个已经存在的进程中启动一个新进程的activity了。

实际调用方法是realStartActivity(), 它会调用application线程对象中的 scheduleLaunchActivity()发送一个LAUNCH_ACTIVITY消息到消息队列中, 通过 handleLaunchActivity()来处理该消息。在 handleLaunchActivity()通过 performLaunchActiivty()方法回调Activity的onCreate()方法和onStart()方法,然后通 过handleResumeActivity()方法,回调Activity的onResume()方法,最终显示Activity 界面。
技术图片
更直白的流程解释:
技术图片

四、Binder通信

技术图片

简称:
ATP: ApplicationThreadProxy
AT: ApplicationThread
AMP: ActivityManagerProxy
AMS: ActivityManagerService

图解:

system_server进程中调用startProcessLocked方法,该方法最终通过socket方式, 将需要创建新进程的消息告知Zygote进程,并阻塞等待Socket返回新创建进程的pid;
Zygote进程接收到system_server发送过来的消息, 则通过fork的方法,将zygote 自身进程复制生成新的进程,并将ActivityThread相关的资源加载到新进程app process,这个进程可能是用于承载activity等组件;
在新进程app process向servicemanager查询system_server进程中binder服务端 AMS, 获取相对应的Client端,也就是AMP. 有了这一对binder c/s对, 那么app process便可以通过binder向跨进程system_server发送请求,即attachApplication()
system_server进程接收到相应binder操作后,经过多次调用,利用ATP向app process发送binder请求, 即bindApplication. system_server拥有ATP/AMS, 每一个 新创建的进程都会有一个相应的AT/AMP,从而可以跨进程 进行相互通信. 这便是进 程创建过程的完整生态链。

以上大概介绍了一个APP从启动到主页面显示经历的流程,主要从宏观角度介绍了 其过程,具体可结合源码理解。

顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找
https://github.com/xiangjiana/Android-MS

感谢支持~

uniapp从开发app到上架应用市场需要经历什么?(代码片段)

前言:1、如果你想开发一个自己的App或者是第一次开发App,这个可以收藏当做指导手册2、这篇文章主要讲个人开发者开发App到上架应用市场整个过程,很适合新手。作为一个开发者,可能都想过自己做一个app,可... 查看详情

一个c++源文件从文本到可执行文件经历的过程(代码片段)

一个C++源文件从文本到可执行文件经历的过程以HelloWorld为例进行说明首先我们编写一个cpp源程序test.cpp#include<iostream>usingnamespacestd;intmain()cout<<"helloworld"<<endl;return0;使用g++编译命令时g++-otesttest.cppGcc编 查看详情

django-创建一个没有扩展请求处理的主页面(不直接到模板)(代码片段)

...夹中,它看起来像:项目名APP1应用2APP3我想要做的是创建一个路径看起来像这样的网址fromdjango.contribimportadminfromdjango.urlsimportpath,includeurlpatterns=[path('',include(' 查看详情

从浏览器输入网址回车到看到页面过程到底经历了什么?(代码片段)

...从浏览器输入网址到回车看到页面的过程,面试逃不掉的一个问题,我们知道从浏览器输入网址到看到页面主要是涉及DNS解析,TCP三次握手,请求报文,响应报文,TCP4次挥手。#首先我们先来看一下总体的访问过程#总体的访问过... 查看详情

kingadmin后台主页面开发(代码片段)

功能需求能显示所有已注册的app和其内部的model表。页面如下图所示:index.html仅显示内容区域,导航栏忽略。divclass="container-fluid"><divclass="row"><divclass="col-sm-9col-md-8main"><!--遍历每个app,取出每... 查看详情

从setcontentview到onresume应用显示过程分析(代码片段)

...些过程中比较重要的部分重点分析。在上一篇文章分析了一个app从zygote到onCreate的过程(https://blog.csdn.net/qq_36063677/article/details/129756237),onCreate方法中不能少的就是setContentView,这次就分析Activity从setContentView到onRe... 查看详情

输入一个url到浏览器页面展示都经历了哪些过程(代码片段)

→ 1-输入网址→ 2-缓存解析→ 3-域名解析→ 4-tcp连接,三次握手→ 6-页面渲染 一:输入网址那肯定是输入你要访问的网站网址了,俗称url;二:缓存解析浏览器获取了这个url,当然就去解析了,它先去缓... 查看详情

一个页面从输入url到页面加载显示完成,中间都经历了什么

...的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网 查看详情

如何使用颤振中的路线导航到主页面以外的页面?

...布时间】:2019-05-2107:19:33【问题描述】:我创建了一条从一个页面导航到另一个页面的路由,方法如下classtaskextendsStatelessWidget@overrideWidgetbuild(BuildContextcontext)re 查看详情

android从点击应用图标到界面显示的过程

...注册;最后启动了Launcher桌面应用。其实Launcher本身就是一个应用程序,运行在自己的进程中,我们看到的桌面就是Launcher中的一个Activity。应用安装的时候,通过PackageManagerService解析apk的AndroidManifest.xml文件,提取出这个apk的信息... 查看详情

从launcher程序启动app流程分析(代码片段)

...初探”在android手机上,当我们点击桌面上的按钮启动一个应用,就能打开应用的界面。这里我们所说的桌面其实就是android系统启动后的就已经帮我们运行的第一个程序,launcher程序。launcher程序可以理解为作为其它应... 查看详情

uniapp从开发app到上架应用市场需要经历什么?(代码片段)

前言:1、如果你想开发一个自己的App或者是第一次开发App,这个可以收藏当做指导手册2、这篇文章主要讲个人开发者开发App到上架应用市场整个过程,很适合新手。作为一个开发者,可能都想过自己做一个app,可... 查看详情

第一次面试经历(hr面)

...知,看来我已经跳过了第一轮面试。  来到hr请我进了一个小间坐下里填求职书,里面有各种个人信息,有兴趣爱好,有工作经历,以及离开上一任公司原因。  过了大概15分钟,hr进来直接面试我,我之前不知道是什么面,... 查看详情

计算机启动过程(代码片段)

...示,中间经历了哪些过程呢?启动的英文是boot,来自于一个谚语pulloneselfupbyone'sbootstraps通过拉自己的鞋带把自己拽起这个很明显是矛盾的。工程师早期用这句谚语用来比喻早期的计算机开机,因为计算机启动需要运行程序,... 查看详情

从100pv到1亿级pv网站架构演变

一个网站就像一个人,存在一个从小到大的过程。养一个网站和养一个人一样,不同时期需要不同的方法,不同的方法下有共同的原则。本文结合我自已14年网站人的经历记录一些架构演变中的体会。1:积累是必不可少的架构师... 查看详情

toutiao开源项目分析笔记12从总体到局部构建视频主页面

1.构建视频主列表的整体碎片VideoTabLayout1.1.首先创建一个VideoTabLayout packagecom.jasonjan.headnews.module.video;importandroid.os.Bundle;importandroid.support.annotation.Nullable;importandroid.support.design.widget.Tab 查看详情

flutter从启动到显示(代码片段)

...ootWidget 七.RendererBinding 八.scheduleWarmUpFrame总结前言 研究启动过程,目前是理解和加深Flutter框架原理,为后续开发高性能的Flutter应用提供理论思 查看详情

flutter从启动到显示(代码片段)

...ootWidget 七.RendererBinding 八.scheduleWarmUpFrame总结前言 研究启动过程,目前是理解和加深Flutter框架原理,为后续开发高性能的Flutter应用提供理论思 查看详情