返回

深入剖析Android卡屏:一段奇葩经历

Android

在软件开发的浩瀚海洋中,卡屏是程序员们挥之不去的阴影。而近期,一次Android卡屏的遭遇,让我陷入了漫长的排查与探索之旅。

问题的发现

在Android Studio中新建了一个简单的项目,在MainActivity中添加了一个Button和一个ProgressBar。代码如下:

public class MainActivity extends AppCompatActivity {

    private Button button;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = findViewById(R.id.button);
        progressBar = findViewById(R.id.progressBar);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                progressBar.setVisibility(View.VISIBLE);
                // 假设此处执行一些耗时操作
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                progressBar.setVisibility(View.GONE);
            }
        });
    }
}

当点击按钮时,ProgressBar会显示并执行一段耗时操作(模拟)。但在运行该项目时,我们却遇到了奇怪的卡屏现象:

  • 症状一: 点击按钮后,界面卡死5秒,然后ProgressBar才消失。
  • 症状二: Logcat中并没有任何异常信息。

问题排查

起初,我怀疑是模拟的耗时操作导致的,但通过修改休眠时间证实,这不是问题根源。接着,我逐步排查代码,尝试重写耗时操作,修改按钮点击事件,但均无果。

日志调试

绝望之际,我转而使用日志调试。通过在耗时操作的起始和结束处打印日志,发现操作的执行时间仅为100ms左右,与卡屏时间相差甚远。

问题定位

此时,我意识到卡屏问题可能出在Android系统底层。我查看了Android源码,发现有一个名为Choreographer的类,它负责协调Android应用中的UI渲染。

进一步调查发现,Choreographer有一个vsync机制,该机制会将UI渲染同步到屏幕刷新率。在Android设备上,屏幕刷新率通常为60Hz,这意味着Choreographer会每隔16.6ms触发一次UI渲染。

原因分析

我意识到,在耗时操作期间,Android系统可能会因为vsync机制而将UI渲染线程阻塞。当耗时操作时间较短(例如100ms)时,UI渲染线程会在5次vsync之后才被唤醒。因此,当耗时操作结束后,ProgressBar的消失操作需要等到下一次vsync才被执行,导致了5秒的卡屏。

解决方案

针对该问题,提出了两种解决方案:

  1. 使用非阻塞异步操作: 将耗时操作移到非阻塞的异步线程中执行,避免阻塞UI渲染线程。
  2. 在耗时操作完成后主动触发Choreographer 使用Choreographer.getInstance().postFrameCallback()方法,在耗时操作完成后主动触发一次UI渲染,从而避免卡顿。

我采用了第二种解决方案,并在代码中加入以下代码:

progressBar.setVisibility(View.GONE);
Choreographer.getInstance().postFrameCallback(new FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {

    }
});

优化建议

此外,还有一些针对Android卡屏的优化建议:

  • 优化耗时操作,避免UI线程中执行复杂计算或网络请求。
  • 使用异步线程处理非必须的UI更新任务。
  • 减少View的嵌套层级,优化View的绘制性能。
  • 使用StrictMode检查和修复潜在的性能问题。

结语

这次Android卡屏的奇葩经历,让我深入了解了Choreographer机制和UI渲染的原理。通过分析和调试,我们找到了问题的根源,并提出了有效的解决方案和优化建议。我希望这篇文章能为遇到类似问题的开发者提供帮助,助力打造流畅、无卡顿的Android应用。