Android still successfully updated the UI on non-UI threads for in-depth analysis. androidui
"View can only be updated in the main UI thread".
Are you familiar with this sentence?
Come on, buddy. Let's look at the following example.
@ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); TV = (TextView) findViewById (R. id. TV); Thread. currentThread (). setName ("UIThread"); new LooperThread ("Modify non-main thread "). start ();} private class LooperThread extends Thread {private String text; public LooperThread (String text) {this. text = text ;}@ Override public void run () {Thread. currentThread (). setName ("OtherThread"); TV. setText (text );}}
Isn't the code cool! It must have crashed! However, if you try it, you will find that the vast majority will not collapse. As for the very few causes of crash, I will talk about it later.
You may be confused, isn't it possible to "update the View only in the main UI thread? Why can't you update the View in the Child thread?
Then, you can read the following code again, so writing will surely crash.
Public void changeNoUI (View view) {new LooperThread ("Modify non-main thread"). start ();}
The called code is the same as the above Code, except that we set a click event for a Button and manually call it by ourselves. This will surely crash, and what error will be reported?
The following error is reported:
02-02 16:44:38.786: E/AndroidRuntime(17907): FATAL EXCEPTION: OtherThread 02-02 16:44:38.786: E/AndroidRuntime(17907): Process: com.example.demo, PID: 17907 02-02 16:44:38.786: E/AndroidRuntime(17907): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6226) 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:883) 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.view.ViewGroup.invalidateChild(ViewGroup.java:4320) 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.view.View.invalidate(View.java:10947) 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.view.View.invalidate(View.java:10902) 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.widget.TextView.checkForRelayout(TextView.java:6673) 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.widget.TextView.setText(TextView.java:3860) 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.widget.TextView.setText(TextView.java:3718) 02-02 16:44:38.786: E/AndroidRuntime(17907): at android.widget.TextView.setText(TextView.java:3693) 02-02 16:44:38.786: E/AndroidRuntime(17907): at com.example.demo.MainActivity$LooperThread.run(MainActivity.java:78)
That is to say, only the thread that creates the View hierarchy can modify the View. We have updated the View in a non-UI main thread, so an error is reported.
However, you haven't said why the first method is called. Why don't you report an error !!!
Don't worry. Let's take a look at the error message above. There are many things to study!
Let's start from the following: the first is LooperThread. run (): Why is the error reported? Go up, because we have called setText, and then go up to TextView. checkForRelayout, and then invalidate. we modified the text. We must use invalidate. Who called it? It turned out to be ViewGroup. invalidateChild. I searched for it and finally found the culprit. ViewRootImpl. checkThread () reported an error!
What is ViewRootImpl? This is very powerful. Now you only need to know that it can do a lot of interface-related things. In fact, I only know a little about this class...
If checkThread () does anything, an error is returned! ViewRootImpl is a hidden class. We can only look at the source code. The source code is as follows:
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
This method is used to report an error! ViewRootImpl is attached to the AttachInfo class, while AttachInfo is a hidden class of View, which you cannot see in Eclipse. You need to directly View the source code of the framework. Therefore, the Thread. currentThread () Here is the main UI Thread, which is used to determine whether we have modified the VIew on the non-main UI Thread.
The problem arises again. Why is the first method? No error is reported !!!
OK. Don't go crazy. Let's review the lifecycle of the Activity first. Depend! How is the lifecycle of the Activity related?
The Activity prepares data on the interface in onCreate. After onStart (), the interface of the Activity is visible to the user. So what is the purpose of this? Of course! The difference between the above two call methods is that the call time is different! One is to enable thread call in onCreate, and the other is to manually call, suggesting that the second method is to call after onResume!
The difference in the call time determines the significance of setText in the code!
In the first method, although it is setText in the Child thread, the View has not been drawn yet, so the invalidate after the call is not made, which is equivalent to setting only an attribute of TextView and not invalidate, there will be no subsequent method calls. In the final analysis, the checkThread of ViewRootImpl will not be called, and no error will be reported. In the second method, after setText is called, a series of method calls will be triggered. The VIew should refresh the interface, the ViewGroup should update the layout, calculate the size and position of the sub-View, and finally, viewRootImpl will be checkThread, And it will crash.
Therefore, strictly speaking, although the first method sets the View attribute in the sub-thread, it cannot be attributed to the "Update View" category, because it has not been drawn yet, there is no such update.
So why do we say that the vast majority will not report errors? This is because, after onCreate enables the Sub-Thread, the sub-Thread executes the time-consuming operation, such as Thread. sleep, the sub-thread will crash when it calls setText again, because onStart and onResume are executed at this time, and you want to update the View in the sub-thread, so there is no door!
I don't know if I have the same idea: I remember that Toast will also report an error in the Child thread, and I can add lorule. prepare and lorule. loop. Can I do this here?
Of course, the answer is no.
Toast and View are essentially different. Toast reports an error in the subthread because the display of Toast needs to be added to a MessageQueue, then logoff is obtained and sent to Handler for display, because the sub-thread does not have a logoff, you need to add the logoff. prepare and logoff. loop creates a Looper, but in essence, this is still called in the sub-thread, so an error will still be reported!
Why does Android require that the View be changed only in the main UI thread? This is about the Single-thread model of Android. If multi-thread View modification is supported, the thread synchronization and thread security problems will be very complicated, therefore, the Android system is fixed, and View operations must be performed on the UI thread, which simplifies the system design.
OK. I hope the above will help you.