Why can we update the UI in a non-UI thread?

Source: Internet
Author: User

Why can we update the UI in a non-UI thread?

Respect Original reprinted Please note: From AigeStudio (http://blog.csdn.net/aigestudio) Power by Aige infringement must be investigated!

ArtilleryTown Building

See this title ...... It is estimated that N people will say that I am more funny ............ Because many friends often see or hear people say that we need to update the UI in the UI thread (or the main thread) when learning Android (especially when getting started after 4.0) to update the UI, do not update the UI in the Child thread, and the Android official also recommends that we do not directly update the UI in a non-UI thread. Why? The official Android statement is:

"The Android UI toolkit is not thread-safe and the view must always be manipulated on the UI thread ."

Therefore, many children's shoes have such an inertial thinking: the UI cannot be updated in non-UI threads! Since Android does not recommend that we do this, it will certainly impose some restrictions on our code. For example, when we try to run the following code:

/*** Main interface *** @ author Aige {@ link http://blog.csdn.net/aigestudio} * @ since 2014/11/17 */public class MainActivity extends Activity {private TextView tvText; @ Overridepublic void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); tvText = (TextView) findViewById (R. id. main_ TV); new Thread (new Runnable () {@ Overridepublic void run () {try {Thread. sleep (200);} catch (InterruptedException e) {e. printStackTrace ();} tvText. setText ("OtherThread ");}}). start ();}}
To describe the situation, I also paste the xml layout file code here:

<!-- http://blog.csdn.net/aigestudio --><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:background="#ffffff"    android:layout_height="match_parent" >    <TextView        android:id="@+id/main_tv"        android:layout_width="wrap_content"        android:layout_height="wrap_content" /></LinearLayout>
After running the above Code, you will get the following error message in Logcat:

Android. view. ViewRootImpl $ CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

This sentence is very simple, and ...... I believe that every user who has been developing Android for a certain period of time has met, android sends a custom AndroidRuntimeException by checking whether our current thread is a UI thread to remind us that "Only the original thread that created a view hierarchy can touch its views" and forces the program to be terminated., the specific implementation is in the checkThread method of the ViewRootImpl class:

@ SuppressWarnings ({"EmptyCatchBlock", "PointlessBooleanExpression"}) public final class ViewRootImpl implements ViewParent, View. attachInfo. callbacks, HardwareRenderer. hardwareDrawCallbacks {// saves massive amounts of code .............................. Void checkThread () {if (mThread! = Thread. currentThread () {throw new CalledFromWrongThreadException ("Only the original thread that created a view hierarchy can touch its views. ") ;}}// saves a lot of code ........................}
This is a restriction that Android made to us after 4.0. The specific reason for writing this Blog is from a Blog post by kaizige: Come and come, let's discuss the little thing that we can only update the View in the main UI thread, I think about it. Maybe many basin friends have similar questions: Can TM update the UI in non-UI threads? At the same time, in order to give me some summary about how to update the UI method in non-UI threads, I decided to write a Blog before 3/4 to clear the obstacles first. First, I will answer a few questions:

Okay, let's take a look at the above Code. In the Thread, I called Thread. sleep (200); to suspend our anonymous Thread for 200 ms. If ...... Suppose that ...... Let's remove it ........................ What will happen? To try:

/*** Main interface *** @ author Aige {@ link http://blog.csdn.net/aigestudio} * @ since 2014/11/17 */public class MainActivity extends Activity {private TextView tvText; @ Overridepublic void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); tvText = (TextView) findViewById (R. id. main_ TV); new Thread (new Runnable () {@ Overridepublic void run () {tvText. setText ("OtherThread ");}}). start ();}}
At this time, you will find that our code TM is correctly executed! In addition, our TextView correctly displays the "OtherThread" text! Seeing these 11 English letters on the screen, I believe that everyone has just put out and sucked in again ............ This is the problem mentioned in kaizige's Blog. We have successfully updated the UI in a non-UI thread. In fact, the most fundamental reason here is that we do not have checkThread for our current Thread, and I used Thread in the code at the beginning of the article. sleep (200) paused for a short period of time. Why does the thread be paused for a period of time? What have we done behind Android's back-to-back? Why are hundreds of donkeys screaming in the middle of the night? Why have the sales department's condoms been hacked? Why are female dormitory underpants frequently stolen? What is the case of serial sow rape? Old nun's door was knocked every night. Is it a ghost? What is behind the accidental death of hundreds of puppies? Is the distortion of human nature or the loss of morality behind all this? Is it a sexual outbreak or hunger? Sorry ...... As mentioned above, we can correctly update the UI in a non-UI thread without reporting an error. There may be only one reason, that is, the checkThread method is not executed to check our current thread ...... However, if you look at the call methods that call the checkThread method, you will find that the TM is all related to the creation and generation of View:


