In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces "what is the principle of threading and updating UI". In daily operation, I believe that many people have doubts about what is the principle of threading and updating UI. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the doubts of "what is the principle of threading and updating UI?" Next, please follow the editor to study!
In case 1, the subthread updates the button text
1) the onCreate method updates the button display text, changing the width of the Button to fixed or wrap_content, will not collapse.
/ / or
Override fun onCreate (savedInstanceState: Bundle?) {
Super.onCreate (savedInstanceState)
SetContentView (R.layout.activity_ui)
Thread {
Btn_ui.text= "Young people should talk about martial arts"
}
}
2) the button display text is updated and the delay is added in the onCreate method.
The width of the Button is fixed without collapse. The width of the Button is wrap_content, and the crash error is Only the original thread that created a view hierarchy can touch its views.
/ / or
Override fun onCreate (savedInstanceState: Bundle?) {
Super.onCreate (savedInstanceState)
SetContentView (R.layout.activity_ui)
Thread {
Thread.sleep (3000)
Btn_ui.text= "Young people should talk about martial arts"
}
}
Case one analysis
I feel a little confused, don't panic, let's take a look at the crash message.
The crash occurs when the button width is wrap_content, that is, the width is set according to the content, and then the button text is updated 3 seconds later. In contrast, there are two crash impact points to be aware of:
Width wrap_content. If it is set to a fixed value, it will not collapse, as shown in case 2, so does it have anything to do with the logic of layout change? Delay 3 seconds. Even wrap_content won't crash without delay, as shown in case 1, so does it have anything to do with the loading progress of some classes?
Take these questions to the source code to find answers. Take a look at the crash log first:
Android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
At android.view.ViewRootImpl.checkThread (ViewRootImpl.java:9219)
At android.view.ViewRootImpl.requestLayout (ViewRootImpl.java:1600)
At android.view.View.requestLayout (View.java:24884)
You can see that there is an error when checking threads in ViewRootImpl's requestLayout, so let's take a look at this method:
@ Override
Public void requestLayout () {
If (! mHandlingLayoutInLayoutRequest) {
CheckThread ()
MLayoutRequested = true
ScheduleTraversals ()
}
}
Void checkThread () {
If (mThread! = Thread.currentThread ()) {
Throw new CalledFromWrongThreadException (
"Only the original thread that created a view hierarchy can touch its views."
}
}
Before we solve the mystery, let's take a look at ViewRootImpl.
ViewRootImpl
From the creation of Activity to the time we see the interface, it actually goes through two processes: loading layout and drawing.
Load layout
Loading the layout is actually our commonly used setContentView (int layoutResID) method, which mainly does a new DecorView, then loads different root layout files according to the theme (theme) or Feature set by the activity, and finally loads the layoutResID resource file. In order to make it easier for you to understand, I drew a picture:
Load layout flow
The final step here is to call the inflate () method of LayoutInflater, which only does one thing, parses the xml file, and then generates the view object based on the node. Finally, a complete DOM structure is formed, which returns the top-most root layout View. (DOM is a document object model whose hierarchical structure is that all elements except the top-level elements are included in other element nodes, which is a bit like a family tree structure, typically html code parsing.)
At this point, a DecorView with a complete view structure has been created, but it has not yet been drawn or displayed on the phone interface.
Draw
The drawing process takes place in handleResumeActivity. Friends who are familiar with the app startup process should know that the handleResumeActivity method is used to trigger the onResume method, and the DecorView drawing is also completed here. Here's another picture:
Summary of drawing process
From this, we can draw some conclusions:
1) setContentView is used to create a new DecorView and load the resource file for the layout.
2) after the onResume method, a new ViewRootImpl is created to measure, lay out and draw the DecorView as the parent of the DecorView.
3) as the only subclass of Window, PhoneWindow stores DecorView variables and manages them, which belongs to the middle layer of the interaction between Activity and View.
Analysis crash
Okay. Come back and look at the cause of the crash:
Void checkThread () {
If (mThread! = Thread.currentThread ()) {
Throw new CalledFromWrongThreadException (
"Only the original thread that created a view hierarchy can touch its views."
}
}
You can see that this is because the current thread currentThread crashes when it is not mThread, and the error reported is "only the original thread that creates the view hierarchy can touch its view". Have you guessed here that this mThread is the "original thread that created the view"?
By looking up, the mThread is actually assigned when the ViewRootImpl is created:
Public ViewRootImpl (Context context, Display display) {
MThread = Thread.currentThread ()
}
From the above analysis of the Activity loading layout process, we know that ViewRootImpl instantiation occurs after onResume and is used to draw DecorView to window.
So we can see that the real cause of the crash is that the current thread will crash if it is not the one that was created by ViewRootImpl. The translation is quite accurate. Only the original thread that created the view can modify the view, which sounds reasonable. I have the right to change you only when I create you.
Then take a look at the previous case:
In case 1, when you modify Button in onCreate, you are only modifying DecorView, but you did not create a ViewRootImpl, so you did not get to the checkThread method, of course, it will not crash. ViewRootImpl is created after onResume.
In case 2, the interface is drawn after a delay of 3 seconds, and the creation of ViewRootImpl is obviously completed in the main thread, so mThread is the main thread, while the thread of Button is changed to child thread, so the setText method will trigger the requestLayout method to redraw, resulting in a crash.
But why doesn't the width of Button crash when it is set to a fixed value? Won't you execute the checkThread method? strange.
Looking at the source code of setText, you can find that one way is to check if a new layout is needed-- checkForRelayout ()
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)) {
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.MATCH_PARENT) {
AutoSizeText ()
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 have no choice but to request a new
/ / view layout with a new text layout.
NullLayouts ()
RequestLayout ()
Invalidate ()
}
}
As you can see, if the layout size has not changed, we will not execute the requestLayout method to re-draw the layout, but will only call the autoSizeText method to calculate the text size, and invalidate will draw the text itself, so when our width and height is set to a fixed value, the setText () method will not execute the requestLayout () method, and naturally it will not be able to implement the checkThread () method.
Introspection
After solving the problem, you still need to reflect on why you need checkThread to check threads.
Check the thread, in fact, is to check whether the current thread of the update UI operation is the thread that created the UI, which ensures thread safety, because the UI control itself is not thread safe, but locking appears to be too heavy, which will reduce the View loading efficiency, after all, it is related to interaction. Therefore, a single-thread model is formed directly through the logic of judging threads to ensure the thread safety of View operations. Case 2, the child thread and the main thread showToast respectively
1) toast pops up in the onCreate method and crashes-- Can't toast on a thread that has not called Looper.prepare ()
Override fun onCreate (savedInstanceState: Bundle?) {
Super.onCreate (savedInstanceState)
SetContentView (R.layout.activity_ui)
Thread {
ShowToast (I bought a watch last year)
}
}
2) pop up toast in the onCreate method and add the Looper.prepare () and Looper.loop () methods. Don't break down.
Plus the delay of 3 seconds, do not crash.
Override fun onCreate (savedInstanceState: Bundle?) {
Super.onCreate (savedInstanceState)
SetContentView (R.layout.activity_ui)
Thread {
/ / Thread.sleep (3000)
Looper.prepare ()
ShowToast (I bought a watch last year)
Looper.loop ()
}
}
3) using the same Toast instance, click the button before the Toast in the child thread disappears, modify the Toast text in the main thread and display it, and the program crashes-Only the original thread that created a view hierarchy can touch its views.
Rerun, display in the child thread and disappear, click the button without crashing.
Change the phone, Samsung S9, run it again, click the button before the Toast in the child thread disappears, and don't crash.
Lateinit var mToast: Toast
Override fun onCreate (savedInstanceState: Bundle?) {
Super.onCreate (savedInstanceState)
SetContentView (R.layout.activity_ui)
Thread {
Looper.prepare ()
MToast=Toast.makeText (this@UIMainActivity, "I bought a watch last year", Toast.LENGTH_LONG)
MToast.show ()
Looper.loop ()
}
Btn_ui.setOnClickListener {
MToast.setText ("I bought a watch this year")
MToast.show ()
}
}
Case two analysis
Before we solve the mystery, let's take a look at Toast.
Toast principle Toast.makeText (this,msg,Toast.LENGTH_SHORT). Show ()
A simple and commonly used sentence of code, or through the way of flowchart to see how it is created and displayed.
Toast flow chart
Just like the DecorView loading and drawing process, the layout file is loaded first, and the View is created. Then, through the addView method, create a new ViewRootImpl instance again, as a parent, to measure the layout and draw.
Collapse analysis
1) first of all, let's talk about the first crash-- Can't toast on a thread that has not called Looper.prepare (), that is, the thread creating Toast must have Looper running.
According to the source code, we also know that Toast shows and hides messages through Handler, so there must be a Handler usage environment, that is, binding Looper objects, and starting to loop messages through the loop method.
2) the second collapse-Only the original thread that created a view hierarchy can touch its views.
The crash here reported the same error as the previous update Button, so we have reason to suspect that the requestLayout method of ViewRootImpl is called on different threads for the same reason.
We see that when the button is clicked, the mToast.setText () method is called. Why, this is exactly the same as the case.
The setText () method of TextView is called in the setText method, and then because the width and height of TextView in Toast is wrap_content, the requestLayout method is triggered, and finally the requestLayout method of the uppermost View, that is, ViewRootImpl, is called.
So the reason for the crash is that the first time Toast show in a child thread, it creates a new ViewRootImpl instance and binds the current thread, that is, the child thread, to the mThread variable. Then the same Toast calls the setText method in the main thread, and eventually calls the requestLayout method of ViewRootImpl, causing the thread to check. The current thread, that is, the main thread, is not the thread that created the ViewRootImpl instance, so it crashes.
3) so why doesn't it crash when you click the button after Toast disappears?
The reason is that in the hide method of Toast, the assignParent method of View will eventually be called to set the mParent of Toast to null, that is, ViewRootImpl to null. So when you call the setText method, you will not be able to execute the requestLayout method, and you will not check the thread to the checkThread method. Post the code:
Public void handleHide () {
If (mView! = null) {
If (mView.getParent ()! = null) {
MWM.removeViewImmediate (mView)
}
MView = null
}
}
RemoveViewImmediate--- > removeViewLocked
Private void removeViewLocked (int index, boolean immediate) {
ViewRootImpl root = mRoots.get (index)
View view = root.getView ()
/ /...
If (view! = null) {
View.assignParent (null)
If (deferred) {
MDyingViews.add (view)
}
}
}
Void assignParent (ViewParent parent) {
If (mParent = = null) {
MParent = parent
} else if (parent = = null) {
MParent = null
} else {
Throw new RuntimeException ("view" + this + "being added, but" + "it already has a parent")
}
}
4) but why doesn't another phone collapse?
I found this by accident, on my Samsung S9 phone, the runtime will not crash, and the feedback from the interface is not to modify the text on the Toast on the current page, but to create a new Toast display, that is, the setText method is written in the code.
So I guess that on some phones, the setting of Toast should be changed. When the setText method is called, it will immediately end the current Toast display and call the hide method. Then modify the Toast text and display it, which is the practice of the third point just now.
Of course, this is just my guess, the great god who has studied the mobile phone source code can also add.
At this point, the study of "what is the principle of threading and updating UI" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.