5

In my Android vitals 30% of my users experience slow rendering. My app has a pretty complicated UI so I made a really basic project to try to solve this problem. But it turns out it's pretty slow even with the simplest of layouts.

The layout is a centered text that Android studio offers as a template:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="slowrenderingtest.pichaipls.com.slowrenderingtest.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/timerView"
        android:text="00:00"
        android:textSize="40dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

The activity that uses the layout changes the text every second (because it's a timer):

Timer updateTicks = new Timer();
updateTicks.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Random r = new Random();
                timerView.setText(String.format("%02d", r.nextInt(60))+":"+
                                    String.format("%02d", r.nextInt(60)));
            }
        });
    }
}, 100, 1000);

When I turn on GPU profiling, this is getting pretty close to the 16ms limit for drawing each frame on some slower devices (apparently 30% of my users). Just one TextView. But here's the catch: when I first run the activity the rendering times are low but then they shoot up after a few seconds. If I keep touching the screen (there are no controls on the screen), the rendering times remain low. I'm guessing this is due to the CPU/GPU going to a low power state (so rendering takes longer).

My problem is the Android vitals. I keep seeing the warning about slow rendering times (I think anything above 5% of sessions experiencing slow rendering gets a warning) but I don't know how I can speed this up. And I'm worried it may affect my app's ranking but even with this really simple example far too many Android users have slow devices.

Can anything be done about this?

TimSim
  • 3,936
  • 7
  • 46
  • 83
  • Can you try running CPU profiling and find out which method is taking more time, I bet it is `r.nextInt()` which should not be used on UI thread excessively – Akash Kava Oct 24 '17 at 07:49
  • No, it has nothing to do with `r.nextInt()'. The problem is the same if I just show an incrementing number. In fact, taxing the CPU seems to make the drawing faster. If instead of just generating a random number I instead also calculate the factorial of that number, the problem with the slow GPU updates gets solved, although in a horrible and inefficient way. – TimSim Oct 24 '17 at 12:06

2 Answers2

4

We're having the same issue is well and after some digging I suspect it's due the CPU getting idle?

Our case is similar. We have a Runnable inside which we update a TextView's text (only) every second using a Handler. The TextView resides on an item inside our RecyclerView. Interestingly, when we scroll the page, or there's some animation going on, or when interacting with the app (basically anything else other than the ticking), we get don't hit the 16ms mark. But let the device be idle and bam, each frame taking between 30-50 ms.

We tried different solutions mentioned in SO for similar situation. Like changing View's width from wrap_content to match_parent. Didn't help with the issue. We tried changing the 1000ms interval to 500, 200, 100, 50, 20, 16, 10 and only saw better results when using 16/10ms (CPU is constantly working?).

Found this article regarding CPU throttling. So probably when the CPU is idle, it's taking some more time to draw the frames?

If you look at CPU monitor in Android Studio, you'll notice CPU becomes idle as soon as nothing is happening. (Higher ones are when there's interaction, and using Nexus 5). enter image description here

But look the GPU monitor, that looks quite different from the Profile GPU Rendering bars on screen, no frames are actually crossing the 16ms mark! Not sure which GPU monitoring is the correct one. (Big yellow ones are when there's interaction). enter image description here

Ahmadul Hoq
  • 705
  • 5
  • 14
  • 1
    Yes, I have submitted an update where the textview is updated every 50ms or so. I'll see if that improves the vitals. If it does, it will be another example of Google's sometimes breathtaking incompetence. – TimSim Oct 24 '17 at 11:51
  • Yup, updating the timer 60 times a second solves the problem. Now instead of letting the phone go into low power state, it's working constantly to satisfy Google's "bad behavior" metrics. – TimSim Nov 09 '17 at 11:11
  • @TimSim we tried this approach but since it's a battery hog we came to conclusion this is not a standard approach we can fix their "bad behavior" metrics. So we decided to leave it for now. Surprisingly we are not seeing the frozen frames issue on Play Store anymore even though the code is basically the same. Probably they changed how they calculate this metrics. – Ahmadul Hoq Nov 13 '17 at 02:22
  • 1
    I wasn't talking about frozen frames but slow frame draw times. The solution I implemented is simply making the app work like all other timer apps (including those made by Google) by drawing frames 60 times a second even if once per second would be optimal. The difference in battery drain is marginal. And for anyone curious, it doesn't seem like fixing Android vitals affects downloads of the app that much, if at all. – TimSim Jan 05 '18 at 13:27
0

Try to make the textView match_parent on width...If you change the text every second, it means the TextView will need to calculate it's size each time. You will be surprised of the results...

Alexandru Circus
  • 5,478
  • 7
  • 52
  • 89