多View绘制进度矩形50ms刷新卡顿,求流畅兼性能优化方案
优化方案
1. 合并定时任务,避免重复调度
不要给30个视图各自创建定时任务,改用全局统一调度:一次定时任务触发所有视图的重绘请求。原方案中30个单线程定时任务会导致调度器任务队列堆积,全局调度能减少调度压力,同时保证所有视图同步更新。
示例修改:
// 全局维护所有需要更新的视图列表 private val progressViews = mutableListOf<YourViewType>() init { // 只创建一个全局定时任务 backgroundEach(50) { progressViews.forEach { it.postInvalidateOnAnimation() } } // 当前视图加入列表 progressViews.add(view) register(view.eventOnDraw.listen(::onDraw)) }
2. 替换postInvalidate为postInvalidateOnAnimation
postInvalidate会立即向UI线程发送重绘消息,短时间内大量发送会导致消息队列阻塞。postInvalidateOnAnimation会将重绘请求合并到系统下一帧的绘制流程(和Choreographer同步),避免冗余的重绘操作,更贴合Android的绘制机制。
3. 改用scheduleWithFixedDelay替代scheduleAtFixedRate
你的backgroundEach使用scheduleAtFixedRate,它会严格按固定间隔触发任务,不管上一次任务是否执行完成,单线程调度器下极易出现任务堆积。换成scheduleWithFixedDelay,它会在上一次任务执行完毕后再等待指定间隔触发,避免调度队列积压。
4. 缓存计算结果,减轻onDraw负担
onDraw方法会被频繁调用,要避免在其中做重复计算:
- 缓存
player.commands.size的值,避免每次onDraw都读取 - 提前计算进度值,不要在onDraw中访问
player的属性(如果player的方法调用有耗时操作,会直接拖慢绘制)
示例修改:
// 缓存命令总数 private var commandCount = player.commands.size // 缓存当前进度 private var currentProgress = 0f // 定时任务中先计算进度,再触发重绘 backgroundEach(50) { currentProgress = player.asStarted()?.inLoopIndex?.div(commandCount.toFloat()) ?: 0f progressViews.forEach { it.postInvalidateOnAnimation() } } private fun onDraw(canvas: Canvas) { if (currentProgress > 0) { canvas.drawRect(0f, 0f, width * currentProgress, height.toFloat(), paint) } }
5. 用ValueAnimator驱动进度(推荐方案)
放弃手动定时任务,改用Android系统的ValueAnimator,它的回调会和系统帧信号同步(默认60fps),能精准控制进度更新,且不会产生多余的重绘请求,同时天然支持进度的平滑过渡。
示例代码:
private val progressAnimator = ValueAnimator.ofFloat(0f, 1f).apply { // 根据总步数计算单次循环时长,或设置为固定值 duration = player.commands.size * 50L repeatCount = ValueAnimator.INFINITE addUpdateListener { anim -> currentProgress = anim.animatedValue as Float view.postInvalidateOnAnimation() } } init { progressAnimator.start() register(view.eventOnDraw.listen(::onDraw)) } private fun onDraw(canvas: Canvas) { if (currentProgress > 0) { canvas.drawRect(0f, 0f, width * currentProgress, height.toFloat(), paint) } }
6. 开启硬件加速优化绘制
确保视图开启硬件加速(默认开启),绘制矩形这类简单图形时,GPU会直接处理,大幅降低UI线程的负载。若视图有复杂绘制,可设置view.layerType = View.LAYER_TYPE_HARDWARE,让视图拥有独立的硬件层,减少重绘时的计算量。
内容的提问来源于stack exchange,提问作者Renetik
相关产品推荐
相关产品推荐

