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 aadb 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?
Android vitals is an initiative by Google to improve the stability and performance of Google Play apps on Android devices. When an opted-in user runs your app, their Android device logs information about aspects of quality including stability metrics, performance metrics, battery usage, and permission denials.
If your app suffers from slow UI rendering, then the system is forced to skip frames and the user will perceive stuttering in your app. We call this jank. To help you improve app quality, Android automatically monitors your app for jank and displays the information in the Android vitals dashboard.
Most Android devices refresh the screen 60 times a second (there are undoubtedly exceptions—earlier Android devices were sometimes in the 50 or less fps range). Because the screen is refreshed every 16 ms (1 s/60 fps = 16 ms per frame), it is crucial to ensure that all of your rendering can occur in less than 16 ms.
Android Studio needs at least 8 GB RAM to run better. Change your Hard Disk to SSD. Loading/Compiling/Designing/Writing time will be reduced even in 4GB RAM. Use Power Save mode from File Menu that will reduce lots of background work.
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.
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:
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);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With