Skip to content

Android 渲染优化

目录

  1. 引言
  2. [GPU 渲染管线](#gpu 渲染管线)
  3. [View 绘制流程优化](#view 绘制流程优化)
  4. 合成层优化
  5. 离屏渲染
  6. [Skia 引擎](#skia 引擎)
  7. [Systrace 分析](#systrace 分析)
  8. [Perfetto 分析](#perfetto 分析)
  9. [Layout 性能分析](#layout 性能分析)
  10. 面试考点
  11. 最佳实践
  12. 总结

引言

渲染性能直接影响应用流畅度。Android 的渲染系统复杂,涉及 CPU、GPU、合成器等多个组件。理解渲染原理和优化技巧对于打造流畅应用至关重要。本文将深入探讨 Android 渲染优化的各个方面。

渲染性能指标

指标说明目标值
帧率每秒渲染帧数60fps
渲染时间单帧渲染耗时< 16.67ms
绘制时间draw 方法耗时< 5ms
合成时间SurfaceFlinger 耗时< 3ms

渲染架构

┌────────────────────────────────────────┐
│          Android 渲染架构             │
├────────────────────────────────────────┤
│  App 进程     →  View 树构建          │
│  UI 线程      →  Measure/Layout/Draw    │
│  Renderer    →  GPU 命令生成          │
│  SurfaceFlinger → 合成显示            │
└────────────────────────────────────────┘

GPU 渲染管线

渲染管线详解

┌────────────────────────────────────────┐
│         GPU 渲染管线流程              │
├────────────────────────────────────────┤
│  1. Vertex Shader    顶点着色器       │
│  2. Geometry Shader  几何着色器       │
│  3. Rasterization    光栅化           │
│  4. Fragment Shader  片段着色器       │
│  5. Blending         混合             │
│  6. Display          显示             │
└────────────────────────────────────────┘

渲染管线优化

kotlin
class GPURenderingOptimizer {
    // 1. 减少 View 层级
    fun optimizeViewHierarchy(root: View) {
        // 合并 View
        // 使用 ConstraintLayout
        // 避免嵌套 ScrollView
    }
    
    // 2. 减少绘制操作
    fun optimizeDrawing(view: View) {
        // 使用硬件加速
        view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
    }
    
    // 3. 优化纹理
    fun optimizeTextures() {
        // 使用合适尺寸的图片
        // 避免运行时缩放
    }
}

顶点处理优化

kotlin
// 顶点处理优化示例
class VertexOptimization {
    // 减少顶点数量
    fun optimizePath(path: Path): Path {
        // 简化复杂路径
        val simplified = Path()
        // 使用简化算法
        return simplified
    }
    
    // 使用静态纹理
    fun useStaticTexture(texture: Texture) {
        // 标记为静态纹理
        texture.isStatic = true
    }
}

片段着色优化

kotlin
// 自定义 View 渲染优化
class OptimizedCustomView : View() {
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        // 使用 canvas.saveLayer 谨慎
        // 避免不必要的离屏渲染
        if (needsOffscreenRender) {
            canvas.saveLayer(canvas.clipBounds, Paint())
            // 绘制内容
            canvas.restore()
        } else {
            // 直接绘制
            drawContent(canvas)
        }
    }
}

View 绘制流程优化

Measure 优化

kotlin
// ❌ 错误:复杂的 Measure
class BadMeasureView : View {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 复杂的计算
        val width = complexCalculation(widthMeasureSpec)
        val height = complexCalculation(heightMeasureSpec)
        setMeasuredDimension(width, height)
    }
}

// ✅ 正确:简单的 Measure
class GoodMeasureView : View() {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)
        setMeasuredDimension(width, height)
    }
}

Layout 优化

kotlin
// 避免在 Layout 中执行耗时操作
class OptimizedLayoutView : View {
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        super.onLayout(changed, l, t, r, b)
        
        // 只计算位置,不做复杂操作
        // 复杂操作延迟执行
        post {
            complexLayoutOperation()
        }
    }
}

Draw 优化

kotlin
// Draw 优化技巧
class OptimizedDrawView : View() {
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val rect = Rect()
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        // 1. 复用 Paint 对象
        // 2. 复用 Rect 对象
        // 3. 使用 canvas.clipRect 限制绘制区域
        canvas.clipRect(visibleRect)
        
        // 4. 避免在 draw 中创建新对象
        drawContent(canvas, paint, rect)
    }
    
    // 5. 使用 invalidate 区域刷新
    fun updatePartial() {
        invalidate(updateRect)
    }
}

