Error message:

Only the original thread that created a view hierarchy can touch its views.
Copy the code

The error message appears in viewrootimpl.java

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
Copy the code

The ViewRootImpl is responsible for calling the measure layout in the DecorView. The measure layout in the DecorView is iterated over the measure layout in the control tree, and any small View in the control tree is refreshed. The requestLayout method of ViewRootImpl will be called and the control tree will be refreshed

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
Copy the code

The scheduleTraversals() function draws the View and checks whether the current thread is the main thread before rendering. If it is not the main thread, it throws an exception. This limits the ability of the developer to update the UI in child threads.

The reason why the child thread did not initially report an error for the UI operation in onCreate() is that the ViewRootImp has not yet been created and has not yet been evaluated by the current thread.

When was ViewRootImp created?

Find the handleResumeActivity method by analyzing the activityThread. Java source code:

@Override public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {... final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); . if (r.window == null && ! a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) { a.mWindowAdded = true; r.mPreserveWindow = false; ViewRootImpl impl = decor.getViewRootImpl(); if (impl ! = null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (! a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } else { a.onWindowAttributesChanged(l); }}... }Copy the code

You can see that the assignment is made after calling performResumeActivity first

public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide) { if (r ! = null && ! r.activity.mFinished) { r.activity.performResume(); . }}Copy the code

Finally, we found an onResume method that calls back the life cycle.

The ViewRootImpl object is created after the onResume callback, which explains why child threads can update the UI in the onCreate method and even in the onResume method, because the ViewRootImpl object has not been created yet. There is no judgment as to whether the thread is the master thread

conclusion

The UI must be updated on the main thread to actually improve the efficiency and security of the interface and bring better fluency;

If multiple threads are allowed to update the UI, but access to the UI is unlocked, once multiple threads seize resources, the interface will be cluttered and updated, and the experience effect is self-evident;

So in Android it is mandatory to update the UI on the main thread