【Android】页面启动耗时统计流程梳理

文章基于Android 11

写在前面:
最近的文章都会放流程图,时序图之类的图片,解释下为什么这么做:
图片的好处:

  • 流程清晰,一目了然
  • 很多代码,如同老太太的裹脚布,又臭又长。影响理解,特别当想和别人解释清除一件事时,大量的代码,会搞得大家没耐心。

所以,如果只是浅尝辄止,只要看图即可得到你想要的结论。如果想深入了解,则跟着图片,对照源码梳理一遍流程。因此这篇文章,如果你有自己阅读源码的方式,理解完第一张图,后续的也就不用看了。后面的文章是给想学源码,却不知道怎么学的人,也是我看源码的个人习惯。

正文开始

本文重点

  • 弄清楚Android是如何统计页面启动耗时的
  • 这个时间是如何算出来的

查看Android的日志可以发现这样一条日志

2024-09-25 10:50:20.944 1292-1350 ActivityTaskManager system_process I Displayed xxx包名/.MainActivity: +966ms

那么我们要统计计时,其实只要弄清楚这个日志的时间怎么计算出来的。跟踪源码,全局搜索Displayed发现

ActivityMetricsLogger#logAppDisplayed

    private void logAppDisplayed(TransitionInfoSnapshot info) {
        if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) {
            return;
        }

        EventLog.writeEvent(WM_ACTIVITY_LAUNCH_TIME,
                info.userId, info.activityRecordIdHashCode, info.launchedActivityShortComponentName,
                info.windowsDrawnDelayMs);

        StringBuilder sb = mStringBuilder;
        sb.setLength(0);
        sb.append("Displayed ");
        sb.append(info.launchedActivityShortComponentName);
        sb.append(": ");
        TimeUtils.formatDuration(info.windowsDrawnDelayMs, sb);
        Log.i(TAG, sb.toString());
    }

可以看到重点就是这个时间info.windowsDrawnDelayMs,那么这个数据是怎么来的呢?直接上图(如果看不清楚,下载下来看):

在这里插入图片描述

下面是代码分析:整个过程属实无聊。是我自己看源码的一个方法,如果你有自己看源码的方式,后续不看也罢。
as右键find Usages(后续所有的只有一个来源都是这么得到的结论)
TransitionInfoSnapshot#windowsDrawnDelayMs数据的来源只有一个

		private TransitionInfoSnapshot(TransitionInfo info, ActivityRecord launchedActivity,int windowsFullyDrawnDelayMs) {
			//......
			windowsDrawnDelayMs = info.mWindowsDrawnDelayMs;
			//......
        }

ActivityMetricsLogger#mWindowsDrawnDelayMs

		/** Elapsed time from when we launch an activity to when its windows are drawn. */
		//直译:从启动 Activity 到绘制其窗口所经过的时间。
        int mWindowsDrawnDelayMs;

查看这个值的写入只有一个地方

ActivityMetricsLogger#notifyWindowsDrawn

    /**
     * Notifies the tracker that all windows of the app have been drawn.
     *
     * @return Non-null info if the activity was pending to draw, otherwise it might have been set
     *         to invisible (removed from active transition) or it was already drawn.
     */
	TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r, long timestampNs) {
        if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn " + r);

        final TransitionInfo info = getActiveTransitionInfo(r);
        if (info == null || info.allDrawn()) {
            if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn no activity to be drawn");
            return null;
        }
        //这里进行赋值
        // Always calculate the delay because the caller may need to know the individual drawn time.
        info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
        info.removePendingDrawActivity(r);
        final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
        if (info.mLoggedTransitionStarting && info.allDrawn()) {
            done(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
        }
        return infoSnapshot;
    }

TransitionInfo#calculateDelay

int calculateDelay(long timestampNs) {
    // Shouldn't take more than 25 days to launch an app, so int is fine here.
    //传入的时间 - 开始过渡的时间
    return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs - mTransitionStartTimeNs);
}

接下来分两部分

  • 开始过渡的时间什么时候赋值
  • 传入的时间是什么时间

开始过渡的时间写入只有一个来源

        private TransitionInfo(ActivityRecord r, LaunchingState launchingState, int transitionType,
                boolean processRunning, boolean processSwitch) {
            mLaunchingState = launchingState;
            //此处就是赋值的地方
            mTransitionStartTimeNs = launchingState.mCurrentTransitionStartTimeNs;
            //......
        }