绘制流程完整优化

kotlin
class ViewDrawFlowOptimizer {
    // 1. 减少 View 层级
    fun flattenViewHierarchy() {
        // 使用 merge 标签
        // 使用 ConstraintLayout
        // 移除不必要的嵌套
    }
    
    // 2. 优化 View 可见性
    fun optimizeViewVisibility() {
        // GONE 比 INVISIBLE 更好
        // 使用 Visibility 控制
    }
    
    // 3. 使用 ViewStub
    fun useViewStub() {
        // 延迟加载 View
        val stub = findViewById<ViewStub>(R.id.stub)
        stub.inflate()
    }
    
    // 4. 优化 RecyclerView
    fun optimizeRecyclerView() {
        // 使用 DiffUtil
        // 设置 itemAnimator
        // 使用 ViewPool
    }
}

合成层优化

合成层原理

┌────────────────────────────────────────┐
│          合成层流程                    │
├────────────────────────────────────────┤
│  View 绘制 → 创建图层                 │
│  图层合成 → SurfaceFlinger             │
│  显示输出 → 屏幕刷新                  │
└────────────────────────────────────────┘

图层数量优化

kotlin
class LayerCountOptimizer {
    // 获取当前图层数量
    fun getLayerCount(view: View): Int {
        var count = 0
        if (view is ViewGroup) {
            for (i in 0 until view.childCount) {
                count += getLayerCount(view.getChildAt(i))
            }
        }
        if (view.layerType != View.LAYER_TYPE_NONE) {
            count++
        }
        return count
    }
    
    // 减少图层数量
    fun reduceLayerCount() {
        // 移除不必要的硬件图层
        // 避免过度使用 setLayerType
    }
}

合成层策略

kotlin
// 合成层优化策略
class CompositionStrategy {
    // 1. 减少独立图层
    fun optimizeComposition(view: View) {
        // 合并可以一起合成的 View
        view.setLayerType(View.LAYER_TYPE_NONE, null)
    }
    
    // 2. 使用 translateZ 创建合成层
    fun useTranslateZ(view: View) {
        // 比 setLayerType 更高效
        view.translationZ = 1f
    }
    
    // 3. 优化 elevation
    fun optimizeElevation(view: View) {
        // elevation 也会创建合成层
        view.elevation = 0f // 不需要时设为 0
    }
}

合成性能监控

kotlin
class CompositionMonitor {
    private var layerCount = 0
    
    fun monitorComposition(view: View) {
        layerCount = countLayers(view)
        
        if (layerCount > 20) {
            Log.w("Composition", "Too many layers: $layerCount")
        }
    }
    
    private fun countLayers(view: View): Int {
        var count = 0
        if (view.isHardwareLayer) {
            count++
        }
        if (view is ViewGroup) {
            for (i in 0 until view.childCount) {
                count += countLayers(view.getChildAt(i))
            }
        }
        return count
    }
}

离屏渲染

离屏渲染原理

┌────────────────────────────────────────┐
│          离屏渲染流程                  │
├────────────────────────────────────────┤
│  1. 创建离屏缓冲区                    │
│  2. 在缓冲区中绘制                    │
│  3. 将缓冲区绘制到主画布               │
│                                         │
│  优点:支持复杂特效                    │
│  缺点:增加内存和渲染时间              │
└────────────────────────────────────────┘

离屏渲染触发

kotlin
class OffscreenRenderTriggers {
    // 1. canvas.saveLayer
    fun triggerSaveLayer(canvas: Canvas) {
        canvas.saveLayer(Rect(0, 0, width, height), Paint())
        // 绘制内容
        canvas.restore()
    }
    
