Android性能优化

news2024/11/17 8:25:43

Android性能优化

一、卡顿优化

前言:说到卡顿我们可能正常能想到是FPS刷新率,这是一个平均值,FPS高并不代表页面流畅,比如一个页面某一贞耗时了160毫秒,但是其他都是16毫秒,那么这个页面通过FPS的数据来看体现不出来卡顿,但是实际是用户明显的感觉到了卡顿的感觉。
那么我们可以通过卡顿的帧数跟总帧数的占比来判定页面卡顿情况

卡顿率 = 卡顿的帧数 / 总帧数

加入屏幕数心率是60/s,那么每帧耗时16ms,如果有的帧数超过了16毫秒就发生了掉帧,也就是卡顿。比如某一贞耗时160毫秒,那么我们就认为掉了9帧,根据掉帧的数量可以分级为下面的情况
在这里插入图片描述
1、获取各帧耗时一般有以下两种方案

  1. 通过设置自定义android.util.Printer,监听Looper的dispatchMessage耗时
  2. 通过向Choreographer循环注册FrameCallback,统计两次Vsync事件时间间隔
  3. 谷歌提供的AndroidX系列的组件JankStats 在Android7以上实现
class JankLoggingActivity : AppCompatActivity() {

    private lateinit var jankStats: JankStats

    private val jankFrameListener = JankStats.OnFrameListener { frameData ->
            // 在实际使用中可以将日志上传到远端统计
            Log.v("JankStatsSample", frameData.toString())
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
            // 初始化 JankStats,传入 window 和卡顿回调
            jankStats = JankStats.createAndTrack(window, jankFrameListener).apply {
            // 支持设置卡顿阈值,默认为2
            this.jankHeuristicMultiplier = 3f
        }

        // 设置页面状态
        val metricsStateHolder = PerformanceMetricsState.getHolderForHierarchy(binding.root)
        metricsStateHolder.state?.putState("Activity", javaClass.simpleName)
        // ...
    }

    override fun onResume() {
     super.onResume()
     // onResume后重新开始统计
     jankStats.isTrackingEnabled = true
    }

    override fun onPause() {
     super.onPause()
     // onPause后停止统计
     jankStats.isTrackingEnabled = false
    }

这里我们可以先将卡顿数据存储在内存或者本地存储中,当卡顿数量达到一定程度或者页面切换时,再统一上传卡顿数据,减少上传次数,如下所示:

internal class JankActivityLifecycleCallback : ActivityLifecycleCallbacks {
    private val jankAggregatorMap = hashMapOf<String, JankStatsAggregator>()
    // 聚合回调
    private val jankReportListener = JankStatsAggregator.OnJankReportListener { reason, totalFrames, jankFrameData ->
            jankFrameData.forEach { frameData ->
             // 获取当前 Activity name
             Log.v("Activity",frameData.states.firstOrNull { it.key == "Activity" }?.value ?: "")
             // 获取掉帧数
                val dropFrameCount = frameData.frameDurationUiNanos / singleFrameNanosDuration
                if (dropFrameCount <= JankMonitor.SLIGHT_JANK_MULTIPIER) {
                    slightJankCount++
                } else if (dropFrameCount <= JankMonitor.MIDDLE_JANK_MULTIPIER) {
                    middleJankCount++
                } else if (dropFrameCount <= JankMonitor.CRITICAL_JANK_MULTIPIER) {
                    criticalJankCount++
                } else {
                    frozenJankCount++
                }
            }
            // 实际使用中可以上传到远端统计
            Log.v("JankMonitor","*** Jank Report ($reason), " +
                        "totalFrames = $totalFrames, " +  // 总帧数
                        "jankFrames = ${jankFrameData.size}, " + // 总卡顿数
                        "slightJankCount = $slightJankCount, " + // 轻微卡顿数
                        "middleJankCount = $middleJankCount, " + // 中等卡顿数
                        "criticalJankCount = $criticalJankCount, " + // 严重卡顿数
                        "frozenJankCount = $frozenJankCount" // 冻结帧数
            )
        }
    }

    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        // 为所有 Activity 添加卡顿监听
        activity.window.callback = object : WindowCallbackWrapper(activity.window.callback) {
            override fun onContentChanged() {
                val activityName = activity.javaClass.simpleName
                if (!jankAggregatorMap.containsKey(activityName)) {
                    val jankAggregator = JankStatsAggregator(activity.window, jankReportListener)
                    PerformanceMetricsState.getHolderForHierarchy(activity.window.decorView).state?.putState("Activity", activityName)
                    jankAggregatorMap[activityName] = jankAggregator
                }
            }
        }
    }

    // ...
}

