Jetpack Compose 如何统计UI渲染耗时?
Jetpack Compose 场景下Fragment UI渲染耗时统计方案
核心逻辑是分别记录渲染触发起点和UI完成渲染终点两个时间戳,差值就是总渲染耗时,以下是可直接落地的实现方案:
1. 确定统计起点
通常选Fragment触发Compose加载的时机作为起点,直接在Fragment的onCreateView生命周期中记录时间戳即可:
class YourFeatureFragment : Fragment() { // 存储渲染开始时间戳 private var renderStartTime = 0L override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { renderStartTime = System.currentTimeMillis() return ComposeView(requireContext()).apply { setContent { YourFeatureScreen() } } } }
2. 统计纯UI渲染耗时(无异步数据依赖场景)
如果页面不需要等待接口返回、静态UI渲染完成就算结束,可以用Compose提供的onGloballyPositioned修饰符,它会在布局计算完成、位置确定时回调,刚好对应UI首次渲染完成的时机:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { renderStartTime = System.currentTimeMillis() return ComposeView(requireContext()).apply { setContent { // 标记是否已经上报,避免后续布局变更重复触发统计 val hasReported = remember { mutableStateOf(false) } // 修饰符加在整个页面的根Composable上 YourFeatureScreen( modifier = Modifier.onGloballyPositioned { if (!hasReported.value) { val renderCost = System.currentTimeMillis() - renderStartTime // 此处可替换为你自己的埋点上报逻辑 Log.d("RenderTrack", "静态UI渲染总耗时:${renderCost}ms") hasReported.value = true } } ) } } }
3. 统计完整首屏渲染耗时(含异步数据依赖场景)
如果页面需要等接口返回数据、列表渲染完成才算首屏结束,可以结合业务状态判断,等数据加载完成后再触发统计:
该方案可以精准对齐业务侧的首屏定义,不会把骨架屏、加载态的渲染时间当成最终首屏耗时
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { renderStartTime = System.currentTimeMillis() return ComposeView(requireContext()).apply { setContent { val viewModel: YourFeatureViewModel = viewModel() // 监听页面业务状态 val pageState by viewModel.pageState.collectAsStateWithLifecycle() val hasReported = remember { mutableStateOf(false) } when(pageState) { is PageState.Loading -> LoadingSkeleton() is PageState.Error -> ErrorPage() is PageState.Success -> { // 数据加载成功后渲染内容页,再监听布局完成回调 FeatureSuccessPage( data = (pageState as PageState.Success).data, modifier = Modifier.onGloballyPositioned { if (!hasReported.value) { val renderCost = System.currentTimeMillis() - renderStartTime Log.d("RenderTrack", "完整首屏渲染总耗时:${renderCost}ms") hasReported.value = true } } ) } } } } }
4. 无侵入式系统级统计方案
如果不想侵入Compose业务代码,可以给ComposeView设置绘制监听,首次绘制完成就是渲染结束时机:
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { renderStartTime = System.currentTimeMillis() return ComposeView(requireContext()).apply { setContent { YourFeatureScreen() } viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { override fun onPreDraw(): Boolean { val renderCost = System.currentTimeMillis() - renderStartTime Log.d("RenderTrack", "首次绘制完成耗时:${renderCost}ms") // 用完就移除监听,避免重复触发 viewTreeObserver.removeOnPreDrawListener(this) return true } }) } }
两种方案的适用场景:
onGloballyPositioned方案:适合需要自定义首屏结束节点、对齐业务统计标准的场景,灵活性更高OnPreDrawListener方案:适合不需要区分业务加载状态、只需要统计系统层面首次绘制耗时的场景,不需要修改业务Composable代码
内容的提问来源于stack exchange,提问作者Sandesh Baliga
相关产品推荐
相关产品推荐

