Android Neutron line threads can't update the UI?

Source: Internet
Author: User

Android UI access is not locked, so accessing the UI on multiple threads is not secure. So android specifies that the UI can only be accessed in the UI thread.

But is there an extreme situation? Makes it possible for us to access the UI in a child thread to make the program run? Next we'll use an example to confirm it.

To create a new project, the Activity_main.xml layout looks like this:

<?XML version= "1.0" encoding= "Utf-8"?><Relativelayoutxmlns:android= "Http://schemas.android.com/apk/res/android"Android:layout_width= "Match_parent"Android:layout_height= "Match_parent"    >    <TextViewAndroid:id= "@+id/main_tv"Android:layout_width= "Wrap_content"Android:layout_height= "Wrap_content"android:textsize= "18SP"android:layout_centerinparent= "true"        /></Relativelayout>

Very simple, just add a center TextView

The mainactivity code looks like this:

 Public classMainactivityextendsappcompatactivity {PrivateTextView Main_tv; @Overrideprotected voidonCreate (Bundle savedinstancestate) {Super. OnCreate (savedinstancestate);        Setcontentview (R.layout.activity_main); MAIN_TV=(TextView) Findviewbyid (R.ID.MAIN_TV); NewThread (NewRunnable () {@Override Public voidrun () {Main_tv.settext ("Access in Child threads");    }}). Start (); }}

It is also a very simple line that creates a child thread in the OnCreate method and makes UI access operations.

Click Run. You will find that even if you access the UI in a child thread, the program can run. The results are as follows:

Well, why did you get an error in updating the UI in a child thread before? Is it really possible to access the UI in a child thread?

First of all, this is an extreme situation, modify mainactivity as follows:

 Public classMainactivityextendsappcompatactivity {PrivateTextView Main_tv; @Overrideprotected voidonCreate (Bundle savedinstancestate) {Super. OnCreate (savedinstancestate);        Setcontentview (R.layout.activity_main); MAIN_TV=(TextView) Findviewbyid (R.ID.MAIN_TV); NewThread (NewRunnable () {@Override Public voidrun () {Try{Thread.Sleep (200); } Catch(interruptedexception e) {e.printstacktrace (); } main_tv.settext ("Access in Child threads");    }}). Start (); }}

Let the child thread sleep for 200 milliseconds and then wake up with UI access.

As a result you will find that the program has collapsed. This is the normal phenomenon. Throws a familiar exception like this:

Android.view.viewrootimpl$calledfromwrongthreadexception:only the original thread that created a view hierarchy can Touc H its Views.
At Android.view.ViewRootImpl.checkThread (viewrootimpl.java:6581)
At Android.view.ViewRootImpl.requestLayout (viewrootimpl.java:924)

......

As a developer, we should take a serious look at these exception messages, which can be used to find out why in the first case you can access the UI. Let's analyze the exception information:

First, you can tell from the following exception information

At Android. View. Viewrootimpl. Checkthread (Viewrootimpl. Java:6581)

This exception is thrown from the Checkthread method of the Android.view.ViewRootImpl.

Here, by the way, a knowledge point: Viewrootimpl is the implementation class of Viewroot.

That now follow Viewrootimpl's Checkthread method look, the source code is as follows:

void Checkthread () {    if (mthread! = Thread.CurrentThread ())        {throwNew  calledfromwrongthreadexception (                "only the original thread, created a view hierarchy can touch its V Iews. " );    }}

There are only a few lines of code, and Mthread is the main thread, which is initialized when the application is started.

From this we can draw the conclusion that:
When accessing the UI, Viewroot checks which thread is currently accessing the UI, and if it is not the primary thread, throws the following exception:

Only the original thread, created a view hierarchy can touch its Views

That doesn't seem to explain anything? Continue to see exception information

At Android. View. Viewrootimpl. Requestlayout (Viewrootimpl. Java:924)

Now look at the Requestlayout method,

@Override  Public void requestlayout () {    if (!  Mhandlinglayoutinlayoutrequest) {        checkthread ();         true ;        Scheduletraversals ();    }}

Here is also called the Checkthread () method to check the current thread, eh? There seems to be no information other than checking threads. Then point into the Scheduletraversals () method to see

void scheduletraversals () {    if (!  mtraversalscheduled) {        true;         = Mhandler.getlooper (). Getqueue (). Postsyncbarrier ();        Mchoreographer.postcallback (                null);         if (! Munbufferedinputdispatch) {            scheduleconsumebatchedinput ();        }        Notifyrendererofframepending ();        Pokedrawlockifneeded ();    }}

Notice that the second parameter of the Postcallback method is passed in much like a background task. Then go in.

Final class Implements Runnable {    @Override    publicvoid  run () {        dotraversal ()}    }

If you find it, follow the Dotraversal () method again.

 void   Dotraversal () { if   (mtraversalscheduled) {mtraversalscheduled  = false  ;        Mhandler.getlooper (). Getqueue (). Removesyncbarrier (Mtraversalbarrier);  if   (Mprofile) {Debug.startmethodtrac        ing ( "viewancestor" );        } performtraversals ();  if   (Mprofile) {Debug.stopmethodtraci            Ng ();        Mprofile  = false  ; }    }}

You can see that a performtraversals () method is called inside, and the view drawing process starts with this performtraversals method. The code of the Performtraversals method is a bit long not posted out, if continue to follow up is to learn the drawing of the view. And we now know that every visit to Ui,android will redraw the view. This is very well understood.

