# 流畅度优化

## 目录
- [卡顿原因分析](#卡顿原因分析)
- [60fps保证](#60fps保证)
- [VSYNC机制](#vsync机制)
- [Choreographer](#choreographer)
- [过度绘制优化](#过度绘制优化)
- [流畅度优化工具](#流畅度优化工具)
- [面试常见问题](#面试常见问题)

---

## 卡顿原因分析

### 卡顿的定义

卡顿是指界面渲染不流畅，帧率低于 60fps（每秒 60 帧），用户感知到画面不连续。

### 卡顿的主要原因

#### 1. 主线程阻塞

```java
// ❌ 错误示例：主线程执行耗时操作
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    // 主线程执行耗时操作
    loadDataFromDatabase(); // 阻塞主线程
    processLargeImage(); // 阻塞主线程
}

// ✅ 正确做法：使用异步处理
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    // 异步执行耗时操作
    new Thread(() -> {
        loadDataFromDatabase();
        processLargeImage();
        
        // 更新 UI 回到主线程
        runOnUiThread(() -> {
            updateUI();
        });
    }).start();
}
```

#### 2. 布局复杂

```xml
<!-- ❌ 错误示例：布局层级过深 -->
<LinearLayout>
    <LinearLayout>
        <LinearLayout>
            <LinearLayout>
                <TextView />
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

<!-- ✅ 正确做法：使用 ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView />
</androidx.constraintlayout.widget.ConstraintLayout>
```

#### 3. 过度绘制

```java
// ❌ 错误示例：多层背景
<LinearLayout
    android:background="@drawable/bg1">
    <LinearLayout
        android:background="@drawable/bg2">
        <TextView
            android:background="@drawable/bg3" />
    </LinearLayout>
</LinearLayout>

// ✅ 正确做法：移除不必要的背景
<LinearLayout>
    <TextView />
</LinearLayout>
```

#### 4. 频繁创建对象

```java
// ❌ 错误示例：在 onDraw 中创建对象
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint(); // 每次绘制都创建新对象
    canvas.drawText("Hello", 0, 0, paint);
}

// ✅ 正确做法：复用对象
public class MyView extends View {
    private Paint mPaint; // 复用 Paint 对象
    
    public MyView(Context context) {
        super(context);
        mPaint = new Paint();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText("Hello", 0, 0, mPaint);
    }
}
```

#### 5. 动画性能问题

```java
// ❌ 错误示例：使用 View 动画（在主线程）
view.animate()
    .translationX(100)
    .setDuration(1000)
    .start();

// ✅ 正确做法：使用属性动画（硬件加速）
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 0, 100);
animator.setDuration(1000);
animator.start();
```

---

## 60fps保证

### 帧率计算

```
fps = 1000ms / 单帧耗时
60fps = 1000ms / 16.67ms
```

### 保证 60fps 的要求

1. **单帧耗时 < 16.67ms**
2. **主线程不阻塞**
3. **布局不复杂**
4. **不过度绘制**
5. **使用硬件加速**

### 性能监控

```java
public class FrameMonitor {
    private Choreographer choreographer;
    private long lastFrameTime = 0;
    private int frameCount = 0;
    private long totalFrameTime = 0;
    
    public void start() {
        choreographer = Choreographer.getInstance();
        choreographer.postFrameCallback(new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                if (lastFrameTime != 0) {
                    long frameTime = (frameTimeNanos - lastFrameTime) / 1_000_000; // 转换为毫秒
                    totalFrameTime += frameTime;
                    frameCount++;
                    
                    if (frameTime > 16.67) {
                        Log.w("FrameMonitor", "Frame dropped: " + frameTime + "ms");
                    }
                }
                lastFrameTime = frameTimeNanos;
                choreographer.postFrameCallback(this);
            }
        });
    }
    
    public float getAverageFps() {
        if (frameCount == 0) {
            return 0;
        }
        float averageFrameTime = totalFrameTime / (float) frameCount;
        return 1000.0f / averageFrameTime;
    }
}
```

---

## VSYNC机制

### VSYNC 简介

VSYNC（Vertical Synchronization）是垂直同步信号，用于同步屏幕刷新和 GPU 渲染。

### VSYNC 工作原理

```
时间轴：
0ms    16.67ms   33.34ms   50ms
|--------|--------|--------|
  VSYNC   VSYNC   VSYNC
```

1. **VSYNC 信号触发**：每 16.67ms 触发一次
2. **CPU 处理**：处理输入事件、测量布局
3. **GPU 渲染**：渲染帧到缓冲区
4. **屏幕显示**：显示缓冲区内容

### VSYNC 优化

```java
// 使用 VSYNC 同步动画
view.postOnAnimation(() -> {
    // 在下一个 VSYNC 执行
    updateAnimation();
});
```

---

## Choreographer

### Choreographer 简介

Choreographer 是 Android 4.1+ 引入的帧调度器，用于协调动画、输入和绘制。

### Choreographer 工作原理

```java
public class Choreographer {
    // 三种回调类型
    public static final int CALLBACK_INPUT = 0;      // 输入事件
    public static final int CALLBACK_ANIMATION = 1;  // 动画
    public static final int CALLBACK_TRAVERSAL = 2;  // 绘制
    
    // 执行顺序
    // 1. CALLBACK_INPUT：处理输入事件
    // 2. CALLBACK_ANIMATION：执行动画
    // 3. CALLBACK_TRAVERSAL：执行绘制
}
```

### Choreographer 使用

```java
// 在下一个 VSYNC 执行
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        // 执行操作
        updateUI();
        
        // 继续下一帧
        Choreographer.getInstance().postFrameCallback(this);
    }
});

// 延迟执行
Choreographer.getInstance().postFrameCallbackDelayed(callback, delayMillis);
```

### 帧率监控

```java
public class FrameRateMonitor {
    private Choreographer choreographer;
    private long lastFrameTime = 0;
    private int droppedFrames = 0;
    
    public void start() {
        choreographer = Choreographer.getInstance();
        choreographer.postFrameCallback(new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                if (lastFrameTime != 0) {
                    long frameTime = (frameTimeNanos - lastFrameTime) / 1_000_000;
                    if (frameTime > 16.67) {
                        droppedFrames += (int) (frameTime / 16.67) - 1;
                        Log.w("FrameRate", "Dropped frames: " + droppedFrames);
                    }
                }
                lastFrameTime = frameTimeNanos;
                choreographer.postFrameCallback(this);
            }
        });
    }
}
```

---

## 过度绘制优化

### 什么是过度绘制？

过度绘制是指同一像素被绘制多次，浪费 GPU 资源。

### 过度绘制检测

#### 1. 开发者选项

```
设置 -> 开发者选项 -> 调试 GPU 过度绘制 -> 显示过度绘制区域
```

- **蓝色**：绘制 1 次（正常）
- **绿色**：绘制 2 次（可接受）
- **浅红色**：绘制 3 次（需要优化）
- **深红色**：绘制 4 次以上（必须优化）

#### 2. 代码检测

```java
// 启用过度绘制检测
View.setLayerType(View.LAYER_TYPE_HARDWARE, null);
```

### 过度绘制优化方法

#### 1. 移除不必要的背景

```xml
<!-- ❌ 错误示例：多层背景 -->
<LinearLayout
    android:background="@drawable/bg1">
    <LinearLayout
        android:background="@drawable/bg2">
        <TextView />
    </LinearLayout>
</LinearLayout>

<!-- ✅ 正确做法：移除不必要的背景 -->
<LinearLayout>
    <TextView />
</LinearLayout>
```

#### 2. 使用 clipRect

```java
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    
    // 裁剪绘制区域
    canvas.clipRect(0, 0, width, height);
    
    // 只绘制可见区域
    drawContent(canvas);
}
```

#### 3. 使用 ViewStub

```xml
<!-- 延迟加载不必要的布局 -->
<ViewStub
    android:id="@+id/view_stub"
    android:layout="@layout/heavy_layout" />
```

#### 4. 优化自定义 View

```java
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    
    // 只绘制可见区域
    if (shouldDraw()) {
        drawContent(canvas);
    }
}
```

---

## 流畅度优化工具

### 1. GPU 渲染模式分析

```
设置 -> 开发者选项 -> GPU 渲染模式分析 -> 在屏幕上显示为条形图
```

- **绿色线**：16.67ms 基准线
- **红色条**：超过 16.67ms，表示卡顿

### 2. Systrace/Perfetto

```bash
# 分析流畅度
python systrace.py -t 10 -o trace.html gfx view input
```

### 3. Android Studio Profiler

- CPU Profiler：分析 CPU 使用
- Memory Profiler：分析内存使用
- Network Profiler：分析网络请求

### 4. Layout Inspector

- 查看布局层级
- 分析布局性能
- 查看 View 属性

### 5. 自定义监控工具

```java
public class PerformanceMonitor {
    private Handler handler = new Handler(Looper.getMainLooper());
    private Runnable monitorRunnable = new Runnable() {
        @Override
        public void run() {
            // 监控主线程性能
            long startTime = System.currentTimeMillis();
            handler.post(() -> {
                long endTime = System.currentTimeMillis();
                long duration = endTime - startTime;
                if (duration > 16) {
                    Log.w("Performance", "Main thread blocked: " + duration + "ms");
                }
            });
            handler.postDelayed(this, 100);
        }
    };
    
    public void start() {
        handler.post(monitorRunnable);
    }
    
    public void stop() {
        handler.removeCallbacks(monitorRunnable);
    }
}
```

---

## 面试常见问题

### Q1: 如何保证 60fps？

**答案：**
1. **单帧耗时 < 16.67ms**
2. **主线程不阻塞**：避免在主线程执行耗时操作
3. **布局优化**：减少布局层级，使用 ConstraintLayout
4. **避免过度绘制**：移除不必要的背景
5. **使用硬件加速**：启用硬件加速
6. **优化动画**：使用属性动画，避免 View 动画

### Q2: 什么是 VSYNC？

**答案：**
- VSYNC（Vertical Synchronization）是垂直同步信号
- 用于同步屏幕刷新和 GPU 渲染
- 每 16.67ms 触发一次（60fps）
- 保证画面不撕裂，流畅显示

### Q3: Choreographer 的作用？

**答案：**
- Choreographer 是帧调度器
- 协调输入事件、动画、绘制
- 按顺序执行：输入 -> 动画 -> 绘制
- 保证在 VSYNC 信号时执行

### Q4: 如何检测卡顿？

**答案：**
1. **GPU 渲染模式分析**：查看条形图
2. **Systrace/Perfetto**：分析系统性能
3. **Android Studio Profiler**：分析 CPU、内存
4. **自定义监控**：监控帧率、主线程阻塞
5. **开发者选项**：启用过度绘制检测

### Q5: 过度绘制如何优化？

**答案：**
1. **移除不必要的背景**：减少背景层数
2. **使用 clipRect**：只绘制可见区域
3. **使用 ViewStub**：延迟加载布局
4. **优化自定义 View**：只绘制必要内容
5. **使用硬件加速**：提升绘制性能

### Q6: 主线程阻塞的原因和解决方案？

**答案：**
- **原因**：
  1. 执行耗时操作（网络请求、数据库操作）
  2. 复杂计算
  3. 同步等待
  
- **解决方案**：
  1. 使用异步处理（线程、协程）
  2. 使用 Handler 切换到后台线程
  3. 使用 AsyncTask、RxJava 等框架
  4. 优化算法，减少计算时间

### Q7: 流畅度优化的最佳实践？

**答案：**
1. **主线程优化**：避免阻塞，使用异步处理
2. **布局优化**：减少层级，使用 ConstraintLayout
3. **绘制优化**：避免过度绘制，使用硬件加速
4. **动画优化**：使用属性动画，避免 View 动画
5. **监控性能**：使用工具检测，及时发现问题

---

## 总结

流畅度优化是 Android 性能优化的重要部分，主要从以下几个方面入手：

1. **保证 60fps**：单帧耗时 < 16.67ms
2. **主线程优化**：避免阻塞，使用异步处理
3. **布局优化**：减少层级，使用 ConstraintLayout
4. **绘制优化**：避免过度绘制，使用硬件加速
5. **VSYNC 机制**：理解 VSYNC 和 Choreographer
6. **性能监控**：使用工具检测，及时发现问题

通过合理的流畅度优化，可以提升用户体验，让应用运行更加流畅。

---

*最后更新：2024年*
