Search code examples
androidxmlkotlinoptimizationandroid-databinding

How to optimize setTint


I'm making the Formula 1 light system. Therefore I've created a grid of circles with the color #212121. When I'm trying to change the colors it gives me the error:

2021-02-17 09:41:25.186 29905-29905/com.example.ligthsouttimer I/Choreographer: Skipped 241 frames!  The application may be doing too much work on its main thread.

When I changed only one circle there were no problems but now it constantly skips all the frames where things are happining.
I'm running on my phone (samsung galaxy A40).


My xml layout file:

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginStart="25dp"
            android:layout_marginTop="50dp"
            android:layout_marginEnd="25dp"
            android:layout_marginBottom="50dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="h,3:2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:srcCompat="@drawable/blk_rectangle" />

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:orientation="vertical"
            android:padding="5dp"
            app:layout_constraintBottom_toBottomOf="@+id/imageView"
            app:layout_constraintEnd_toEndOf="@+id/imageView"
            app:layout_constraintStart_toStartOf="@+id/imageView"
            app:layout_constraintTop_toTopOf="@+id/imageView">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle11"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle21"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle31"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle41"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle51"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle12"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle22"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle32"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle42"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle52"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle13"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle23"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle33"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle43"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle53"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle14"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle24"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle34"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle44"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1">

                    <ImageView
                        android:id="@+id/circle54"
                        android:layout_width="0dp"
                        android:layout_height="0dp"
                        android:padding="5dp"
                        app:layout_constraintBottom_toBottomOf="parent"
                        app:layout_constraintDimensionRatio="w,1:1"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        app:srcCompat="@drawable/circle" />
                </androidx.constraintlayout.widget.ConstraintLayout>

            </LinearLayout>

        </LinearLayout>

        <Button
            android:id="@+id/start_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

My circle xml file:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">

    <solid
        android:color="#212121" />

</shape>

My colors xml file:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#6200EE</color>
    <color name="colorPrimaryDark">#3700B3</color>
    <color name="colorAccent">#03DAC5</color>

    <color name="black">#000000</color>
    <color name="red">#ff0000</color>
    <color name="grey">#212121</color>
</resources>

My MainActivity.kt:

package com.example.ligthsouttimer

import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.ShapeDrawable
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.databinding.DataBindingUtil
import com.example.ligthsouttimer.databinding.ActivityMainBinding
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main.view.*

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
//        setContentView(R.layout.activity_main)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.startBtn.setOnClickListener {
            DrawableCompat.setTint(circle13.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            DrawableCompat.setTint(circle14.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            Thread.sleep(1_000)
            DrawableCompat.setTint(circle23.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            DrawableCompat.setTint(circle24.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            Thread.sleep(1_000)
            DrawableCompat.setTint(circle33.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            DrawableCompat.setTint(circle34.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            Thread.sleep(1_000)
            DrawableCompat.setTint(circle43.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            DrawableCompat.setTint(circle44.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            Thread.sleep(1_000)
            DrawableCompat.setTint(circle53.drawable,ContextCompat.getColor(applicationContext, R.color.red))
            DrawableCompat.setTint(circle54.drawable,ContextCompat.getColor(applicationContext, R.color.red))

            var random_sleep = (2..3000).random().toLong()
            Thread.sleep(random_sleep)

            DrawableCompat.setTint(circle13.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle14.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle23.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle24.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle33.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle34.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle43.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle44.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle53.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
            DrawableCompat.setTint(circle54.drawable,ContextCompat.getColor(applicationContext, R.color.grey))
        }

    }
}

Solution

  • First off, your xml layout could be optimised by flattening the structure. It should be possible to place all image views inside the single ConstraintLayout and remove the intermediary LinearLayouts and ConstraintLayouts.
    That should make view inflation and layout passes quite a bit faster.


    That being said, the actual problem is this:

            var random_sleep = (2..3000).random().toLong()
            Thread.sleep(random_sleep)
    

    A call to Thead.sleep blocks the thread, meaning it can't do anything else until the time is over. This code is executed on the main thread so the entire app is frozen and unresponsive while sleeping, hence the "The application may be doing too much work on its main thread." message.


    Scheduling some code to run later can most easily be done with Handler in Android. Example:

    val delay = (2..3000).random().toLong()
    Handler(Looper.getMainLooper()).postDelayed({
        // code to run after delay
    }, delay)
    

    Removing the Thread.sleep call and placing all the setTint calls to be delayed inside that function should solve the blocking.