    // 2. 圆角 View
    fun triggerRoundCorner(view: View) {
        view.clipToOutline = true // 触发离屏
    }
    
    // 3. 透明度绘制
    fun triggerAlphaDraw(view: View) {
        view.alpha = 0.5f // 触发离屏
    }
}

避免不必要的离屏渲染

kotlin
// ❌ 错误:过度使用 saveLayer
class BadOffscreenRender : View() {
    override fun onDraw(canvas: Canvas) {
        canvas.saveLayer(Rect(0, 0, width, height), Paint())
        // 绘制内容
        canvas.restore()
    }
}

// ✅ 正确:按需使用 saveLayer
class GoodOffscreenRender : View() {
    override fun onDraw(canvas: Canvas) {
        if (needsOffscreen()) {
            canvas.saveLayer(visibleRect, Paint())
            drawContent(canvas)
            canvas.restore()
        } else {
            drawContent(canvas)
        }
    }
    
    private fun needsOffscreen(): Boolean {
        // 只在必要时返回 true
        return hasAlpha || hasShadow
    }
}

离屏渲染优化

kotlin
class OffscreenRenderOptimizer {
    // 1. 缓存离屏结果
    private val cachedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    private val cachedCanvas = Canvas(cachedBitmap)
    
    fun optimizeOffscreen() {
        // 缓存复杂绘制
        if (!isDirty) {
            canvas.drawBitmap(cachedBitmap, 0f, 0f, null)
            return
        }
        
        // 重新绘制并缓存
        drawToCachedCanvas(cachedCanvas)
        canvas.drawBitmap(cachedBitmap, 0f, 0f, null)
    }
    
    // 2. 使用 setLayerType 替代 saveLayer
    fun useLayerType(view: View) {
        view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
    }
}

Skia 引擎

Skia 简介

Skia 是 Android 的图形渲染引擎,负责:

  • 2D 图形绘制
  • 文本渲染
  • 图像合成
  • 路径处理

Skia 优化技巧

kotlin
class SkiaOptimization {
    // 1. 使用抗锯齿
    val antiAliasPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    
    // 2. 设置 Paint 标志
    val optimizedPaint = Paint().apply {
        isAntiAlias = true
        isDither = true
        xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
    }
    
    // 3. 复用 Paint 对象
    private val sharedPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    
    fun drawWithSharedPaint(canvas: Canvas) {
        canvas.drawCircle(100f, 100f, 50f, sharedPaint)
    }
}

路径优化

kotlin
class PathOptimization {
    // 1. 简化路径
    fun simplifyPath(path: Path): Path {
        val simplified = Path()
        // 使用简化算法
        return simplified
    }
    
    // 2. 缓存路径
    private val cachedPath = Path()
    
    fun useCachedPath() {
        cachedPath.reset()
        cachedPath.addCircle(100f, 100f, 50f, Path.Direction.CW)
        canvas.drawPath(cachedPath, paint)
    }
    
    // 3. 使用 PathMeasure
    fun measurePath(path: Path) {
        val measure = PathMeasure(path, false)
        // 获取路径信息
        val length = measure.length
    }
}

文本渲染优化

kotlin
class TextRenderOptimization {
    // 1. 使用静态文本
    private val staticTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        textSize = 16f
        typeface = Typeface.DEFAULT
    }
    
    // 2. 避免动态 Typeface
    fun avoidDynamicTypeface() {
        // ❌ 错误:每次创建 Typeface
        // paint.typeface = Typeface.createFromAsset(...)
        
        // ✅ 正确:缓存 Typeface
        paint.typeface = cachedTypeface
    }
    
    // 3. 使用 TextPaint
    fun useTextPaint() {
        val textPaint = TextPaint().apply {
            isAntiAlias = true
            textSize = 16f
        }
        canvas.drawText(text, x, y, textPaint)
    }
}

Systrace 分析

Systrace 基础

bash
# 安装 systrace
pip install systrace