如上所示,主要做了以下事:
为所有 Activity 添加了聚合的卡顿监听,当卡顿数达到阈值或者 Activity 退到后台时会触发聚合回调。
在合回调中可以获取这段时间的总帖数,与卡顿的帧的列表,通过计算卡顿帧的掉帧数,我们可以获取总卡顿数,轻微卡顿数,严重卡顿数等。将这些数据上传就可以计算出页面的卡顿率。
在回调中我们同样可以获取页面的状态,比如我们这里设置的activityName,通过设置状态我们可以统计不同场景下的卡顿率,比如滚动与非滚动。
2.如何定位卡顿问题

  1. 堆栈抓取方案:思路其实很简单,在卡顿发生时 Dump 主线程堆栈,通过分析堆栈找到卡顿的原因
  2. 字节码插桩方案:堆栈抓取方案的最大缺陷是无法获取方法的执行耗时,而字节码插桩方式可以完美解决这一问题。Matrix 插桩对于好机器的性能影响可忽略,对差机器性能稍有损耗,但影响很小。对安装包大小影响,对于微信这种大体量的应用,实际插桩函数 16w+,对安装包增加了 800K 左右。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java去重的终极指南:性能对比与高效实现

文章目录 前言一、使用Set接口下面是对几种Set实现类的简单介绍及代码示例&#xff1a;1.HashSet&#xff1a;2.LinkedHashSet&#xff1a;3.TreeSet&#xff1a; 二、使用Stream API三、其他方式1.使用Collectors.toSet()方法&#xff1a;配合Stream API的collect()方法&#…

vue3+cesium项目搭建

前言 最近需要在一个Vue3的项目中使用到cesium&#xff0c;对于一个cesium没有太多了解的人来说&#xff0c;还是比较麻烦的&#xff0c;本篇博文就将自己在这个过程踩的坑记录下来&#xff0c;有需要的可以看一下 1、vuecesium框架搭建 2、项目运行起来后&#xff0c;球体不…

展会邀请|虹科诚邀您参加7月11-13日上海慕尼黑光博会

2023年上海慕尼黑光博会与机器视觉展将于7月11-13日在上海国家会展中心隆重召开&#xff01; 慕尼黑上海光博会自2006年举办以来&#xff0c;已成为中国激光、光学、光电行业一年一度的聚会。慕尼黑上海光博会助力行业发展趋势&#xff0c; 集中展示涵盖激光器与光电子、光学与…

科技云报道:当云厂商主动拥抱生成式AI,会碰撞出什么样的火花?

科技云报道原创。 如果说这是AI大模型的时代&#xff0c;不如说是生成式AI的时代。 在AI大模型、生成式AI、ChatGPT这三者中&#xff0c;生成式AI是最广泛的概念&#xff0c;涵盖了所有使用AI生成新内容的应用。 大模型是实现生成式AI的一种方式&#xff0c;而ChatGPT则是大…

精选了20个Python实战项目(附源码),拿走就用!零基础练手不二项目!

Python是目前最好的编程语言之一。由于其可读性和对初学者的友好性&#xff0c;已被广泛使用。 那么要想学会并掌握Python&#xff0c;可以实战的练习项目是必不可少的。 接下来&#xff0c;我将给大家介绍20个非常实用的Python项目&#xff0c;帮助大家更好的学习Python。 …

电表是怎么计算度数的

电表是一种用来测量电能的仪表&#xff0c;也称为电度表、火表、电能表、千瓦小时表等。电表可以通过测量电流、电压、功率等因素来计算用户消耗的电能&#xff0c;从而确定用户应缴纳的电费。在本文中&#xff0c;我们将详细介绍电表的计算方式以及如何读取电表的度数。 一、电…

自学网络安全(黑客)

一、为什么选择网络安全&#xff1f; 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地&#xff0c;网络安全行业地位、薪资随之水涨船高。 未来3-5年&#xff0c;是安全行业的黄金发展期&#xff0c;提前踏入…

Android之WebView加载PDF链接预览PDF文件

文章目录 前言一、效果图二、实现步骤1.在项目main目录下新建一个assets2.新建一个js为index.js3.新建一个HTML为index.html4.xml布局4.Activity类&#xff08;kotlin&#xff09;5.Activity类&#xff08;Java&#xff09; 总结 前言 Android的webview压根就不支持加载pdf&am…

深度卷积网络的实际应用

