This paper will analyze the conditions and reasons that Only the original thread that created a view hierarchy can touch its views from the root.

With the conclusion, the analysis will continue:

  1. Activity#onCreate()Update using child threads inTextViewWill the app crash?
  2. ViewRootImplAfter initialization, updates can be made in child threadsTextView“?
  3. TextView.setText()Caused by thecheckThread()Only throughrequestLayout()The trigger.

checkThread()Call conditions

Only the original thread that created a view hierarchy can touch its views by checkThread().

Based on thecheckThread()Run Alt + F7 to find:

The most familiar are requestLayout() and invalidate()

The invalidate() call chain goes to invalidateChildInParent().

Analysis of theinvalidate()Special attention should be paid to:That is, with hardware acceleration on,invalidate()Will go through a special process directlyreturnDoes not callcheckThread()

If the target API level is 14 or higher, hardware acceleration is enabled by default

RequestLayout () and invalidate() without hardware acceleration will trigger checkThread().

In fact, hardware acceleration is basically never turned off, except for custom Views, when using apis that hardware acceleration doesn’t support.

checkThread()When can exceptions be thrown

First look at the source code:

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

An exception is thrown when mThread and checkThread() callers on thread B are inconsistent.

Is the mThread the main thread? Run Alt + F7 on it:

MThread initialized in ViewRootImp constructor, so mThread is ViewRootImp(…) The thread in which the caller of. Then continue Alt + F7 “will find no trace.

If you are not familiar with Viewrotimp, I will talk about Viewrotimp when I have time, starting with the Activity launch process

Let me give you the upper conclusion:

  1. AMSWill firstActivityLife cycle events are sent toApplicationThread.ApplicationThreadThe event is sent to the main thread when it is receivedHandler, i.e.,mHAnd then executeActivityThread#handleResumeActivitycompleteViewRootImpInitialization of, soViewRootImp(...)Is executed in the main thread.
  2. ViewRootImpCan trigger the drawing process, specific visibleViewRootImpInstance method of.

When checkThread() is triggered, an exception will be thrown if the thread creating the View is inconsistent with the thread updating the View content.

Analyze several practical problems

Activity#onCreate()Update using child threads inTextViewDoes the content collapse?

Scene 1:Will not collapse. At this timeViewRootImpThe instantiation is not complete, let alone the instantiation method is calledcheckThread().

Scene two:Collapse. At this timeViewRootImpIf the instance method is called while the content is being updatedcheckThread()It will collapse.

The implication is that checkThread() may not be called, as described below.

ViewRootImplAfter initialization, can I update the UI in the child thread?

Yes, the code is as follows:

The non-main thread can be found after runningnon-ui-threadYou can also update the UI in.

TextView.setText()Caused by thecheckThread()Only throughrequestLayout()The trigger?

Textview.settext () completes the UI update with checkForRelayout().

@UnsupportedAppUsage private void checkForRelayout() { // If we have a fixed width, we can just swap in a new text layout // if the text height stays the same or if the view height is fixed. if ((mLayoutParams.width ! = LayoutParams.WRAP_CONTENT || (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth)) && (mHint == null || mHintLayout ! = null) && (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) { // Static width, so try making a new text layout. int oldht = mLayout.getHeight(); int want = mLayout.getWidth(); int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth(); /* * No need to bring the text into view, since the size is not * changing (unless we do the requestLayout(), in which case it * will happen at measure). */ makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING, mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(), false); if (mEllipsize ! = TextUtils.TruncateAt.MARQUEE) { // In a fixed-height view, so use our new text layout. if (mLayoutParams.height ! = LayoutParams.WRAP_CONTENT && mLayoutParams.height ! = LayoutParams.MATCH_PARENT) { autoSizeText(); invalidate(); return; } // Dynamic height, but height has stayed the same, // so use our new text layout. if (mLayout.getHeight() == oldht && (mHintLayout == null || mHintLayout.getHeight() == oldht)) { autoSizeText(); invalidate(); return; } } // We lose: the height has changed and we have a dynamic height. // Request a new view layout using our new text layout. requestLayout(); invalidate(); } else { // Dynamic width, so we have no choice but to request a new // view layout with a new text layout. nullLayouts(); requestLayout(); invalidate(); }}Copy the code

The source code indicates that invalidate() is called instead of requestLayout() when the width and height of the TextView remain the same. Combined with the conclusion of the previous section of this article, checkThrea() is not called when hardware acceleration is enabled. So when we do Activity#onCreate(), we do a setText(…) on a TextView with a certain width and height in the child thread. The application does not crash.

The last

why

CheckThread () = checkThread(); checkThread() = checkThread();

The motivation for writing this article

The original intention that writes this article is, Guo God sent shock recently! Can Android child threads also modify the UI? “, and some of the students in the comments section raised their own questions, so I added to it, and also wanted to say that ViewRootImp can be created on any thread, not just the main thread.

Just a few more words

In the usual development process, we are basically in the main thread of the honest UPDATE UI, the reason why we will analyze all kinds of strange use cases in the article, just to let you later if you encounter such an exception at the beginning of the article, analysis more calmly.