查看launchingState.mCurrentTransitionStartTimeNs的赋值

TransitionInfoSnapshot#notifyActivityLaunching

    private LaunchingState notifyActivityLaunching(Intent intent, @Nullable ActivityRecord caller,
            int callingUid) {
        final long transitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
        //......

        if (existingInfo == null) {
            // Only notify the observer for a new launching event.
            launchObserverNotifyIntentStarted(intent, transitionStartTimeNs);
            final LaunchingState launchingState = new LaunchingState();
            launchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
            return launchingState;
        }
        existingInfo.mLaunchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
        return existingInfo.mLaunchingState;
    }

可以看到是在notifyActivityLaunching方法被调用时的SystemClock.elapsedRealtimeNanos()。继续跟踪这个方法被调用的时机

ActivityStarter#startResolvedActivity

ActivityStartController#doPendingActivityLaunches

ActivityStart#executeRequest

到这里熟悉Activity启动流程的基本也就知道开始过渡时间的来源了。不熟悉的我放一张图,大家看下Activity的启动流程,应该也能明白这个时间大概是什么时候。(为了方便看清楚,只截图了一部分,完整图很大,截图放不下。可私聊我获取)

在这里插入图片描述

接下来查看结束时间,即TransitionInfo#calculateDelay传入的时间

再贴一次代码

    TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r, long timestampNs) {
        //......
        // Always calculate the delay because the caller may need to know the individual drawn time.
        info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
       //......
    }

ActivityRecord#onWindowsDrawn

    /** Called when the windows associated app window container are drawn. */
    void onWindowsDrawn(boolean drawn, long timestampNs) {
       //......
        final TransitionInfoSnapshot info = mStackSupervisor
                .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
        //......
    }

ActivityRecord#updateReportedVisibilityLocked

    void updateReportedVisibilityLocked() {
        if (nowDrawn != reportedDrawn) {
            onWindowsDrawn(nowDrawn, SystemClock.elapsedRealtimeNanos());
            reportedDrawn = nowDrawn;
        }
    }

至此,结束时间赋值来源分析结束

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/883038.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台

一、介绍 果蔬识别系统,本系统使用Python作为主要开发语言,通过收集了12种常见的水果和蔬菜(‘土豆’, ‘圣女果’, ‘大白菜’, ‘大葱’, ‘梨’, ‘胡萝卜’, ‘芒果’, ‘苹果’, ‘西红柿’, ‘韭菜’, ‘香蕉’, ‘黄瓜’)…

基于SpringBoot+Vue的校园快递代取管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏:Java精选实战项目…

CNN网络训练WISDM数据集:模型仿真及可视化分析

卷积神经网络(CNN)因其强大的特征提取能力和深度学习架构而备受推崇,CNN在处理图像数据时展现出的卓越性能,使其成为解决各种视觉识别任务的首选工具。WISDM数据集是一个广泛用于运动估计研究的基准数据集,它包含了多个…

14年408-计算机网络

第一题: 解析:OSI体系结构 OSI由下至上依次是:物理层-网络链路层-网络层-运输层-会话层-表示层-应用层。 因此直接为会话层提供服务的是运输层。答案选C 第二题: 解析:数据链路层-交换机的自学习和帧转发 主机a1向交换…

关于养育孩子的一点想法

我们许多人总是很看重结果,不重视过程,在工作中有时候确实会这样,但这种想法会经常蔓延到生活中,比如养育孩子,我们总有一个目标,希望他成才,实现某种理想,弥补你人生中的某种缺憾&a…

开通微信视频号直播的流程

首先我们要了解什么是视频号? 视频号其实就是腾讯家的“抖音”/“快手”,可以发布视频和直播(包括直播带货)。 微信视频号不同于订阅号、服务号,它是一个全新的内容记录与创作平台,也是一个了解他人、了解…

AIGC专栏15——CogVideoX-Fun详解 支持图文生视频 拓展CogVideoX到256~1024任意分辨率生成

AIGC专栏15——CogVideoX-Fun详解 支持图&文生视频 拓展CogVideoX到256~1024任意分辨率生成 学习前言项目特点生成效果相关地址汇总源码下载地址 CogVideoX-Fun详解技术储备Diffusion Transformer (DiT)Stable Diffusion 3EasyAnimate-I2V 算法细节算法组成InPa…

计算机出现msvcp140.dll丢失的6种解决方法,亲测有效

