页面卡顿检测方案

news2024/11/14 20:37:42

引言

卡顿现象在早期的开发项目中是一个非常值得注意的问题。随着应用功能的不断增加,代码复杂度也在不断提升,特别是在较为低端的机型上,稍有不慎就可能引发卡顿现象。虽然近年来新发布的设备性能显著提升,但卡顿问题仍然不容忽视。即使在高端设备上,复杂的动画、数据处理任务或不合理的资源分配,都会导致用户体验的显著下降。

卡顿不仅影响用户的使用感受,还可能导致用户对应用的不满,进而降低应用的留存率。因此,如何有效地检测、分析和解决卡顿问题,成为了 iOS 开发中一个至关重要的环节。尤其是在开发的早期阶段,通过及时发现和解决卡顿问题,能够有效提升应用的性能表现,并为后续的优化工作打下坚实的基础。

在这篇文章中,我们将深入探讨 iOS 应用中的卡顿检测方法,介绍一些常用的工具和技术手段,帮助开发者在开发过程中及早发现潜在的性能瓶颈,确保应用在各类设备上都能保持流畅的用户体验。

什么是卡顿?

什么是帧率 (Frames Per Second, FPS): 帧率就是每秒中显示的画面帧数,通常来说,iOS设备的屏幕刷新帧率是60Hz,也就是说理想情况下每秒应该现实60帧画面。

帧率与流畅度的关系:如果页面每秒的帧数低于60帧比如20帧或者更低,那么用户就会感到卡顿,帧率越低,卡顿越明显。

iOS应用所有的UI渲染工作都是在主线程上进行的,但是主线程不仅仅负责UI渲染,还需要处理用户交互,动画,网络请求数据处理等任务,如果有些复杂任务阻塞的主线程,那么就会导致帧率下降,进而引发卡顿。

卡顿产生原因

当我们在主线程上进行比较复杂的计算和数据处理时会严重影响帧率。当一次性加载过多或者过大的图片时,尤其是在滑动过程中进行加载容易导致UI卡顿。

在项目开发中比较常见的场景,当一个页面中有多个列表需要加载和显示时。或者当我们使用UIScrollView加载过多的图片时,由于没有复用机制这些图片会被同时渲染从而阻塞主线程。

卡顿检测指标

卡顿检测最显而易见的两个指标一个是帧率,当页面渲染的帧率没有达到流程的水准我们就可以定义为它当前发生了卡顿。

另一个指标是主线程的阻塞时间,主线程被阻塞的时间长度直接影响UI的响应速度,如果主线程阻塞时间过程,用户操作就会感觉到迟钝或者卡顿。

卡顿检测方案

基于上面两个指标我们来实现两个不同的卡顿检测方案。

使用CADisplayLink检测帧率

CADisplayLink的用法和定时器相似,它会以屏幕的刷新频率调用制定的目标方法,所以我们可以通过计算1秒内目标方法的执行次数来计算FPS,当FPS低于我们约定值时可以判断发生卡顿。

具体代码如下:

class DLFrameRateMonitor: NSObject {
    
    /// CADisplayLink
    private var displayLink:CADisplayLink?
    /// 上一次时间
    private var lastTimestamp:CFTimeInterval = 0
    /// 帧数
    private var frameCount:Int = 0
    
    /// 开始检测
    func startMonitor() {
        displayLink = CADisplayLink(target: self, selector: #selector(displayLinkAction))
        displayLink?.add(to: RunLoop.main, forMode: .common)
        lastTimestamp = 0
        frameCount = 0
    }
    
    /// 结束检测
    func stopMonitor() {
        displayLink?.invalidate()
        displayLink = nil
    }
    
    @objc private func displayLinkAction() {
        if lastTimestamp == 0 {
            lastTimestamp = displayLink?.timestamp ?? 0
            return
        }
        frameCount += 1
        let delta = displayLink?.timestamp ?? 0 - lastTimestamp
        if delta < 1 {
            return
        }
        let fps = Double(frameCount) / delta
        print("FPS: \(fps)")
        lastTimestamp = displayLink?.timestamp ?? 0
        frameCount = 0
    }
}

使用主线程阻塞检测卡顿

该方案的核心私信就是通过检测主线程的执行时间来判断是否发生了看现象。

通过创建一个子线程,监测主线程的“RunLoop”状态,创建一个“CFRunLoopObserver”监测主线程“RunLoop”的执行状态,记录它的进入和退出时间,如果这个时间差超过一定的的阈值,那么我们就认为它发生了卡顿。具体代码如下:

注册通知:

    override init() {
        super.init()
        // 注册runloop状态改变通知
       let observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.beforeSources.rawValue | CFRunLoopActivity.afterWaiting.rawValue, true, 0) { (observer, activity) in
            self.runloopActivityChange(activity: activity)
        }
        CFRunLoopAddObserver(CFRunLoopGetMain(), observer, CFRunLoopMode.commonModes)
    }
    /// runloop状态改变
    private func runloopActivityChange(activity:CFRunLoopActivity) {
        self.activity = activity
        semaphore.signal()
    }