# 捕获 trace
python -m systrace.systrace --time 10 --output trace.html \
    -m androidview androidinput androidweb

# 分析 trace
cateto trace.html

Systrace 分析项

kotlin
class SystraceAnalyzer {
    // 1. 查看绘制时间
    fun analyzeDrawTime() {
        // 检查 Draw 时间是否超过 12ms
        // 检查布局时间
    }
    
    // 2. 查看合成时间
    fun analyzeCompositionTime() {
        // 检查 SurfaceFlinger 合成时间
        // 检查图层数量
    }
    
    // 3. 查看 GPU 时间
    fun analyzeGPUTime() {
        // 检查 GPU 渲染时间
        // 检查离屏渲染
    }
}

使用 Trace 标记

kotlin
// 添加自定义 trace 标记
class TraceMarker {
    fun traceBlock(name: String, block: () -> Unit) {
        Trace.beginSection(name)
        try {
            block()
        } finally {
            Trace.endSection()
        }
    }
    
    fun traceViewDraw(view: View) {
        traceBlock("Draw_${view.javaClass.simpleName}") {
            // 绘制逻辑
        }
    }
}

Perfetto 分析

Perfetto 使用

bash
# 使用 Perfetto UI
# https://ui.perfetto.dev

# 捕获 trace
perfetto --config android.config --duration=10

# 分析 trace
# 打开 ui.perfetto.dev 加载 trace 文件

Perfetto 分析项

kotlin
class PerfettoAnalyzer {
    // 1. 渲染时间线
    fun analyzeRenderTimeline() {
        // View 绘制
        // 合成时间
        // 帧提交
    }
    
    // 2. GPU 活动
    fun analyzeGPUActivity() {
        // GPU 渲染队列
        // 纹理上传
        //  shader 编译
    }
    
    // 3. 内存分配
    fun analyzeMemoryAllocation() {
        // 纹理内存
        // 离屏缓冲区
        // Java 对象分配
    }
}

Perfetto 配置

kotlin
// 启用 Perfetto 追踪
class PerfettoConfig {
    fun enablePerfetto() {
        // 设置追踪类别
        val categories = listOf(
            "android.view.Choreographer",
            "gpu",
            "sched"
        )
        
        // 开始追踪
        startTrace(categories)
    }
}

Layout 性能分析

Layout 性能指标

kotlin
class LayoutPerformanceMetrics {
    // 1. Measure 时间
    var measureTime: Long = 0
    
    // 2. Layout 时间
    var layoutTime: Long = 0
    
    // 3. Draw 时间
    var drawTime: Long = 0
    
    // 4. View 层级深度
    var viewDepth: Int = 0
    
    // 5. View 总数
    var viewCount: Int = 0
}

布局层级优化

kotlin
// ❌ 错误:过深的层级
<LinearLayout>
    <LinearLayout>
        <LinearLayout>
            <LinearLayout>
                <TextView />
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

// ✅ 正确:扁平化层级
<ConstraintLayout>
    <TextView
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</ConstraintLayout>

Layout 性能监控

kotlin
class LayoutPerformanceMonitor {
    fun monitorLayout(view: View) {
        // 1. 统计 View 数量
        val viewCount = countViews(view)
        
        // 2. 统计层级深度
        val depth = getDepth(view)
        
        // 3. 记录测量时间
        val startTime = System.nanoTime()
        view.measure(...)
        val measureTime = (System.nanoTime() - startTime) / 1000000
        
        // 4. 告警
        if (viewCount > 50 || depth > 10 || measureTime > 5) {
            Log.w("Layout", "Performance issue detected")
        }
    }
    
    private fun countViews(view: View): Int {
        var count = 1
        if (view is ViewGroup) {
            for (i in 0 until view.childCount) {
                count += countViews(view.getChildAt(i))
            }
        }
        return count
    }
}

布局优化技巧

kotlin
class LayoutOptimizationTips {
    // 1. 使用 ConstraintLayout
    fun useConstraintLayout() {
        // 减少嵌套
        // 提高性能
    }
    
