草庐IT

Android应用启动速度优化

JakeWharton 2023-09-13 原文
应用启动流程总结:

①点击启动一个App,Launcher进程采用Binder IPC向ActivityManagerService发起startActivity请求;
②ActivityManagerService接收到请求后,向zygote进程发送创建进程的请求;
③Zygote进程fork出新的子进程,即App进程;
④App进程通过Binder IPC向sytem_server进程发起绑定Application请求;
⑤system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;
⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
⑦主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法。

从这里的应用启动过程来看,我们要对应用启动速度进行优化,能做的就是在application和launcherActivity的启动进行优化,尽量减少耗时操作。

应用启动的类型:冷启动、温启动、热启动概念
冷启动

冷启动指的是该应用程序在此之前没有被创建,发生在应用程序首次启动或者自上次被终止后的再次启动。简单的说就是app进程还没有,需要创建app的进程启动app。

比如开机后,点击屏幕的app图标启动应用。

冷启动的过程主要分为两步:

1)系统任务。加载并启动应用程序;显示应用程序的空白启动窗口;创建APP进程
2)APP进程任务。启动主线程;创建Activity;加载布局;屏幕布局;绘制屏幕
其实这不就是APP的启动流程嘛?所以冷启动是会完整走完一个启动流程的,从系统到进程。

温启动

温启动指的是App进程存在,但Activity可能因为内存不足被回收,这时候启动App不需要重新创建进程,只需要执行APP进程中的一些任务,比如创建Activity。

比如返回主页后,又继续使用其他的APP,时间久了或者打开的应用多了,之前应用的Activity有可能被回收了,但是进程还在。
所以温启动相当于执行了冷启动的第二过程,也就是APP进程任务,需要重新启动线程,Activity等。

热启动

热启动就是App进程存在,并且Activity对象仍然存在内存中没有被回收。
比如app被切到后台,再次启动app的过程。

所以热启动的开销最少,这个过程只会把Activity从后台展示到前台,无需初始化,布局绘制等工作。

那我们所要做的启动速度优化呢,就是对于冷启动这种情况进行的。

启动过程需要经过哪些生命周期方法呢?
-> Application 构造函数

-> Application.attachBaseContext()

-> Application.onCreate()

-> Activity 构造函数

-> Activity.setTheme()

-> Activity.onCreate()

-> Activity.onStart

-> Activity.onResume

-> Activity.onAttachedToWindow

-> Activity.onWindowFocusChanged

启动开始标识为Application.attachBaseContext(),启动结束的最后标识是LauncherActivity的onWindowFocusChanged方法调用完成。

1、Application的onCreate流程,对于大型的APP来说,通常会在这里做大量的通用组件的初始化操作;
建议:很多第三方SDK都放在Application初始化,我们可以放到用到的地方才进行初始化操作。
2、Activity的onCreate流程,特别是UI的布局与渲染操作,如果布局过于复杂很可能导致严重的启动性能问题;

App进程的创建等环节我们是无法主动控制的,可以优化的也就是Application、Activity创建以及回调等过程。

解决方案1 :添加一个默认的启动页的Theme能从现象解决这个问题,但是并没有从根本上解决启动慢的问题。

解决方案2:
Application中主要做了各种三方组件的初始化,参数设置,启动各种服务等;
对于过多的初始化任务,我们考虑以下优化方案:

1.考虑异步初始化三方组件,不阻塞主线程;
2.延迟部分三方组件的初始化;

启动耗时统计:

如果是本地调试的话,统计启动时间还是很简单的,通过命令行方式即可:

adb shell am start -W packagename/activity

例如:

adb shell am start -W com.xxx.xxxx/com.xxxx.login.activity.LauncherActivity

得到冷启动时间:


WaitTime
返回从 startActivity 到应用第一帧完全显示这段时间. 就是总的耗时,包括前一个应用 Activity pause 的时间和新应用启动的时间;
ThisTime
表示一连串启动 Activity 的最后一个 Activity 的启动耗时;
TotalTime
表示新应用启动的耗时,包括新进程的启动和 Activity 的启动;

