Search code examples
androidcrashandroid-roomfloating-action-button

My app crashes when i click on the Floating Action Button: Only the original thread that created a view hierarchy can touch its views


I'm building an app using Room Database. In my DetailActivity i've set up a floating action button to mark a movie as favorite. When i click on the fab the app crashes. I provide the method in which i'm having the error.

public void click (){
        if (isClicked) {
            MoviesExecutor.getInstance().diskIO().execute(new Runnable() {
                @Override
                public void run() {
                    mDb.moviesDao().removeMovie(movies.getMovieId());
                    favoriteFab.setImageResource(R.drawable.heart_favorite_border_black);

                }
            });
            isClicked = false;

        } else {MoviesExecutor.getInstance().diskIO().execute(new Runnable() {
            @Override
            public void run() {
                mDb.moviesDao().addMovie(movies);
                favoriteFab.setImageResource(R.drawable.heart_favorite_black);
            }
        });
            Toast.makeText(this, "added", Toast.LENGTH_LONG).show();
            isClicked = true;

        }

LOGS:

02-13 15:30:55.294 21789-21880/? E/AndroidRuntime: FATAL EXCEPTION: pool-2-thread-1
    Process: com.example.android.popularmovies2, PID: 21789
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
        at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
        at android.view.ViewGroup.invalidateChild(ViewGroup.java:5082)
        at android.view.View.invalidateInternal(View.java:12758)
        at android.view.View.invalidate(View.java:12722)
        at android.view.View.invalidate(View.java:12706)
        at android.widget.ImageView.setImageDrawable(ImageView.java:479)
        at android.support.design.widget.FloatingActionButton.setImageDrawable(FloatingActionButton.java:483)
        at android.support.v7.widget.AppCompatImageHelper.setImageResource(AppCompatImageHelper.java:90)
        at android.support.design.widget.FloatingActionButton.setImageResource(FloatingActionButton.java:478)
        at com.example.android.popularmovies2.DetailActivity$4.run(DetailActivity.java:152)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
        at java.lang.Thread.run(Thread.java:818)

Solution

  • The error message is very much self-explanatory, DB operations are almost guaranteed to succeed so you can just move this line:favoriteFab.setImageResource(R.drawable.heart_favorite_border_black); out of the runnable. So your code will be something like:

    public void click (){
        if (isClicked) {
            MoviesExecutor.getInstance().diskIO().execute(new Runnable() {
                @Override
                public void run() {
                    mDb.moviesDao().removeMovie(movies.getMovieId());
                }
            });
    
            favoriteFab.setImageResource(R.drawable.heart_favorite_border_black);
            isClicked = false;
    
        } else {MoviesExecutor.getInstance().diskIO().execute(new Runnable() {
            @Override
            public void run() {
                mDb.moviesDao().addMovie(movies);
            }
        });
        favoriteFab.setImageResource(R.drawable.heart_favorite_black);
        Toast.makeText(this, "added", Toast.LENGTH_LONG).show();
        isClicked = true;
    }