    // 2. 使用 merge
    fun useMerge() {
        // 合并布局
        // 减少层级
    }
    
    // 3. 使用 include 谨慎
    fun useIncludeCarefully() {
        // include 会增加层级
        // 配合 merge 使用
    }
    
    // 4. 避免嵌套 ScrollView
    fun avoidNestedScrollView() {
        // 使用 NestedScrollView
        // 或者重新设计布局
    }
}

面试考点

基础考点

1. 硬件加速原理

问题: 什么是硬件加速?如何启用?

回答:

  • GPU 代替 CPU 渲染
  • 支持透明度和特效
  • 通过 hardwareAccelerated="true" 启用

2. 过度绘制

问题: 什么是过度绘制?如何检测?

回答:

  • 同一像素绘制多次
  • 开发选项 → 调试绘制过度绘制
  • 红色表示 4 次以上绘制

3. 离屏渲染

问题: 什么是离屏渲染?如何避免?

回答:

  • 在离屏缓冲区绘制
  • saveLayer 触发
  • 减少不必要的 saveLayer

进阶考点

1. 合成层优化

问题: 如何优化合成层?

回答:

  • 减少独立图层数量
  • 使用 translateZ 创建图层
  • 避免过度使用 elevation

2. View 绘制流程

问题: View 绘制流程是什么?

回答:

  • Measure → 测量大小
  • Layout → 计算位置
  • Draw → 绘制内容

3. 掉帧检测

问题: 如何检测掉帧?

回答:

  • 使用 Choreographer
  • 使用 Systrace
  • 使用 Perfetto

高级考点

1. GPU 渲染管线

问题: GPU 渲染管线的流程?

回答:

  • 顶点着色器
  • 几何着色器
  • 光栅化
  • 片段着色器
  • 混合

2. Skia 优化

问题: Skia 优化技巧?

回答:

  • 复用 Paint 对象
  • 简化路径
  • 缓存文本 Typeface

最佳实践

渲染优化检查清单

kotlin
class RenderOptimizationChecklist {
    val checks = listOf(
        Check("启用硬件加速", { isHardwareAccelerationEnabled() }),
        Check("减少 View 层级", { viewDepth < 10 }),
        Check("避免过度绘制", { overdrawCount < 3 }),
        Check("优化合成层", { layerCount < 20 }),
        Check("减少离屏渲染", { offscreenCount < 5 })
    )
}

常见错误

1. 过度使用硬件图层

kotlin
// ❌ 错误:所有 View 都使用硬件图层
override fun onFinishInflate() {
    findAllViews().forEach {
        it.setLayerType(View.LAYER_TYPE_HARDWARE, null)
    }
}

// ✅ 正确:仅在需要时使用
fun animateView(view: View) {
    view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
    // 动画结束后移除
}

2. 在 Draw 中创建对象

kotlin
// ❌ 错误:在 onDraw 中创建对象
override fun onDraw(canvas: Canvas) {
    val paint = Paint() // 每次都创建
    canvas.drawCircle(100f, 100f, 50f, paint)
}

// ✅ 正确:复用对象
private val paint = Paint()
override fun onDraw(canvas: Canvas) {
    canvas.drawCircle(100f, 100f, 50f, paint)
}

总结

渲染优化是提升应用流畅度的关键。通过 GPU 渲染管线优化、View 绘制流程优化、合成层优化、离屏渲染优化、Skia 引擎优化,以及使用 Systrace 和 Perfetto 分析工具,可以显著提升渲染性能。

关键要点

  1. GPU 渲染:理解渲染管线原理
  2. View 绘制:优化 Measure/Layout/Draw
  3. 合成层:减少独立图层数量
  4. 离屏渲染:避免不必要的离屏
  5. Skia 引擎:复用对象,简化路径
  6. 性能分析:使用 Systrace/Perfetto

渲染性能优化效果

优化项优化前优化后提升
帧率45fps60fps33%
绘制时间15ms5ms67%
图层数量301550%

通过系统的渲染优化,可以显著提升应用流畅度,让用户体验更加丝滑。