That is to say, once we try to generate our controls, one of these methods will inevitably be called. At this time, many friends will suffer ............ However, please do not be bound by the thinking of the checkThread method. At this time, you should expand your thinking scope. Since the checkThread method belongs to the member method of ViewRootImpl, so will our ViewRootImpl be not created at this time? With this starting point, we will review all aspects of the ActivtyThread scheduling Activity lifecycle. First, let's look at the processing in the javasmlaunchactivity method:

Public final class ActivityThread {// saves massive amounts of code .............................. Private Activity initialize mlaunchactivity (ActivityClientRecord r, Intent customIntent) {ActivityInfo aInfo = r. activityInfo; // Save the logical processing of packageInfo // Save the logical processing of ComponentName Activity = null; try {java. lang. classLoader cl = r. packageInfo. getClassLoader (); // use the Instrumentation object to generate an instance of the Activity class activity = mInstrumentation. newActivity (cl, component. getClassName (), r. intent); // saves three lines of code ............} Catch (Exception e) {// catch and handle exceptions are omitted} try {Application app = r. packageInfo. makeApplication (false, mInstrumentation); // saves multiple lines of irrelevant code if (activity! = Null) {Context appContext = createBaseContextForActivity (r, activity); CharSequence title = r. activityInfo. loadLabel (appContext. getPackageManager (); Configuration config = new Configuration (mCompatConfiguration); // saves multiple lines of irrelevant code if (customIntent! = Null) {activity. mIntent = customIntent;} r. lastNonConfigurationInstances = null; activity. mStartedActivity = false; int theme = r. activityInfo. getThemeResource (); if (theme! = 0) {activity. setTheme (theme);}/** call the callActivityOnCreate method to process the Create logic */activity. mCalled = false; mInstrumentation. callActivityOnCreate (activity, r. state); if (! Activity. mCalled) {// ignore multiple lines of unrelated code} r. activity = activity; r. stopped = true;/** call the Start mstart method to process the Start logic */if (! R. activity. mFinished) {activity. optional mstart (); r. stopped = false;} // saves multiple lines of irrelevant code} // saves two lines of irrelevant code} catch (SuperNotCalledException e) {// capture and handle exceptions} catch (Exception e) {// capture and handle exceptions} return activity ;} // saves a lot of code ........................}
The Visual View of the rabbitmlaunchactivity method contains the information we want. It creates the Activity and schedules the logical processing of Create and Start. What about the callActivityOnCreate method:

Public class Instrumentation {// saves massive amounts of code .............................. Public void callActivityOnCreate (Activity activity, Bundle icicle) {// save some logic ...... Activity. Sort mcreate (icicle); // save some logic ...... } // Saves a lot of code ........................}
In callActivityOnCreate, in addition to scheduling MQ, the most important thing is that the performCreate method is called through the Activity instance:

Public class Activity extends ContextThemeWrapper implements LayoutInflater. Factory2, Window. Callback, KeyEvent. Callback, OnCreateContextMenuListener, ComponentCallbacks2 {// saves massive amounts of code .............................. Final void initialize mcreate (Bundle icicle) {onCreate (icicle); mVisibleFromClient =! MWindow. getWindowStyle (). getBoolean (com. android. internal. r. styleable. window_windowNoDisplay, false); mFragments. dispatchActivityCreated ();} // saves a lot of code ........................}
The logic of the performCreate method is even more simple. The most important thing is to call the onCreate method of our Activity. We didn't find what we wanted here, so let's look at javasmstart:

Public class Activity extends ContextThemeWrapper implements LayoutInflater. Factory2, Window. Callback, KeyEvent. Callback, OnCreateContextMenuListener, ComponentCallbacks2 {// saves massive amounts of code .............................. Final void initialize mstart () {mFragments. noteStateNotSaved (); mCalled = false; mFragments.exe cPendingActions (); mInstrumentation. callActivityOnStart (this); if (! MCalled) {throw new SuperNotCalledException ("Activity" + mComponent. toShortString () + "did not call through to super. onStart () ");} mFragments. dispatchStart (); if (mAllLoaderManagers! = Null) {final int N = mAllLoaderManagers. size (); LoaderManagerImpl loaders [] = new LoaderManagerImpl [N]; for (int I = N-1; I> = 0; I --) {loaders [I] = mAllLoaderManagers. valueAt (I) ;}for (int I = 0; I <N; I ++) {LoaderManagerImpl lm = loaders [I]; lm. finishRetain (); lm. doReportStart () ;}}// saves a lot of code ........................}
Compared with mongomcreate, mongomstart has more logic processing, but still has the desired results. In the end, it is the same as calling callActivityOnStart by the Instrumentation object:

Public class Instrumentation {// saves massive amounts of code .............................. Public void callActivityOnStart (Activity activity) {activity. onStart () ;}// saves massive amounts of code ........................}
CallActivityOnStart only calls the onStart method of the Activity ...... The onStart method does not have any expected results ~~~~ We will continue to look at the onResume method Scheduling with the mentality of making a free fall from the top of the Eiffel Tower. In ActivityThread, we use handleResumeActivity scheduling:

Public final class ActivityThread {// saves massive amounts of code .............................. Final void handleResumeActivity (IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {unscheduleGcIdler (); ActivityClientRecord r = descrimresumeactivity (token, clearHide); if! = Null) {final Activity a = r. activity; // skip irrelevant code ............ Final int forwardBit = isForward? WindowManager. LayoutParams. SOFT_INPUT_IS_FORWARD_NAVIGATION: 0; boolean willBeVisible =! A. mStartedActivity; if (! WillBeVisible) {try {willBeVisible = ActivityManagerNative. getDefault (). willActivityBeVisible (. getActivityToken ();} catch (RemoteException e) {}} if (r. window = null &&! A. mFinished & willBeVisible) {r. window = r. activity. getWindow (); View decor = r. window. getDecorView (); decor. setVisibility (View. INVISIBLE); ViewManager wm =. getWindowManager (); WindowManager. layoutParams l = r. window. getAttributes ();. mDecor = decor; l. type = WindowManager. layoutParams. TYPE_BASE_APPLICATION; l. softInputMode | = forwardBit; if (. mVisibleFromClient) {. mWindowAdded = true; wm. a DdView (decor, l) ;}} else if (! WillBeVisible) {// saves irrelevant code ............ R. hideForNow = true;} cleanUpPendingRemoveWindows (r); if (! R. activity. mFinished & willBeVisible & r. activity. mDecor! = Null &&! R. hideForNow) {if (r. newConfig! = Null) {// ignore irrelevant code ............ Specified mconfigurationchanged (r. activity, r. newConfig); freeTextLayoutCachesIfNeeded (r. activity. mCurrentConfig. diff (r. newConfig); r. newConfig = null;} // saves irrelevant code ............ WindowManager. LayoutParams l = r. window. getAttributes (); if (l. softInputMode & WindowManager. LayoutParams. SOFT_INPUT_IS_FORWARD_NAVIGATION )! = ForwardBit) {l. softInputMode = (l. softInputMode &(~ WindowManager. layoutParams. SOFT_INPUT_IS_FORWARD_NAVIGATION) | forwardBit; if (r. activity. mVisibleFromClient) {ViewManager wm =. getWindowManager (); View decor = r. window. getDecorView (); wm. updateViewLayout (decor, l) ;}} r. activity. mVisibleFromServer = true; mNumVisibleActivities ++; if (r. activity. mVisibleFromClient) {r. activity. makeVisible () ;}} if (! R. onlyLocalRequest) {r. nextIdle = mNewActivities; mNewActivities = r; // skip irrelevant code ............ Logoff. myQueue (). addIdleHandler (new Idler ();} r. onlyLocalRequest = false; // communication processing with ActivityManager is omitted} else {// The processing logic of Activity is omitted when an exception occurs} // a huge amount of code is omitted ........................}
The logic of the handleResumeActivity method is relatively complicated. In addition to the logic judgment of the current display Window and the initialization that has not been created, the handleResumeActivity method will eventually call the makeVisible method of the Activity:

Public class Activity extends ContextThemeWrapper implements LayoutInflater. Factory2, Window. Callback, KeyEvent. Callback, OnCreateContextMenuListener, ComponentCallbacks2 {// saves massive amounts of code .............................. Void makeVisible () {if (! MWindowAdded) {ViewManager wm = getWindowManager (); wm. addView (mDecor, getWindow (). getAttributes (); mWindowAdded = true;} mDecor. setVisibility (View. VISIBLE);} // saves a lot of code ........................}
In the makeVisible method, the logic is quite simple. Get a window manager object and add the Root View DecorView we mentioned in the custom control actually very simple 7/12 to it, the specific implementation of addView is in WindowManagerGlobal:

Public final class WindowManagerGlobal {public void addView (View view, ViewGroup. layoutParams params, Display display, Window parentWindow) {// save a lot of code ViewRootImpl root; // save a line of code synchronized (mLock) {// save unrelated code root = new ViewRootImpl (view. getContext (), display); // save one line of code mRoots. add (root); // save a line of code} // save some code }}
In addView, A ViewRootImpl object is generated and saved in the mRoots array. Every time we addView, A ViewRootImpl object is generated, in fact, we can also extend the question: Can an APP have multiple root views? The answer is yes, because as long as I call the addView method, the View parameter we pass in can be considered as a root View,! There is only one root view in the default implementation of the framework, that is, the DecorView in addView in the makeVisible method above. So why can we say that an APP can have multiple activities, however, each Activity only has one Window, one DecorView, and one ViewRootImpl. Many children's shoes will still ask, that is, after the onResume method is executed, our ViewRootImpl will be generated, right, but why can the following code still run correctly:

/*** Main interface *** @ author Aige {@ link http://blog.csdn.net/aigestudio} * @ since 2014/11/17 */public class MainActivity extends Activity {private TextView tvText; @ Overridepublic void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); tvText = (TextView) findViewById (R. id. main_ TV) ;}@ Overrideprotected void onResume () {super. onResume (); new Thread (new Runnable () {@ Overridepublic void run () {tvText. setText ("OtherThread ");}}). start ();}}
That's right. You can execute it! First, we have a thread. Second, we need to involve the way framework handles UI events, we have mentioned in the implementation of the principle of the Android paging effect that the Android UI event processing relies on Message Queue. When a Msg is pushed to MQ, the process is not immediate, it requires an event. We use Thread in the online program. sleep (200) is waiting. What is waiting? When the ViewRootImpl instance object is created, the Message Queue processing in the GUI will be concentrated in the "deep understanding of the Android GUI framework" series if I have the opportunity, so I have another question! Nanni, since ViewRootImpl has not been created, why can the text be painted ?!!! If you have this question, I can only say that you have carefully asked this question. However, I am not going to answer this question and leave it to you. In fact, I am teaching you how to find the cause, yu has been granted to you, so I will not talk about it anymore ~~~~ Since we have found The cause, how can we get rid of the nightmare of "the Android UI toolkit is not thread-safe and The view must always be manipulated on the UI thread?

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.