开始检测:

    /// 开始检测
    func startMonitor() {
        if isMonitoring {
            return
        }
        isMonitoring = true
        monitorQueue.async {
            while self.isMonitoring {
                let timeout = DispatchTime.now() + .seconds(1)
                let result = self.semaphore.wait(timeout: timeout)
                if result == .timedOut {
                    if self.activity == .beforeSources || self.activity == .afterWaiting {
                        self.timeoutCount += 1
                        if self.timeoutCount >= 3 {
                            print("卡顿了")
                        }
                    }
                }
                else {
                    self.timeoutCount = 0
                }
            }
        }
    }

结束检测:

    /// 结束检测
    func stopMonitor() {
        isMonitoring = false
        semaphore.signal()
    }

结语

在现代应用开发中,用户体验至关重要,而流畅的界面响应是确保良好用户体验的关键之一。主线程卡顿,无论是由于复杂的计算任务、长时间的 I/O 操作,还是不必要的 UI 更新,都会直接影响应用的流畅度和响应速度。因此,及时检测和解决卡顿问题,是提升应用性能和用户满意度的重要步骤。

在本篇文章中,我们探讨了几种主线程卡顿检测方案,包括 CADisplayLink 和基于主线程运行时状态的监测方法。CADisplayLink 作为一种高精度的解决方案,能够实时检测帧率的波动,从而发现卡顿现象。它的优势在于能提供详细的帧渲染信息,帮助我们精准定位性能瓶颈。然而,使用 CADisplayLink 的方法也有其局限性,例如可能对系统性能产生额外的负担。

另一方面,通过结合 RunLoop 状态和 GCD,我们可以实现更灵活的卡顿检测方案。这些方法能够利用系统提供的机制,实时监控主线程的状态和响应时间,帮助我们更好地识别潜在的性能问题。通过信号量和子线程监测,我们可以避免主线程因卡顿问题导致的用户体验下降。

每种检测方案都有其优缺点,选择合适的方法需要根据应用的具体需求和性能目标进行权衡。通过有效地应用这些检测技术,我们可以及早发现和修复卡顿问题,从而优化应用性能,提升用户体验。

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

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

相关文章

kettle定时发送邮件功能怎样集成到系统中?

kettle定时发送邮件的配置步骤&#xff1f;如何设置kettle发信&#xff1f; kettle不仅能够及时通知相关人员数据处理的状态&#xff0c;还能确保系统的自动化和高效运作。AokSend将详细探讨如何将kettle定时发送邮件功能无缝集成到系统中&#xff0c;让您的数据处理流程更加智…

idea2024设置中文

今天下载idea2024.2版本&#xff0c;发现已经装过中文插件&#xff0c;但是还是不显示中文&#xff0c;找了八天原来还需要设置中文选项 方案一 点击文件 -> 关闭项目 点击自定义 -> 选择语言 方案二 点击文件 -> 设置 外观与行为 -> 系统设置 -> 语言和地区…

C语言迷宫制造

目录 开头程序程序的流程图程序的效果我推荐要制造的迷宫下一篇博客要讲的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #include <string.h> void printmaze(const cha…

android关于binder的简单通信过程

文章目录 简述aidl文件服务端的实现客户端的实现验证过程 简述 主要实现的是两个应用之间跨进程通信的过程&#xff0c;client端调用server端的具体实现&#xff0c;然后server端给client回调数据&#xff0c;详细如下所示 aidl文件 以下的文件需要在服务端与客户端都配置一…

使用极狐GitLab进行K3S集群的维护与控制

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门面向中国程序员和企业提供企业级一体化 DevOps 平台&#xff0c;用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规&#xff0c;而且所有的操作都是在一个平台上进行&#xff0c;省事省心省钱。可以一键安装极狐GitL…

企业图纸防泄密怎么做?10款好用的图纸加密软件推荐

在当今竞争激烈的市场环境中&#xff0c;企业图纸作为核心技术和商业机密的重要组成部分&#xff0c;其安全性直接关系到企业的竞争力和市场地位。因此&#xff0c;采取有效措施防止图纸泄密至关重要。本文将探讨企业图纸防泄密的综合策略&#xff0c;并推荐10款优质的图纸加密…

工业企业能源管理系统

进入新世纪以来&#xff0c;我国的社会主义市场经济持续繁荣&#xff0c;在经济发展的同时&#xff0c;能源耗费量与日俱增&#xff0c;在很大程度上阻碍了我国经济的可持续发展。为了实现节能减排的目标&#xff0c;大量企业进行了产业结构调整和优化升级&#xff0c;促进了资…

OpenAI Embeddings API: How to change the embedding output dimension?