在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是“msvcp140.dll丢失”。这个错误通常会导致某些应用程序无法正常运行,给用户带来困扰。本文将总结6种解决msvcp140.dll丢失的方法,帮助大家轻松解决这个问题。 一&…

随机梯度下降的学习

梯度下降(Gradient-Descent) 在机器学习的旅途中,不可避免需要与它打交道,那么该如何初步理解它的用途呢? 好的,想象你在一个山谷中,想要找到最低点(山谷的底部)。你现…

如何使用 Bittly 为基于 HTTP 的 API 快速创建 UI 操作界面

在开发 Web 应用或服务时,通常会提供不同数量的 API 接口给客户端或其他第三方使用, 当 API 数量达到一定数量的时候,在处理接口间的调用链以及参数关系时就会变得异常麻烦。 在这种情况下便可通过 Bittly 的面板功能将这些 API 结构进行组装…

电子元件制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

5G智能工厂与物联数字孪生平台的融合应用,不仅为电容器制造业注入了新的活力,更为整个制造业的数字化转型树立了新的标杆。电子元件制造过程中,数字孪生平台通过实时监测生产线的各个环节,实现了生产流程的可视化监控。管理人员可…

es的封装

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、类和接口介绍0.封装思想1.es的操作分类 二、创建索引1.成员变量2.构造函数2.添加字段3.发送请求4.创建索引总体代码 三.插入数据四.删除数据五.查询数据 前…

大数据新视界 --大数据大厂之探索ES:大数据时代的高效搜索引擎实战攻略

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

java 获取集合a比集合b多出来的对象元素

public class OrderListEntity {/*** deprecated 对象集合的处理* param aData 集合a* param bData 集合b* return 返回集合a比集合b多出来的部分, 通过id判断*/public static List<OrderListEntity> AHasMoreThanBData(List<OrderListEntity> aData, List<Ord…

AI日常绘画【国庆海报】:盛世迎华诞,Flux国庆节海报制作教程

大家好我是安琪&#xff01;&#xff01;&#xff01; 马上就要到祖国母亲的节日了&#xff0c;想想心里都美滋滋的&#xff0c;终于可以放松一下了。相信AI绘画关于国庆主题肯定也会精彩纷呈吧&#xff0c;今天和大家分享几组关于国庆海报的制作教程。 本文使用基于Flux的相关…

西安交大曹相湧、孟德宇教授团队最新成果┆HSIGene: 一个用于高光谱图像生成的基础模型(含详细视频解读)

目录 论文简介 1. 团队介绍 2. 研究背景及主要贡献 3. 方法 4. 实验及结果 5. 总结与展望 6. 论文介绍视频 参考文献 论文简介 本推文详细介绍了一篇西安交通大学孟德宇教授与曹相湧副教授团队最新论文《HSIGene: A Foundation Model For Hyperspectral Image Gener…

微信小程序报名表怎么弄_轻松打造高效便捷的互动新体验

在当今数字化时代&#xff0c;便捷与高效成为了我们日常生活中不可或缺的一部分。无论是企业活动、社团招新&#xff0c;还是日常办公统计&#xff0c;一个优秀的报名工具都能极大地提升我们的工作效率和用户体验。今天&#xff0c;就让我们一起探索如何通过微信小程序报名表&a…

Growthly Quest 增长工具:助力 Web3 项目实现数据驱动的增长

作者&#xff1a;Stella L (stellafootprint.network) 在瞬息万变的 Web3 领域&#xff0c;众多项目在用户吸引、参与和留存方面遭遇重重难关。Footprint Analytics 推出 Growthly&#xff0c;作为应对这些挑战的全方位解决方案&#xff0c;其中创新性的 Quest&#xff08;任务…

如何确定SAP 某些凭证或者单号的号码编码范围的 OBJECT 是什么?

在SAP的运维或者项目实施中&#xff0c;有时会如何确定SAP 某些凭证或者单号的号码 OBJECT 是什么&#xff1f; 一般一下常用的可以通过事务代码 例如&#xff1a; XDN1 Create Number Ranges for Customer Accounts&#xff0c;定义客户编码FBN1查看维护会计凭证号范围 我…

Java开发:文件上传和下载

一、文件上传 使用MultipartFile 类型接收参数&#xff1b; 调用上传有两种方式&#xff1a; 方式一&#xff1a;使用curl命令 curl -F "file/data/filename.txt" http://localhost:8080/upload --verbose方式二&#xff1a;使用html&#xff0c;写一个form表单&…