当前位置: 首页 > news >正文

Android性能优化之【启动优化】

我们都知道,现在的App已经由增量阶段转化为存量阶段。所以做好一款App是比做出一款App更重要的事情,这也是我写性能优化这个专栏的初衷。我先大概讲一下性能优化我准备讲那几块,大家都知道性能优化的方向有很多,不可能都去讲解。删繁就简就显得尤为重要,大家不管是看源码还是学技术,学到核心才是最关键的,这也是我最近一段时间才逐渐领悟到的。

本次性能优化专题准备分为4块:

  • 启动优化
  • 内存优化
  • 崩溃优化
  • 卡顿优化

文章更新的顺序也会按这个顺序来,我个人感觉性能优化掌握这几块通用的基本差不太多,如果后面有从事性能优化专项工作的同学可以在这个基础上深入。话不多说,我们开始启动优化的学习旅程吧。

学习启动优化前,我们应该先弄清楚手机从开机到App启动到底做了哪些事。如果不清楚Framework层的同学我建议可以先去看一本书《Android进阶揭秘》,然后再去下载一个版本的Android源码配合着看看,可能一开始看会比较痛苦,坚持一段时间后我相信你会大有收获。下面的内容默认同学们有这方面基础了。

这个流程我们再细分为3个方面:

  • 从开机到显示应用列表
  • 从点击应用图标到Activity创建成功
  • 从Activity创建成功到画面显示

从开机到显示应用列表

我们先来看一张流程图:

 

开机加电后,CPU先执行预设代码 、加载ROM中的引导程序 Bootloader 和Linux内核到RAM内存中去,然后初始化各种软硬件环境、加载驱动程序、挂载根文件系统,执行 init进程 

init进程会启动各种系统本地服务,如 SM (ServiceManager)、MS(Media Server)、bootanim(开机动画)等,然后init进程会在解析init.rc文件后fork()出 Zygote进程 

Zygote会启动Java虚拟机,通过jni进入Zygote的java代码中,并创建 socket 实现IPC进程通讯,然后启动 SS (SystemServer)进程。

SS进程负责启动和管理整个Framework,包括 AMS (ActivityManagerService)、 WMS (WindowManagerService)、PMS(PowerManagerService)等服务、同时启动binder线程池,当SS进程将系统服务启动就绪以后,就会通知AMS启动Home。

AMS通过Intent隐式启动的方式启动 Launcher ,Launcher根据已安装应用解析对应的xml、通过findBiewById()获得一个RecycleView、加载应用图标、最后成功展示App列表。

解释

  • 预设代码 :cpu制造厂商会预设一个地址,这个地址是各厂家约定统一的,Android手机会将固态存储设备ROM预先映射到该地址上; 

  • Bootloader :类似BIOS,在系统加载前,用以初始化硬件设备,建立内存空间的映像图,为最终调用系统内核准备好环境; 

  • init进程 :init进程时Android系统中用户进程的鼻祖进程,主要作用是启动系统本地服务、fork出Zygote进程; 

  • SM :ServiceManager是一个守护进程,它维护着系统服务和客户端的binder通信; 

  • Zygote进程 :Zygote进程是所有Java进程的父进程,我们的APP都是由Zygote进程fork出来的; 

  • socket :一种独立于协议用于两个应用程序之间的数据传输的网络编程接口,是IPC中的一种;(但是在Android中一般使用Binder来实现IPC,这里使用socket的原因后面有写到) 

  • SS :Framework两大重要进程之一(另一个是Zygote),载着framework的核心服务,系统里面重要的服务都是SS开启的; 

  • AMS :服务端对象,负责系统中所有Activity的生命周期,打开App、Activity的开启、暂停、关闭都需要AMS来控制; 

  • WMS :窗口管理服务,窗口的启动、添加、删除、大小、层级都是由WMS管理;(下面会解释什么是窗口) 

  • Launcher :Launcher就是系统桌面,主要用来启动应用桌面,同时管理快捷方式和其他组件,本质上也是一个应用程序,和我们的App一样,也是继承自Activity,有自己的AndroidManifest;(所以才可以被AMS用Intent启动) 

Question 1:Zygote进程为什么使用Socket而不是Binder? fork不允许存在多线程,而Binder通讯恰巧就是多线程;

Question 2:什么是窗口? Android系统中的窗体是屏幕上的一块用于绘制各种UI元素并能够响应应用户输入的一个矩形区域,从原理上来讲,窗体的概念是独自占有一个Surface实例的显示区域,比如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗体。

简而言之:开机时,芯片会将手机磁盘中预定位置的引导代码加载到内存中,启动init进程,init进程会通过解析init.rc启动zygote进程,zygote进程fork出systemserver进程,systemserver进程里面又启动了AMS,WMS,PMS等服务。Launcher本质上是一个Activity,由AMS启动管理。

从点击应用图标到Activity创建成功

我们来看一张流程图:

 

//然后点击应用图标后,先检查要打卡的Activity是否存在

