Home > Mobile >  android.util.AndroidRuntimeException: Animators may only be run on Looper threads
android.util.AndroidRuntimeException: Animators may only be run on Looper threads

Time:01-21

I am getting an error in my project. What is the problem? can you help me?

android.util.AndroidRuntimeException: Animators may only be run on Looper threads

    at com.nisaefendioglu.movieapp.ui.DetailMovieActivity$addToFav$1.invokeSuspend(DetailMovieActivity.kt:65)

MyCode :

DetailMovieActivity:

class DetailMovieActivity : AppCompatActivity() {
    private lateinit var binding: ActivityDetailMovieBinding
    var b:Bundle?=null
    private lateinit var appDb : MovieDatabase
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
      binding= ActivityDetailMovieBinding.inflate(layoutInflater);
        appDb= MovieDatabase.getDatabase(this);
        setContentView(binding.root)
        b=intent.extras
        val i=b?.getString("imdbid")
        val apikey="93b3e8f8"

        ApiClient.instances.getDetailMovie(i,apikey).enqueue(object :Callback<MovieDetailData> {
            override fun onResponse(
                call: Call<MovieDetailData>,
                response: Response<MovieDetailData>
            ) {

                binding.tvType.text = response.body()?.Release
                binding.tvPlot.text=response.body()?.plot
                Glide.with(this@DetailMovieActivity).load(response.body()?.poster)
                    .into(binding.imgPoster)
                binding.imgToolbarBtnFav.setOnClickListener(){
                    addToFav(response.body());
                }

            }

            override fun onFailure(call: Call<MovieDetailData>, t: Throwable) {
                TODO("Not yet implemented")
            }

        })
        binding.imgToolbarBtnBack.setOnClickListener {
            finish()
        }

    }

    private fun addToFav(body: MovieDetailData?) {
        GlobalScope.launch(Dispatchers.IO) {
            if (body?.let { appDb.movieDao().getById(it.Title)} !=null ) {
                binding.imgToolbarBtnFav.setBackgroundResource(R.drawable.favorite_bg);
                    return@launch;
            }else{
                binding.imgToolbarBtnFav.setBackgroundResource(R.drawable.favorite_bg);
                body?.let { appDb.movieDao().insert(it) }
            }
        }

    }

}

MovieDatabase:

@Database(entities = [MovieDetailData::class],version = 2, exportSchema = false)
abstract class MovieDatabase: RoomDatabase() {

    abstract fun movieDao() : MovieDao

    companion object{

        @Volatile
        private var INSTANCE : MovieDatabase? = null

        fun getDatabase(context: Context): MovieDatabase {

            val tempInstance = INSTANCE
            if(tempInstance != null){
                return tempInstance
            }
            synchronized(this){
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    MovieDatabase::class.java,
                    "movies2"
                ).build()
                INSTANCE = instance
                return instance
            }

        }

    }
}

Hello, I am getting an error in my project. What is the problem? can you help me?

Hello, I am getting an error in my project. What is the problem? can you help me?

Hello, I am getting an error in my project. What is the problem? can you help me?

CodePudding user response:

This is the problem: .launch(Dispatchers.IO)

Dispatchers.IO is a thread pool that is completely independent from Android's Looper system that various APIs like Glide use to run callbacks in asynchronous functions. Also, many Android View-related classes must be called on the Android main thread (which also has a Looper).

When in an Activity, you should use lifecycleScope to launch your coroutines, and you should not change the dispatcher since it appropriately uses Dispatchers.Main by default.

private fun addToFav(body: MovieDetailData?) {
    lifecycleScope.launch {
        if (body?.let { appDb.movieDao().getById(it.Title)} != null) {
            binding.imgToolbarBtnFav.setBackgroundResource(R.drawable.favorite_bg) //TODO?
        }else{
            binding.imgToolbarBtnFav.setBackgroundResource(R.drawable.favorite_bg)
            body?.let { appDb.movieDao().insert(it) }
        }
    }

}

You should only use Dispatchers.IO when you are calling blocking functions.


Suggestion: I don't think you should make body nullable in this function since it cannot do anything useful with a null body. The null checks make the code more confusing. You should push the null check to the caller. Then this function can be simplified.

CodePudding user response:

you cannot use background thread to work with UI. here is solution

private fun addToFav(body: MovieDetailData?) {
        lifecycleScope.launch {
            if (body?.let { appDb.movieDao().getById(it.Title)} !=null ) {
                binding.imgToolbarBtnFav.setBackgroundResource(R.drawable.favorite_bg);
                    return@launch;
            }else{
                binding.imgToolbarBtnFav.setBackgroundResource(R.drawable.favorite_bg);
                body?.let { appDb.movieDao().insert(it) }
            }
        }

    }
  • Related