1、三种经典的深度卷积网络 1.1、LeNet-5 使用 sigmoid 函数和 tanh 函数&#xff0c;而不是ReLu 函数&#xff0c;这篇论文中使用的正是 sigmoid 函数和 tanh 函数LeNet-5 是针对灰度图片训练的&#xff0c;所以图片的大小只有 32321 6 个 55 的过滤器&#xff0c;步幅为 …

【如何在深度学习的道路上越走越远?】

作为近几年人工智能领域的主要研究方向之一&#xff0c;深度学习主要通过构建深度卷积神经网络和采用大量样本数据作为输入&#xff0c;最终得到-一个具有强大分析能力和识别能力的模型。深度学习可以是有监督的、半监督的或无监督的。深度学习架构(例如深度神经网络、深度信念…

el-input输入框type=“number“时,禁止鼠标上下滑动改变数值

el-input输入框type"number"时&#xff0c;禁止鼠标上下滑动改变数值 解决方法&#xff1a;在el-input中添加属性设置 mousewheel.native.prevent

【达哥讲网络——只讲你不知道的】第1集:网络体系结构中的功能模块

大家好&#xff0c;经过公司缜密的思考和策划&#xff0c;【达哥讲网络——只讲你不知道的】系列连载今天正式与大家见面了。经过深入考虑&#xff0c;本系列只对一些重要的网络技术原理、网络功能实现原理及配置进行连载&#xff0c;其中会穿插一些实战案例&#xff0c;以帮助…

python与蒸散发与植被总初级生产力估算

植被总初级生产力(GPP)是指植物通过光合作用吸收的碳&#xff0c;是陆地生物圈和大气之间最大的碳通量&#xff0c;GPP的准确量化对于理解气候变化中生态系统功能、农业生产和碳循环的动态以及对气候的反馈具有重要意义 蒸散发&#xff08;Evapotranspiration&#xff0c;ET&a…

websdk上传阿里云视频完整教程

批量上传视频到阿里云 这段时间项目里有一个上传视频到阿里云的功能是我来负责写的&#xff0c;之前一直没有写过这种功能&#xff0c;感觉很难的亚子&#xff0c;但是后来仔细研究了一遍发现也没想象中那么难&#xff0c;最后经过不懈的努力也算是搞出来了哈哈哈&#xff0c;开…

集合List和Map

ArrayList底层的实现原理 初始化后ArrayList添加元素的步骤 首先计算数组的容量&#xff0c;如果当前数组已使用长度1后的大于当前的数组长度&#xff0c;则调用grow方法扩容(原来的1.5倍)&#xff0c;确保新增的数据有地方存储之后&#xff0c;则添加元素到size的位置上。返回…

docker环境下安装mysql 5.6

一、查看mgsql镜像版本 docker search mysql 二、拉取mysql镜像到本地标签为5.6版本 docker pull mysql:5.6 三、使用mysql5.6镜像创建容器(也叫运行镜像) 1.执行命令&#xff1a; docker run -p 3306:3306 --name mysql -v /haolb/mysql/conf:/etc/mysql/conf.d -v /haolb/my…

P2P、BT、ED2k、FTP、磁力链接下载到底是什么鬼?

1、HTTP/HTTPS 下载 有小伙伴会问&#xff0c;这个协议不是用来浏览网页的时候用的吗&#xff1f; 其实不然&#xff0c;用来下载文件一样可以&#xff0c;本质上都是从服务器拉取资源到本地&#xff0c;不同的是网页内容被渲染到浏览器上&#xff0c;而文件直接放在你的下载…

财富航向:企业为何急需财务管理软件?

随着市场的竞争日益激烈&#xff0c;企业对于财务数据的管理越来越重视。财务管理软件存在的好处越来越明显&#xff0c;它们可以帮助企业更好地管理财务信息并提高工作效率。 企业为什么需要财务管理软件&#xff1f; 1、方便管理财务数据 财务管理软件能够方便地管理与公司财…

教程学习:AutoQSAR

教程和练习文件从软件官网下载 内容&#xff1a; 1、拷贝教程提供的练习文件素材&#xff1a; 在软件的help中选择需要的教程&#xff0c;点击Copy to&#xff0c;可以将教程需要的文件拷贝到指定的文件夹里。点击Browse可以进行预览。 2、建立一个数值型的QSAR模型评估结合…

msvcr120.dll找不到是什么原因,怎样修复

msvcr120.dll的定义 msvcr120.dll是微软Visual C Redistributable软件包中的一个动态链接库文件。它是Microsoft Visual 所需的一个重要组件。这个文件主要用于支持和管理C语言编写的应用程序的运行。它包含了许多C的运行库函数和类&#xff0c;以便应用程序能够正常运行和调用…