--> Launcher.startActivitySafely()--> Launcher.startActivity()--> Activity.startActivity()--> Activity.startActivityForResult()//然后获取AMS的代理AMP--> Instrumentation.execStartActivity()--> ActivityManagerNative.getDefault().startActivity()--> ActivityManagerProxy.startActivity()--> ActivityManagerService.startActivity()--> startActivityAsUser(intent, requestCode, userId)--> ActivityStackSupervisor.startActivityMayWait()--> ActivityStackSupervisor.resolveActivity()--> ActivityStackSupervisor.startActivityLocked()--> new ActivityRecord对象,获取ActivityStack--> 找到ActivityStack后Launcher.onPause()//准备启动进程--> ActivityManagerService.startProcessLocked()//通过socket通知Zygote创建进程--> zygoteSendArgsAndGetResult()//创建ActivityThread--> ActivityThread.main()//告诉AMS我已经创建好了--> ActivityThread.attach()--> ActivityManagerProxy.attachApplication()--> ActivityMangerService.attachApplication()//找到Application实例并初始化--> ActivityMangerService.attachApplicationLocked()--> ApplicationThread.bindApplication()//创建Application--> AcitvityThread.bindApplication()--> Application.oncreate()//启动Activity--> ActivityStackSupervisor.attachApplicationLocked()--> ActivityStackSupervisor.realStartActivityLocked()--> ActivityThread.scheduleLaunchActivity()//进入UI线程--> handleLaunchActivity()--> performLaunchActivity()//创建Activity实例--> Instrumentation.newActivity()--> Activity.onCreate()

解释

  • ActivityThread :App的真正入口。当开启App之后,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。与ActivityManagerServices配合,一起完成Activity的管理工作; 

  • ApplicationThread :用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯; 

  • Instrumentation :可以理解为应用进程的管家,每个应用程序只有一个,每个Activity内都有该对象的引用,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作; 

  • ActivityStack :Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程; 

  • ActivityRecord :ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映像; 

Question 1:如何判断APP是否已经启动? AMS会保存一个ProcessRecord信息,有两部分构成,“uid + process”,每个应用工程序都有自己的uid,而process就是AndroidManifest.xml中Application的process属性,默认为package名。

每次在新建新进程前的时候会先判断这个 ProcessRecord 是否已存在,如果已经存在就不会新建进程了,这就属于应用内打开 Activity 的过程了。

从Activity创建成功到画面显示

onCreate()方法中先执行setContentView()方法将对应的xml文件传入,之后会去调用window.setContentView(),最终会在这里创建 Decorview 并填充标题栏、状态栏,然后获取 contentParent ,然后调用LayoutInflater.inflate解析xml文件获取根root(ViewRootImpl),通过root.addView()将contentParent添加到 ViewRootImpl 中去,至此onCreate()结束。

开始onResume()阶段,在开始会向H类发送一个消息,然后在ActivityThread中获取之前创建的Decorview并调用windowManager.add(),最后在windowManager中将窗口和窗口的参数传到root.setView(),然后ViewRoot通过Binder调用WMS,使WMS所在的SS进程接收到按键事件时,可以回调到该root,同时ViewRoot会向自己的handler发送一条消息,然后进行处理(performTraversals),之后开始绘制过程(在Surface的canvas上绘制)。

先利用MeasureSpec完成onmeasure(),然后在onlayout()中确定各元素的坐标,ondraw()负责将view画到canvas上,再通过Surface进行跨进程最终调用Native层的 SGL 、openGI,最后再去调用硬件CPU进行渲染操作,最终界面显示在你眼前

解释

  • DecorView :界面的根View,PhoneWindow的内部类 

  • contentParent :所有View的根View,在DecorView里面 

  • ViewRootImpl :ViewRoot是GUI管理系统与GUI呈现系统之间的桥梁, WindowManager 通过 ViewRootImpl 与 DecorView 起联系。并且, View 的绘制流程都是由 ViewRootImpl 发起的

  • SGL :底层的2D图形渲染引擎 

未完待续!

相关文章:

  • Java 集合与数据结构 · 接口 interfaces ·Collection 常用方法 · Map 常用方法
  • 面试面不过?大厂面试官是这样说的···
  • 秒懂YUV444/YUV422/YUV420计算(二十九)
  • 模方重大更新,支持3ds max、新版大疆数据、匀色、多原点数据等
  • 论文教程之 138位科研工作者分享如何(认真地)阅读一篇科学论文
  • MVC、MVP、MVVM三种模式的介绍及区别
  • 注意力机制综述学习记录
  • 数据结构c语言版第二版(严蔚敏)第三章笔记
  • 羊了个羊,但是Python简(li)单(pu)版
  • 【软件测试】测试用例的设计方法
  • 二叉树的遍历问题
  • FPGA/HDL 人员开发利器-TerosHDL(开源 IDE)
  • 《谁动了我的奶酪》阅读笔记
  • 2023秋招--腾讯天美--游戏客户端--一面面经
  • 跨模态学习能力再升级,EasyNLP电商文图检索效果刷新SOTA
  • 【刷算法】从上往下打印二叉树
  • AngularJS指令开发(1)——参数详解
  • Create React App 使用
  • Docker: 容器互访的三种方式
  • HTTP 简介
  • idea + plantuml 画流程图
  • js
  • JS+CSS实现数字滚动
  • mysql_config not found
  • nodejs调试方法
  • spring-boot List转Page
  • SpringCloud集成分布式事务LCN (一)
  • Vue 重置组件到初始状态
  • 程序员该如何有效的找工作?
  • 马上搞懂 GeoJSON
  • 模型微调
  • 人脸识别最新开发经验demo
  • 设计模式(12)迭代器模式(讲解+应用)
  • 实战|智能家居行业移动应用性能分析
  • 异常机制详解
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • 阿里云API、SDK和CLI应用实践方案
  • 带你开发类似Pokemon Go的AR游戏
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • "无招胜有招"nbsp;史上最全的互…
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • #在 README.md 中生成项目目录结构
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (ibm)Java 语言的 XPath API
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)Windows2003安全设置/维护
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .NET Compact Framework 3.5 支持 WCF 的子集