一般来说,只需查看得到的TotalTime,即应用的启动时间,其包括 创建进程 + Application初始化 + Activity初始化到界面显示 的过程。

特点:

1、线下使用方便,不能带到线上。
2、非严谨、精确时间。

CPU profiler使用

使用CPU profiler来解决哪些性能问题

我们可以使用CPU profiler来分析CPU耗时问题,解决由于CPU资源消耗而产生的性能问题。
1.我们可以使用CPU profiler来查看主线程中每个方法的耗时情况,以及每个方法的调用栈,可以很方便的分析卡顿产生的原因,以及定位到具体的代码方法。
2.我们可以使用它来进行启动优化分析,查看APP启动时的耗时问题。
3.我们可以使用它来查看线程的执行情况,定位线程的CPU资源占用等问题。

开启使用CPU profiler追踪启动过程:

1.事件时间轴:显示应用中的 Activity 在其生命周期内不断转换而经历各种不同状态的过程
2.CPU 时间轴:显示应用的实时 CPU 使用率(以占总可用 CPU 时间的百分比表示)以及应用当前使用的线程总数。
3.线程活动时间轴:列出属于应用进程的每个线程,并使用下面列出的颜色在时间轴上指示它们的活动。

1、Trace Java Methods
会记录每个方法的时间、CPU信息。对运行时性能影响较大。
2、Sample Java Methods
相比于Trace Java Methods会记录每个方法的时间、CPU信息,它会在应用的Java代码执行期间频繁捕获应用的调用堆栈,对运行时性能的影响比较小,能够记录更大的数据区域。

跟踪 Java 方法:在运行时检测应用,以在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这些时间戳,以生成方法跟踪数据,包括时间信息和 CPU 使用率。

开启对程序追踪模式,启动程序,得到追踪的结果:


使用“Call Chart”标签检查跟踪数据
Call Chart 标签提供方法跟踪数据或函数跟踪数据的图形表示形式,其中调用的时间段和时间在水平轴上表示,而其被调用方显示在垂直轴上。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调用显示为蓝色。

这里得到追踪的启动过程各个方法调用的时长,针对耗时较长的方法进行分析和优化,解决耗时问题。

CPU Profile各查看方法比较:

找到这些布局加载耗时,或者第三放库初始化,或者我们自己的方法比较耗时,主要的处理思路就是合理的使用异步初始化、延迟初始化、懒加载机制。

使用启动器来异步加载初始化任务:

启动器流程:
(1)代码Task化,启动逻辑抽象为Task
(2)根据所有任务依赖关系排序生成一个有向无环图
(3)多线程按照排序后的优先级依次执行

比如创建一个ARouterTask:

class ARouterTask(val application: BaseApplication) : Task("") {

    override fun run() {
        if (BuildConfig.DEBUG) {
            ARouter.openLog()
            ARouter.openDebug()
        }
        ARouter.init(application)
    }

}

将task添加进project的task列表中:

    fun  createCommonTaskGroup() : Task {
        var arouterTask = ARouterTask(this)
        var refreshTask = RefreshTask()
        var builder = Project.Builder()
        builder.add(arouterTask)
        builder.add(refreshTask)
        return builder.create()
    }

或者用after给task带有顺序:

 Project.Builder builder = new Project.Builder();
        builder.add(a);
        builder.add(b).after(a);
        builder.add(c).after(a);
        builder.add(d).after(b, c);
        builder.add(e).after(a);
        Project group = builder.create();

最后,manager添加project并start各个异步任务。

        AlphaManager.getInstance(applicationContext).addProject(createCommonTaskGroup())
        AlphaManager.getInstance(applicationContext).start()

参考:
https://blog.csdn.net/u011578734/article/details/109496667
https://juejin.cn/post/6844904093786308622#heading-132
Android启动优化--异步优化
阿里异步启动框架
https://mp.weixin.qq.com/s/dwhBnvMe-ePa2HSreEWsUw

有关Android应用启动速度优化的更多相关文章

  1. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  2. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  3. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  4. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  5. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

  6. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  7. ruby-on-rails - 如何在 Gem 中获取 Rails 应用程序的根目录 - 2

    是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在

  8. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  9. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  10. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

随机推荐