Respect the original reprint please specify: from Aigestudio (Http://blog.csdn.net/aigestudio) Power by aige infringement must investigate!
Artillery Zhen Lou
See this title ... It is estimated that more than n people will say I am a tease. Because a lot of friends in the study of Android (especially from the beginning after 4.0) will often see or hear people say that we update the UI in the UI thread (or the main thread) to update the UI, do not update the UI in the child thread, and the official Android also recommend that we do not directly update the UI in the non-UI thread, why? ? With the official Android Word:
"The Android UI Toolkit is not thread-safe and the view must always being manipulated on the UI thread."
Therefore, many children's shoes will have such a inertia thinking: in non-UI thread can not update ui! Since Android does not recommend that we do this, it is bound to make some restrictions on code, such as 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 Ext Ends 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 ()} catch (Interruptedexception e) { E.printstacktrace ();} Tvtext.settext ("Otherthread");}}). Start ();}}
To illustrate the situation, I'll also post 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>
When we run 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 Touc H its Views.
This is a very simple sentence, and ... I believe that every Android developer has met with a certain amount of time, Android by checking whether our current thread is a UI thread and thus throwing a custom androidruntimeexception to remind us of "only the original Thread that created a view hierarchy can touch it views "and forces the termination of the program to run, specifically in the Checkthread method of the Viewrootimpl class:
@SuppressWarnings ({"Emptycatchblock", "pointlessbooleanexpression"}) public final class Viewrootimpl implements Viewparent, View.AttachInfo.Callbacks, hardwarerenderer.hardwaredrawcallbacks { //save a lot of code ............. .... . void Checkthread () { if (mthread! = Thread.CurrentThread ()) { throw new Calledfromwrongthreadexception ( " Only the original thread, created a view hierarchy can touch its views. "); } } Save a huge amount of code .............}
That's one of the limitations Android has made to us after 4.0. The specific reason for writing this blog is from a blog post: Come, classmate, let's talk about "only in the UI main thread update view" This little thing, because I am so studious, I think a lot of friends also have a similar question: Whether the TM can update the UI in the non-UI thread? And to elicit some of my summaries of updating UI methods on non-UI threads, I decided to go through a blog before 3/4 to clear the first hurdle. First, I'll answer a few questions, including:
- Can the TM actually update the UI in a non-UI thread? Answer: Yes, of course I can.
- Are there any necessary links between the operation of the view and the life cycle of the activity? Answer: No, or vaguely, there is no definite connection.
- Is there a more convenient way to update the UI on a non-UI thread besides handler? Answer: Yes, and quite a few.
OK, here we take a look at the above code, in the thread I called Thread.Sleep (200), to let our anonymous thread paused 200ms, if ... If...... Let's get rid of it .............. What's going to happen? To try:
/** * Main interface * * @author aige {@link http://blog.csdn.net/aigestudio} * @since 2014/11/17 */public class mainactivity Ext Ends 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 point you will find that our code is correctly executed in the TM! And our TextView correctly shows the "Otherthread" Text! See these 11 English letters on the screen I believe that we have just released and sucked in the fart once again to release it .... This is the problem mentioned in the blog, and we have successfully updated the UI in a non-UI thread. In fact, the most fundamental reason is that we didn't checkthread our current thread, and I paused for a short period of time in the first code of the article through Thread.Sleep (200), why did you pause the thread for a while? What did Android secretly do behind our backs in this period of time? Why did the hundreds of-headed donkey scream at midnight? Why is the canteen condom repeated? Why are female dormitory underwear frequently stolen? Who is the serial rape sow case? The door of the old nun is knocked every night, what is a man or a ghost? What is hidden behind the accidental death of hundreds of little female dogs? Behind all this is the distortion of human nature or moral decay? Is it a sexual eruption or a thirst for helplessness? I'm sorry...... Take a pill, we said above, we can correctly update the UI in a non-UI thread in the same way as the above code without error, so there may be only one reason, that is, there is no Checkthread method to check our current thread ... However, a closer look at the call methods that call the Checkthread method shows that the TM is all related to the view creation build:
That is, once we try to build our controls, one of these methods is bound to be called, and a lot of friends will have an egg ache ... However, please do not be bound by the thinking of the Checkthread method, this time you should expand your thinking category, since the Checkthread method belongs to Viewrootimpl member method, then will it be at this point that our Viewrootimpl is not created at all? With this starting point, we re-examine all aspects of Activtythread scheduling activity life cycle, first look at the processing in the Performlaunchactivity method:
Public final class Activitythread {//Save a lot of code ..... .... Private Activity performlaunchactivity (Activityclientrecord), ....., ..... R, Intent customintent) {activityinfo ainfo = R.activityinfo; Eliminate the logic processing of packageinfo//omit the logical processing of componentname activity activity = NULL; try {java.lang.ClassLoader cl = R.packageinfo.getclassloader (); Generate an instance of the activity class from the instrumentation object activity = minstrumentation.newactivity (cl, component. GetClassName (), r.intent); Save three lines of code .....} catch (Exception e) {//Omit exception capture processing} try {Application app = R.packageinfo.makeapplic Ation (False, minstrumentation); Omit multiline unrelated code if (activity! = null) {Context AppContext = createbasecontextforactivity (R, Activit y); Charsequence title = R.activityinfo.loadlabel (Appcontext.getpackagemanager ()); Configuration CoNfig = new Configuration (mcompatconfiguration); Omit multiline unrelated 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.mcalle D = false; Minstrumentation.callactivityoncreate (activity, r.state); if (!activity.mcalled) {//Omit multiline unrelated code} r.activity = activity; R.stopped = true; /* * Call the Performstart method to process the start logic */if (!r.activity.mfinished) { Activity.performstart ();r.stopped = false; }//Save multiple lines of irrelevant code}//omit two lines of unrelated code} catch (Supernotcalledexception e) {/ /omit the capture of exception processing} catch (Exception e) {//Omit capture processing for exception} return activity; }//Save a huge amount of code ........... ...}
Performlaunchactivity method The visual wood has me the information we want, which creates the activity and dispatches the logical processing of Create and start, let's look at the Callactivityoncreate method:
public class Instrumentation {//save a lot of code .......... .......... public void callactivityoncreate (activity activity, Bundle icicle) { //omitted certain logic ... Activity.performcreate (icicle); Save some logic ... } Save a huge amount of code .............}
In addition to some of the callactivityoncreate of MQ, the Performcreate method is called by an instance of activity:
public class Activity extends Contextthemewrapper implements Layoutinflater.factory2, Window.callback, Keyevent.callback, Oncreatecontextmenulistener, ComponentCallbacks2 { //save a lot of code ............. .... . final void Performcreate (Bundle icicle) { onCreate (icicle); Mvisiblefromclient =!mwindow.getwindowstyle (). Getboolean ( Com.android.internal.r.styleable.window_ Windownodisplay, false); Mfragments.dispatchactivitycreated (); } Save a huge amount of code .............}
Performcreate method Logic is even more straightforward, the main thing is to call our activity OnCreate method, we do not find here what we want, then look at Performstart:
public class Activity extends Contextthemewrapper implements Layoutinflater.factory2, Window.callback, Keyev Ent. Callback, Oncreatecontextmenulistener, ComponentCallbacks2 {//Save a lot of code ... final void Performstart () {the. ...] ...... () () { Mfragments.notestatenotsaved (); mcalled = false; Mfragments.execpendingactions (); Minstrumentation.callactivityonstart (this); if (!mcalled) {throw new Supernotcalledexception ("Activity" + mcomponent.toshortstring () + "Did not the 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 (); }}}//Save a huge amount of code .......... ...}
Performstart has more logical processing than performcreate, but still the wood has the result we want, and it eventually calls Callactivityonstart with the instrumentation object:
public class Instrumentation {//save a lot of code .......... .......... public void Callactivityonstart (activity activity) { activity.onstart (); } Save a huge amount of code .............}
Callactivityonstart just called the activity's OnStart method, as well ... the OnStart method doesn't have the results we want ~ ~ ~ we're holding on to a free fall from the top of the Eiffel Tower continue to see the dispatch of Onresume method , which is dispatched through the handleresumeactivity in Activitythread:
Public final class Activitythread {//Save a lot of code ..... ... final void handleresumeactivity (IBinder token, Boolean clear, ...) .....???. Hide, Boolean Isforward, Boolean reallyresume) {Unschedulegcidler (); Activityclientrecord r = performresumeactivity (token, clearhide); if (r! = null) {final Activity a = r.activity; Eliminate the extraneous code ... final int forwardbit = Isforward?. windowmanager.layoutparams.soft_input_is_forward_navigation:0; Boolean willbevisible =!a.mstartedactivity; if (!willbevisible) {try {willbevisible = Activitymanagernative.getdefault (). Willactiv Itybevisible (A.getactivitytoken ()); } catch (RemoteException e) {}} if (R.window = = null &&!a.mfinished && Amp 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 (a.mvisiblefromclient) {a.mwindowadded = true; Wm.addview (decor, L); }} else if (!willbevisible) {//Omit extraneous code ... R.hidefornow = true; } cleanuppendingremovewindows (R); if (!r.activity.mfinished && willbevisible && R.activity.mdecor! = null &&!r.h Idefornow) {if (r.newconfig! = null) {//Omit extraneous code ... Performconfigur Ationchanged (r.activity, r.newconfig); Freetextlayoutcachesifneeded (r.acTivity.mCurrentConfig.diff (R.newconfig)); R.newconfig = null; }//Omit extraneous code ...... Windowmanager.layoutparams L = r.window.getattributes (); if ((L.softinputmode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) ! = forwardbit) {L.softinputmode = (L.softinputmode & (~wind OwManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) | Forwardbit; if (r.activity.mvisiblefromclient) {Viewmanager wm = A.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; Save the irrelevant code ..... Looper.myqueue (). Addidlehandler (New Idler ()); } r.onlylocalrequest = false; Omit communication processing with Activitymanager} else {///omitting the processing logic of the activity when an exception occurs}}//Save a huge amount of code ...... ...}
The Handleresumeactivity method logic is relatively complex, except for a phala of the logical judgment of the current display window and the initialization that is not created, and so on, which in the end will invoke the Makevisible method of activity:
public class Activity extends Contextthemewrapper implements Layoutinflater.factory2, Window.callback, Keyevent.callback, Oncreatecontextmenulistener, ComponentCallbacks2 { //save a lot of code ............. .... . void Makevisible () { if (!mwindowadded) { Viewmanager wm = Getwindowmanager (); Wm.addview (Mdecor, GetWindow (). GetAttributes ()); mwindowadded = true; } Mdecor.setvisibility (view.visible); } Save a huge amount of code .............}
The logic in the Makevisible method is fairly simple, get a Window Manager object and add the root view that we have mentioned in the custom control actually very simple 7/12 decorview to it, addview the specific implementation 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; Omit one line of code synchronized (mLock) { //Omit extraneous code root = new Viewrootimpl (View.getcontext (), display); Omit one line of code //omit one line of code mroots.add (root); Omit one line of code } //Omit part of the Code }}
In AddView, a Viewrootimpl object is generated and saved in the Mroots array, and every time we addview it, a Viewrootimpl object is generated. Actually see here we can also expand the question whether an app can have more than one root view? The answer is yes, because whenever I call the AddView method, the view parameter that we pass in can be considered a root view, but! There is only one root view in the default implementation of the framework, and that is the Decorview we addview in the Makevisible method above, so why we can say that an app can have multiple activity, But each activity will only have a window a decorview a viewrootimpl, see here a lot of children's shoes will still ask, that is, after the Onresume method is executed, our Viewrootimpl will be generated, right, But why the following code still works correctly:
/** * Main interface * * @author aige {@link http://blog.csdn.net/aigestudio} * @since 2014/11/17 */pu Blic 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 ();}}
Yes, it can be done! First of all, we have a thread here, and then here we have to deal with the way the framework handles UI events, and we've said in the introduction polyline of the Android page effect principle that Android's handling of UI events relies on the message Queue, When a msg is pressed into MQ to deal with the process is not immediate, it needs an event, we are in the thread through Thread.Sleep (200) in the wait, waiting for what? When the instance object of the Viewrootimpl is created, there is a message queue in the GUI processing if I have the opportunity to concentrate on the "in-depth understanding of the Android GUI framework" series, here is not to say, then the reunion asked! Nani, since Viewrootimpl has not been created, then why is it possible to draw text?!! If you have this question, I can only say that you observe carefully asked well, but, this question I do not intend to answer, left to you, above I actually teach you how to find the reason, fishing has been granted to you so no longer say ~ ~ ~ Now that we have found the reason, then how do we get rid of "the Android The UI toolkit is not thread-safe and the view must always being manipulated on the UI thread. " What about this nightmare?
Why we can update the UI in a non-UI thread