android-How to solve 'android android.view.ViewRootImpl$CalledFromWrongThreadException

1. The purpose of this post

I would demo how to solve this error when using do some UI operations in a thread:

The code(updateUI from a thread inside an activity or fragment):

public class MyActivity extends AppcompatActivity {
    private class CheckThread extends Thread {
            @Override
            public void run() {
                try {
                        updateUI();
                    }
                 catch (Exception ex) {
                    LogUtils.error("",ex);
                 }
            }
    }

    public void onCreate(...) {
        (new CheckThread()).start();
    }
    private void updateUI() {
        myView.setVisibility(View.VISIBLE);//show some view in a thread
    }
}

Run the app, we get this error:

E/MyTest: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1225)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:360)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.setFlags(View.java:14102)
        at android.view.View.setVisibility(View.java:9992)
        at com.tom.Fragment.showConfirmButton(Fragment.java:87)
        at com.tom.Presenter.onGotToken(Presenter.java:188)
        at com.tom.Presenter.access$800(Presenter.java:43)
        at com.tom.Presenter$CheckThread.run(Presenter.java:168)

2. Environments

  • Android Studio 3.x

3. Solution and Code

3.1 The basics

In Android application development, the term runOnUiThread refers to a method used to perform operations on the UI thread of an Android application. The UI thread, also known as the main thread, is responsible for updating the user interface of the application. It handles all the UI-related tasks, such as drawing views, updating layouts, and responding to user interactions.

Here’s a more detailed introduction to runOnUiThread and its role in Android development:

  1. UI Thread Importance:
    • The UI thread is a single thread that must be kept responsive to ensure a smooth user experience. Long-running operations or blocking tasks can cause the UI to freeze or become unresponsive, leading to a poor user experience.
  2. Background Processing:
    • For this reason, Android encourages developers to perform time-consuming operations, such as network requests, database operations, or complex computations, on background threads using mechanisms like AsyncTask, Thread, Executor, or Kotlin coroutines.
  3. Updating the UI from Background Threads:
    • When a background operation is completed, and it’s necessary to update the UI based on the result, developers must ensure that these updates are made on the UI thread. Directly updating UI components from a background thread can lead to unexpected behavior or crashes.
  4. The runOnUiThread Method:
    • The runOnUiThread method is a pattern used in Android to post a Runnable to the UI thread’s message queue, which will then execute the Runnable on the UI thread. This ensures thread-safe updates to the UI components.
  5. How to Use runOnUiThread:
    • The method is typically used as follows:
      myActivity.runOnUiThread(new Runnable() {
          @Override
          public void run() {
              // Code here will run on the UI thread
              TextView myTextView = findViewById(R.id.my_text_view);
              myTextView.setText("Updated text");
          }
      });
      
    • In this example, myActivity is an instance of an Activity, and the anonymous Runnable contains code that updates a TextView. This code will be safely executed on the UI thread.
  6. Alternatives to runOnUiThread:
    • With the introduction of more modern Android development practices, there are other ways to update the UI from background threads, such as using Handler, HandlerThread, or Kotlin’s withContext with coroutines. Android’s LiveData and ViewBinding also provide mechanisms to update UI components reactively and safely.
  7. Thread Safety:
    • It’s crucial to remember that any operation that directly affects the UI must be done on the UI thread. While runOnUiThread helps with this, developers should be mindful of the thread they are working on and use the appropriate mechanisms to maintain thread safety.

By using runOnUiThread or similar mechanisms, Android developers can write applications that are responsive and provide a smooth user experience, even when performing intensive background operations.

3.2 The solution

Because android system do not allow the UI operations to run in the non-UI thread, so you can change the code like this:

public class MyActivity extends AppcompatActivity {
    private class CheckThread extends Thread {
            @Override
            public void run() {
                try {
                        updateUI();
                    }
                 catch (Exception ex) {
                    LogUtils.error("",ex);
                 }
            }
    }

    public void onCreate(...) {
        (new CheckThread()).start();
    }
    private void updateUI() {
        myView.getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                myView.setVisibility(View.VISIBLE);
            }
        });
        
    }
}

Build and run the app, everything works fine.