如何修复Android Vitals中的慢渲染问题

21
我有一个应用程序,它在新的Google Play控制台 - Android Vitals部分中被列为慢渲染的最低25%。我对此感到担忧,因为这样的文章似乎表明,如果您的应用程序排在最低25%,Google Play可能会惩罚您的应用程序在Play商店排名。
然而,对于我的应用程序,改善此指标似乎是不可能的。它播放音乐并且有一个SeekBar和TextView,每250毫秒更新一次,就像任何音乐播放器一样。我制作了最小的基本程序进行演示:
public class MainActivity extends AppCompatActivity {

    int count;
    SeekBar seekBar;
    TextView textView;

    Runnable runnable =
            new Runnable() {
                @Override
                public void run() {
                    textView.setText(Integer.toString(count));
                    seekBar.setProgress(count);
                    ++count;
                    seekBar.postDelayed(runnable, 250);
                }
            };

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

        seekBar = (SeekBar) findViewById(R.id.seek);
        textView = (TextView) findViewById(R.id.text);
        seekBar.post(runnable);
    }
}

完整项目在这里:https://github.com/svenoaks/SlowRendering.git

当我在类似Nexus设备的硬件上运行此程序时,使用以下命令:
adb shell dumpsys gfxinfo com.example.xyz.slowrendering,我得到以下结果:

Stats since: 19222191084749ns
Total frames rendered: 308
Janky frames: 290 (94.16%)
90th percentile: 32ms
95th percentile: 36ms
99th percentile: 44ms
Number Missed Vsync: 2
Number High input latency: 0
Number Slow UI thread: 139
Number Slow bitmap uploads: 0
Number Slow issue draw commands: 283

这意味着几乎所有的帧都需要超过16毫秒来渲染,我猜测是由于更新的周期性导致的。据我所见,我测试过的所有其他音乐播放器应用程序也存在这个“慢渲染”问题。我担心谷歌的算法会破坏我的应用排名,有没有办法可以提高我的得分?
2个回答

21

你的布局中的TextView引起了问题。因为它有一个layout_widthwrap_content,这意味着它的宽度必须等于内容的宽度(例如这里的文本)。因此,每次调用TextView.setText都会导致昂贵的测量/布局传递。将layout_width简单设置为match_parent即可解决该问题。

enter image description here

enter image description here

这里有两张从systrace中获得的图像,演示了在1帧中在UI线程上运行的工作。顶部的使用layout_width=wrap_content,底部的使用layout_width=match_parent

我测试过的以下两种方法可以提高帧率:

  • 如果您在更短的时间间隔内发布可运行项,比如16ms(seekBar.postDelayed(runnable, 16)),您就能获得流畅的60fps: enter image description here

    P/s: 我还不确定为什么。

  • 使用一些其他方法更新count值而不是在Runnable中。使用View.postOnAnimation(Runnable)重新安排Runnable。该示例项目的结果是60FPS。

EDIT: 两个使用postOnAnimation(Runnable)Runnable

Runnable runnable =
  new Runnable() {
    @Override
    public void run() {
      textView.setText(Integer.toString(count));
      seekBar.setProgress(count);
      seekBar.postOnAnimation(this);
    }
  };



Runnable updateCount = new Runnable() {
    @Override public void run() {
      ++count;
      seekBar.postDelayed(this, 250);
    }
  };

我将layout_width设置为match_parent,但在Moto X Pure和Galaxy S8+上仍然出现大多数不稳定的帧。虽然在X Pure上,“Slow UI Thread”类别的帧数较少,90-99百分位帧数减少到20毫秒左右。 - Steve M
你是在谈论示例项目还是你的生产应用程序? - Tin Tran
即使使用match_parent,在示例项目中仍然存在不稳定的帧。 - Steve M
这也取决于交互式调度程序。在某些设备上,如果您触摸屏幕,它会立即变为<16ms。我使用一个类更新了我的生产应用程序,该类发送NOP命令以使Nexus和Pixel设备的CPU频率最大化,并将我的“慢”会话从10%减少到5%。在开发者控制台中标记为带有橙色“边界线”图标,而不是之前选项卡上的红色图标。 - Steve M
如果你需要的话,我可以帮你打开一个Github PR。 - Tin Tran
显示剩余5条评论

6

我查看了你的代码。不确定这是否是实际的代码,或者你还有更多的代码。无论如何,我会指出一些安卓系统渲染问题。

1. 过度绘制

过度绘制是指浪费GPU处理时间,为仅由其他元素再次着色的像素着色。如果父容器布局添加了背景,然后子项也添加了背景,或者您在应用程序主题的样式文件中添加了公共背景,然后在创建的其他xml布局文件中添加了背景,则可能会出现此类问题。

过度绘制的原因可能是任何东西,请检查您的代码是否存在此类问题。所有移动设备都安装有开发者工具,可在开发人员选项中检查Overdraw。这里是过度绘制的官方文档。

2. 视图层次结构

为了渲染每个视图,Android需要经历三个阶段:

1. 测量

2. 布局

3. 绘制

Android完成这些阶段所需的时间与您的层次结构中的视图数量成比例。我在您的布局文件中看到了包含线性布局的约束布局。我不确定它的用途。约束布局旨在帮助开发人员减少视图层次结构,降低特定布局可以拥有的子项数量。也有一个工具可帮助您完成此任务。这里是官方的安卓指南。请尝试使用这些步骤来解决GPU渲染问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接