Analysis here, in fact, the exception information is not helpful to us, it only tells us the child thread where the access UI throws an exception.
And we think: When the UI is accessed, Viewroot calls the Checkthread method to check which thread is currently accessing the UI, and if not the UI thread throws an exception, which is fine. But why did you start to create a child thread in Mainactivity's OnCreate method to access the UI, or did the program run normally??
The only explanation is that at the time the OnCreate method was executed, Viewrootimpl was not created to check the current thread.

Then you can go into it this way. Find out where Viewrootimpl is and when it was created. Okay, keep moving.

In Activitythread, we find the Handleresumeactivity method, as follows:

Final voidhandleresumeactivity (IBinder token,BooleanClearhide,BooleanIsforward,BooleanReallyresume) {    //If We are getting ready to GC after going to the background, well//we are back active so skip it.Unschedulegcidler (); Msomeactivitieschanged=true; //TODO Push Resumeargs into the activity for considerationActivityclientrecord r =performresumeactivity (token, clearhide); if(r! =NULL) {        FinalActivity A =r.activity; //code omittedR.activity.mvisiblefromserver=true; Mnumvisibleactivities++; if(r.activity.mvisiblefromclient) {r.activity.makevisible (); }        }      //code omitted}

You can see the internal call of the Performresumeactivity method, this method to see the name is definitely callback Onresume method of the entrance, then we will follow up to see.

 Public FinalActivityclientrecord performresumeactivity (IBinder token,Booleanclearhide) {Activityclientrecord R=Mactivities.get (token); if(LOCALLOGV) SLOG.V (TAG, "performing resume of" +R+ "finished=" +r.activity.mfinished); if(r! =NULL&&!r.activity.mfinished) {//code omittedR.activity.performresume (); //code omitted    returnR;}

You can see R.activity.performresume () This line of code, follow the Performresume method, as follows:

Final void Performresume () {    performrestart ();    Mfragments.execpendingactions ();     NULL ;     false ;     // mresumed is set by the instrumentation    Minstrumentation.callactivityonresume (this);     // code omitted }

Instrumentation called the Callactivityonresume method, the Callactivityonresume source code is as follows:

 Public voidCallactivityonresume (activity activity) {activity.mresumed=true;    Activity.onresume (); if(Mactivitymonitors! =NULL) {        synchronized(msync) {Final intN =mactivitymonitors.size ();  for(inti=0; i<n; i++) {                FinalActivitymonitor am =Mactivitymonitors.get (i);            Am.match (activity, activity, activity.getintent ()); }        }    }}

Found, Activity.onresume (). This also confirms that the Performresumeactivity method is indeed the doorway to the callback Onresume method.

So now we look back handleresumeactivity method, after executing the Performresumeactivity method callback Onresume method,
Will come to this piece of code:

true ; mnumvisibleactivities+ +; if (r.activity.mvisiblefromclient) {    r.activity.makevisible ();}

Activity calls the Makevisible method, which should be what to show, follow up to explore.

void makevisible () {    if (!  mwindowadded) {        = Getwindowmanager ();        Wm.addview (Mdecor, GetWindow (). GetAttributes ());         true ;    }    Mdecor.setvisibility (view.visible);}

To add Decorview to WindowManager, it's time to focus on WindowManager's addview approach. And WindowManager is an interface, we should find WindowManager implementation class, and WindowManager implementation class is Windowmanagerimpl. This is the same as the viewroot, that is, the name is more than a impl.

Find the AddView method for Windowmanagerimpl, as follows:

@Override  Public void AddView (@NonNull view view, @NonNull viewgroup.layoutparams params) {    applydefaulttoken (params);    Mglobal.addview (view, params, mdisplay, Mparentwindow);}

It calls the Windowmanagerglobal's AddView method, and now it's locked.
Windowmanagerglobal Method of AddView:

 Public voidAddView (view view, Viewgroup.layoutparams params, display display, Window parentwindow) {//code omittedViewrootimpl Root; View Panelparentview=NULL; //code omittedRoot=NewViewrootimpl (View.getcontext (), display);        View.setlayoutparams (Wparams);        Mviews.add (view);        Mroots.add (root);    Mparams.add (Wparams); }    //because it fires off messages to start doing things    Try{root.setview (view, Wparams, Panelparentview); } Catch(RuntimeException e) {//badtokenexception or invaliddisplayexception, clean up.        synchronized(mLock) {Final intindex = findviewlocked (view,false); if(Index >= 0) {removeviewlocked (index,true); }        }        Throwe; }}

Finally, Viewrootimpl was created in the AddView method of Windowmanagerglobal.

Review the previous analysis and summarize:
Viewrootimpl was created after the Onresume method callback, and we first created the child thread in the OnCreate method and accessed the UI, at that moment, Viewrootimpl was not created to detect whether the current thread is the UI thread, So the program does not crash as can run up, and then modify the program, so that the thread sleeps for 200 milliseconds, the program will collapse. Obviously after 200 milliseconds Viewrootimpl has been created, you can perform the Checkthread method to check the current thread.

The analysis of this blog, like the title, Android Neutron line threads can not update the UI? Child threads created in the OnCreate method access UI is an extreme situation, this does not carefully analyze the source code is not known. I have recently seen a face test, only to find this.

I also learned from the abnormal information to follow up the source to find the answer, you?

This blog is starting with my CSDN blog: http://blog.csdn.net/xyh269

Android Neutron line threads can't update the UI?

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.