How to fix Slow Rendering (Android vitals)

Steve M picture Steve M · May 29, 2017 · Viewed 9.2k times · Source

I have an app that is listed as in the bottom 25% in the new Google Play Console - Android vitals section for Slow Rendering. I am concerned of this because of such articles that seem to say Google Play may penalize your app in the Play Store rankings if you fall in the bottom 25%.

However, it seems impossible to improve this metric for my app. It plays music and has a SeekBar and TextView which is updated every 250ms as any music player would. I made the minimum basic program to demonstrate:

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);
    }
}

Full project here: https://github.com/svenoaks/SlowRendering.git

When I run this program on hardware similar to the Nexus devices, I get these results for a
adb shell dumpsys gfxinfo com.example.xyz.slowrendering command:

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

This would mean almost all my frames taking >16ms to render, I guess due to the periodic nature of the updating. All other music player apps I have tested also have this Slow Rendering problem as far as I can see. I fear Google's algorithm ruining my app ranking, is there any way I can improve my score?

Answer

Tin Tran picture Tin Tran · Jun 4, 2017

The TextView in your layout is causing the problem. Because it has layout width of wrap_content, which said that it's width has to be equals to the width of the content (the text in this example). Therefore, every time you call TextView.setText an expensive measure/layout pass has to occur. Simple setting the layout_width to match_parent will solve the issue.

enter image description here

enter image description here

Here are two images what is taken from systrace, it demonstrate the work run on the UI thread in 1 frame. The top one is done with layout_width=wrap_content and the bottom one is with layout_width=match_parent.

Two following methods that i have tested will improve the frame rate:

  • If you post the runnable in shorter span like 16ms (seekBar.postDelayed(runnable, 16)), you get this smooth 60fps: enter image description here

    P/s: I am not sure why yet.

  • Use some other way to update the count value instead of inside the Runnable. Use View.postOnAnimation(Runnable) to reschedule the Runnable. The result is 60FPS for the sample project.

EDIT: two Runnable that uses postOnAnimation(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);
    }
  };