题意&#xff1a;OpenAI Embeddings API&#xff1a;如何更改嵌入输出维度&#xff1f; 问题背景&#xff1a; In the official OpenAI node library Create embeddings if for example using the model text-embedding-ada-002 the embeddings returned is an array of aroun…

PHP宿舍外面点单系统---附源码97171

目 录 摘 要 Abstract 1 绪论 1.1 研究背景 1.2国内外研究现状 1.3论文结构与章节安排 2 宿舍外卖点单系统分析 2.1可行性分析 2.1.1 技术可行性分析 2.1.2经济可行性分析 2.1.3操作可行性分析 2.2 功能需求分析 2.2.1普通用户功能 2.2.2商家用户功能 2.2.3管理…

Qt:玩转QPainter序列六

前言 继续看源码。 正文 剩下的大部分都是画各种图形的函数&#xff0c;它们一般都有多个重载版本&#xff0c;我就不一 一介绍使用了&#xff0c;只挑其中的一部分使用一下。 在 QPainter 类中&#xff0c;这些方法涉及到绘图的各种功能&#xff0c;主要用于设置视图变换、…

chapter08-面向对象编程——(Object类详解)——day09

目录 319-运算符 320-查看Jdk源码 321-子类重写equals 322-equals课堂练习1 323-equals重写练习2 324-equals重写练习3 325-hashCode 326-toString 327-finalize 319-运算符 引用的都是同一个地址&#xff0c;所以返回true 320-查看Jdk源码 equals只能判断引用类型是…

线程同步学习

1、线程同步的定义 线程同步不是一起、相同&#xff0c;而是协调、协同的意思。 1)按预定的先后次序进行运行&#xff0c;如:您说完&#xff0c;我再说;线程A生成数据后交给线程B处理; 2)公共资源同一时刻只能被一个线程使用;共享数据在同一时刻只能被一个线程修改&#xff0c…

uniapp 小程序支持打开手机相册和摄像头

开发uniapp 时&#xff0c;有时需要让用户上传手机相册或者拍摄图片&#xff0c;对图片进行处理&#xff0c;下面提供了一个method&#xff0c;支持打开摄像头拍照和相册功能&#xff0c;完成后&#xff0c;对图片做base64处理。 // 打开相册的方法openCamera() {let _thisthi…

给自己复盘的tjxt笔记day9

优惠券管理 开发流程 需求分析&#xff0c;接口统计&#xff0c;数据库设计&#xff0c;创建分支&#xff0c;创建新模块&#xff08;依赖&#xff0c;配置&#xff0c;启动类&#xff09;&#xff0c;生成代码&#xff0c;引入枚举状态 优惠券管理 增删改查的业务代码&#…

vagrant 创建虚拟机

创建一个名为 “Vagrantfile” 的文件&#xff0c;修改如下内容&#xff1a; Vagrant.configure("2") do |config|(1..3).each do |i|config.vm.define "k8s-node#{i}" do |node|# 设置虚拟机的Boxnode.vm.box "centos/7"# 设置虚拟机的主机名…

Behave使用体验

behaveuiautomator2jenkins 同理&#xff0c;Behave也可以和Appium/AirTest框架结合 运行环境 pip install uiautomator2 behave behave2cucumber 意事项&#xff1a;behave版本号建议1.2.5&#xff0c;因为1.2.6和Jenkins Cucumber Report插件不兼容 生成报告 html报告 …

各位,请入局AI大模型,现在!立刻!马上!!

AI 大模型人才供不应求 2024年&#xff0c;AI 在国内市场全面大爆发&#xff0c;不断涌现出新的算法、模型和应用场景&#xff0c;各行各业的垂类大模型应用也迎来井喷期。 无论是华为、 百度、阿里、字节等互联网巨头&#xff0c; 还是中小型的科技公司都在高薪挖 AI 大模型人…

易盾空间推理 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 有相关问题请第一时间头像私信联系我…

Codeforces Round 968 (Div. 2) ABC题详细题解(C++,Python)

前言&#xff1a; 本文为Codeforces Round 968 (Div. 2)的ABC详细题解&#xff0c;包含C,Python语言描述&#xff0c;觉得有帮助或者写的不错可以点个赞 感觉D题说的好抽象&#xff0c;看不懂&#xff0c;之后实力够了更新 目录 题A: 题目大意和解题思路: 代码实现(C): 代…

全国大学生数学建模大赛——黄花鱼的最优捕捞策略

渔业管理部门规定&#xff0c;每年只允许在产卵孵化期前的8个 月进行捕捞作业。如果每年投入的捕捞能力(如渔船数、下网 次数等)固定不变&#xff0c;这时单位时间捕捞量将与各年龄组鱼群条 数成正比&#xff0c;比例系数不妨设为捕捞强度系数。通常使用 13mm 网眼的拉网&…