一、现象:子线程更新UI操作,竟然不崩溃二、原因:宽和高尺寸未变化,仅需替换text
2.1 当TextView宽和高尺寸未变化,则子线程调用setText,不崩溃!2.2 当TextView宽和高尺寸发生了变化,则子线程调用setText,崩溃! 三、分析:
上文中有两处不会调用requestLayout,另有两处会调用requestLayout。因为:mParent被ViewRootImpl赋值所以:mParent.requestLayout()调用的是ViewRootImpl.requestLayout()子线程更新UI会崩溃就是因为checkThread中的CalledFromWrongThreadException
一、现象:子线程更新UI操作,竟然不崩溃 private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.tv);
Button button = findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
super.run();
//子线程更新UI:setText未崩溃
textView.setText("test");
}
}.start();
}
});
}
二、原因:宽和高尺寸未变化,仅需替换text
2.1 当TextView宽和高尺寸未变化,则子线程调用setText,不崩溃!
2.2 当TextView宽和高尺寸发生了变化,则子线程调用setText,崩溃!
@UnsupportedAppUsage
private void checkForRelayout() {
// If we have a fixed width, we can just swap in a new text layout
//如果固定了宽度,我们只需替换一个新的text layout。
// if the text height stays the same or if the view height is fixed.
//如果高度不变,或者固定了高度。
三、分析:
public class TextView extends View implements ...
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
...
if (mLayout != null) {
checkForRelayout();
}
...
}
public class TextView extends View implements ...
@UnsupportedAppUsage
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 (...) {
// Static width, so try making a new text layout.
...
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;
}
// Dynamic height, but height has stayed the same,
// so use our new text layout.
if (mLayout.getHeight() == oldht
&& (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
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();//!!!这里才会checkThread
invalidate();
} else {
// Dynamic width, so we have no choice but to request a new
// view layout with a new text layout.
nullLayouts();
requestLayout();//!!!这里才会checkThread
invalidate();
}
}
上文中有两处不会调用requestLayout,
另有两处会调用requestLayout。
只有需要新的view layout时,才需要调用requestLayout
(Request a new view layout using our new text layout.)
public class View implements ...
public void requestLayout() {
...
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
...
}
因为:mParent被ViewRootImpl赋值
public class View implements ...
void assignParent(ViewParent parent) {
if (mParent == null) {
mParent = parent;
}
...
}
public final class ViewRootImpl implements ViewParent, ...
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
...
view.assignParent(this);
...
}
所以:mParent.requestLayout()调用的是ViewRootImpl.requestLayout()
public final class ViewRootImpl implements ViewParent, ...
@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.");
}
}
子线程更新UI会崩溃就是因为checkThread中的CalledFromWrongThreadException


