This evening by the younger brother told him to update the UI in the child thread, asked if I was the version of the problem, I am decisive that his code is wrong, not too much minutes to face, after my careful exploration finally found the reason, perhaps the outcome of this matter is not how important, but I think the process of exploration has some reference value.
First of all, to meet this problem subconsciously is to Google, so I took the following measures (please ignore my offensive English, I believe Google's strong ...)
But I found that I didn't get the results I wanted, and most of the answers were to tell me how to update the UI in a sub-thread to the main thread, okay, I shouldn't have used the number, so I did the following.
Sadly, Google feels that I am expressing a meaning ... (May be my English is too poor, please do not tell me this fact), no way, can only own the battle, thanks to Google search, just let yourself have the experience of this exploration!
First, we take a look at the code, the meaning of the code is very simple, unexpectedly, it runs correctly, and in the mobile interface is displayed changed, which breaks our understanding of the non-main thread can not update the UI
Public class mainactivity extends appcompatactivity { @Override protected void onCreate(Bundle savedinstancestate) {Super. OnCreate (Savedinstancestate); Setcontentview (R.layout.activity_main);FinalTextView TV = (TextView) Findviewbyid (r.id.tv_test);NewThread (NewRunnable () {@Override Public void Run() {Tv.settext ("Changed"); }}). Start (); }}
- And then I realized that this problem might be the same as the one I met before in the OnCreate directly get the view of the wide height can not get the correct value, by something delayed loading factors, in order to verify my idea, I ran the following code
Public class mainactivity extends appcompatactivity { @Override protected void onCreate(Bundle savedinstancestate) {Super. OnCreate (Savedinstancestate); Setcontentview (R.layout.activity_main);FinalTextView TV = (TextView) Findviewbyid (r.id.tv_test);NewThread (NewRunnable () {@Override Public void Run() {Try{Thread.Sleep ( the); }Catch(Interruptedexception e) {E.printstacktrace (); } tv.settext ("Changed"); }}). Start (); }}
正确的错误Finally appeared, please take a look at the happy long-lost mistake
Then we'll explore what's going on tv.setText("Changed"); inside, keep following up on the internal methods, we'll go into this method, we'll notice that eventually it will call the invalidate () method to repaint, which is very much in line with the natural logic, so we'll go to discover what's done in invalidate ().
/** * Check Whether entirely new text requires a new view layout * or merely a new text layout. */ 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. intOLDHT = Mlayout.getheight ();intwant = Mlayout.getwidth ();intHintwant = Mhintlayout = =NULL?0: Mhintlayout.getwidth ();/* * No need to bring the text into view, since the size was not * changing (unless we do the R Equestlayout (), in which case it * would happen at measure). */Makenewlayout (Want, Hintwant, unknown_boring, unknown_boring, mright-mleft-getcompoundpadding Left ()-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.MATC H_parent) {invalidate ();return; }//Dynamic height, but height has stayed the same, //So with our new text layout. if(mlayout.getheight () = = Oldht && (mhintlayout = =NULL|| Mhintlayout.getheight () = = OLDHT) {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 had no choice but to request a new //view layout with a new text layout.Nulllayouts (); Requestlayout (); Invalidate (); } }
- In the same way, I follow the code step-by go to the following method (when browsing the code we have to pay attention to what our purpose is, we are looking for where to determine whether in the main thread), please follow
p.invalidateChild(this, damage); This code, p is a viewparent, Familiar with the view drawing process of the small partners see Viewparent will suddenly realize that the famous Viewrootimpl is Viewparent sub-class, so we go directly to Viewrootimpl search Invalidatechild method
voidInvalidateinternal (intLintTintRintBBooleanInvalidatecache,BooleanFullinvalidate) {if(Mghostview! =NULL) {Mghostview.invalidate (true);return; }if(Skipinvalidate ()) {return; }if(Mprivateflags & (Pflag_drawn | pflag_has_bounds) = = (Pflag_drawn | Pflag_has_bounds) | | (Invalidatecache && (mprivateflags & pflag_drawing_cache_valid) = = Pflag_drawing_cache_valid) | | (Mprivateflags & pflag_invalidated)! = pflag_invalidated | | (Fullinvalidate && isopaque ()! = Mlastisopaque)) {if(fullinvalidate) {mlastisopaque = Isopaque (); Mprivateflags &= ~pflag_drawn; } mprivateflags |= Pflag_dirty;if(Invalidatecache) {mprivateflags |= pflag_invalidated; Mprivateflags &= ~pflag_drawing_cache_valid; }//Propagate The damage rectangle to the parent view. FinalAttachinfo ai = mattachinfo;FinalViewparent p = mparent;if(P! =NULL&& ai! =NULL&& L < R && T < b) {FinalRect damage = Ai.mtmpinvalrect; Damage.set (L, T, R, b); P.invalidatechild ( This, damage); }//Damage the entire projection receiver, if necessary. if(Mbackground! =NULL&& mbackground.isprojected ()) {FinalView receiver = Getprojectionreceiver ();if(Receiver! =NULL) {receiver.damageinparent (); } }//Damage The entire isolatedzvolume receiving this view ' s shadow. if(ishardwareaccelerated () && GetZ ()! =0) {damageshadowreceiver (); } }
- In Viewrootimpl, the Invalidatechild method calls the following method, it is gratifying that we finally found, please pay attention to the first code in the function
checkThread() , click to see the implementation of this function found that he did the things we are familiar with, Familiar with the code familiar with the error message, to this all the truth, to check whether the current thread is the main thread of logic in the Viewrootimpl method, familiar with the view drawing process of the small partner must know that Viewrootimpl is in the Onresume method to create, so say, The UI can be updated in a child thread as long as it is before the Onresume method call.
@Override PublicViewparentinvalidatechildinparent(int[] location, Rect dirty) {checkthread ();if(Debug_draw) LOG.V (TAG,"Invalidate Child:"+ dirty);if(Dirty = =NULL) {invalidate ();return NULL; }Else if(Dirty.isempty () &&!misanimating) {return NULL; }if(Mcurscrolly! =0|| Mtranslator! =NULL) {Mtemprect.set (dirty); Dirty = Mtemprect;if(Mcurscrolly! =0) {Dirty.offset (0,-mcurscrolly); }if(Mtranslator! =NULL) {Mtranslator.translaterectinappwindowtoscreen (dirty); }if(mattachinfo.mscalingrequired) {Dirty.inset (-1, -1); }} invalidaterectonscreen (dirty);return NULL; }
void checkThread() { if (mThread != Thread.currentThread()) { thrownew CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
This exploration process has given me a great revelation, meet the problem, when necessary first to recall the similar problems encountered before, and reasonable use of online search information to explore the truth of their own problems, a reasonable assumption based on key information will make us more effective, and pay attention not blindly believe some of the online conclusions, The paper came to the end of shallow, I know this matter to preach!
Non-UI thread update ui!?