How to fix Slow Rendering (Android vitals)
Solution 1
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 theRunnable
. UseView.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);
}
};
Solution 2
I checked your code. Not sure if this is the actual code or if you have more to this. In any case I will draw attention to some of the Rendering issues in android.
1. OverDraw
Overdraw is where you waste GPU processing time by coloring in pixels that only get colored in again by something else. These can be common if you have added a background to your parent container layout and then the children are also added a background or if you have added a common background on you styles file for the application theme and then added backgrounds to the rest of the xml layout files you have created.
The causes for the overdraw can be anything, try checking your code for this. There is a developer tool installed in all mobile devices to check Overdraw in developer options. Here is the official documentation for overdraw.
2. View hierarchy
To render each view, Android goes through three stages:
1.measure
2.layout
3.draw
The time it takes Android to complete these stages is proportional to the number of views in your hierarchy. I see in your layout file that you have constraint layout which includes a linear layout. I don't see the use of this. Constraint layout was introduced to help developers reduce view hierarchy. Reduce the number of childs a particular layout can have. There is also a tool to help you with this. Here is the official android guide to it. Try these steps to figure out the GPU rendering issues.
Related videos on Youtube
![Steve M](https://i.stack.imgur.com/MnimI.png?s=256&g=1)
Steve M
Updated on July 18, 2022Comments
-
Steve M almost 2 years
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?
-
Steve M about 7 yearsI set layout_width to match_parent and still mostly janky frames, on both Moto X Pure and a Galaxy S8+. Though with the X Pure the 'Slow UI Thread' category has few frames and the 90th - 99th percentile frames are reduced to just over 20ms.
-
Tin Tran about 7 yearsAre u talking about the sample project or your production app ?
-
Steve M about 7 yearsJanky frames on the sample project even with match_parent
-
Steve M about 7 yearsHow did you get 60 FPS with postOnAnimation(), its not working for me. Do you mean only update the views in the postOnAnimation() runnable, and update the count value somewhere else?
-
Steve M about 7 yearsThis is dependent on the interactive governor also. On one device if you touch the screen it instantly becomes <16ms. I updated my production app with a class that sends NOP commands to keep the CPU frequency maxed out on Nexus and Pixel devicesand it halved my "slow" sessions from 10% to 5%. Flagged with orange "borderline" icon in Developer Console, instead of red icon on the tab as previously.
-
Tin Tran about 7 yearspostOnAnimation() is for posting the
Runnable
in the beginning of the next frame/VSYNC. Yes. I mean only update the view in the postOnAnimation() runnable. you can update the count value in a separateRunnable
usingview.postDelay
as you used to. -
Tin Tran about 7 yearsI can help open a Github PR if you want .
-
Tin Tran about 7 yearsI have updated the answer with the
postOnAnimation
method -
Steve M about 7 yearsIt is updating very frequently, is there anyway to do so it only updates every 250ms and still keeps it under 16ms. That frequent updates (whether 16ms or using the constant postOnAnimation) is going to drain battery and block main thread, counterproductive to goals of Android vitals.
-
Vijayadhas Chandrasekaran almost 4 years@TinTran I think you can help me on my two questions. Please review. Question 1 Question 2