Dun*_* Ta 5 java multithreading android kotlin
场景:
在测试片段中的线程时遇到一个奇怪的问题。
我有一个用Kotlin编写的片段,其onResume()中包含以下片段:
override fun onResume() {
super.onResume()
val handlerThread = HandlerThread("Stuff")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post {
Thread.sleep(2000)
tv_name.setText("Something something : " + isMainThread())
}
}
Run Code Online (Sandbox Code Playgroud)
is MainThread()是一个检查当前线程是否为主线程的函数,如下所示:
private fun isMainThread(): Boolean = Looper.myLooper() == Looper.getMainLooper()
Run Code Online (Sandbox Code Playgroud)
我看到我的TextView在2秒后更新为“ Something something:false”
看到false告诉我该线程当前不是UI / Main线程。
我以为这很奇怪,因此我创建了相同的片段,但用onResume()的以下代码段用Java编写:
@Override
public void onResume() {
super.onResume();
HandlerThread handlerThread = new HandlerThread("stuff");
handlerThread.start();
new Handler(handlerThread.getLooper()).post(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
textView.setText("Something something...");
}
});
}
Run Code Online (Sandbox Code Playgroud)
该应用程序崩溃,但出现以下异常:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7313)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1161)
Run Code Online (Sandbox Code Playgroud)
我做了一些研究,但我找不到真正能解释这一点的东西。另外,请假设我的观点全部正确。
题:
当我在以Kotlin编写的Fragment中用我的UI线程运行的可运行对象中修改TextView时,为什么我的应用程序不会崩溃?
如果某处文档中的某些内容可以解释这一点,那么有人可以请我参考一下吗?
我实际上并没有试图从UI线程修改UI,我只是很好奇为什么会这样。
如果您需要更多信息,请告诉我。非常感谢!
更新: 根据@Hong Duan提到的内容,requestLayout()未得到调用。这与Kotlin / Java无关,但与TextView本身无关。
我无所适从,但没有意识到Kotlin片段中的TextView的layout_width为“ match_parent”。而我的Java片段中的TextView的layout_width为“ wrap_content”。
TLDR:用户错误+ requestLayout(),并非总是进行线程检查。
唯一CalledFromWrongThreadException在必要时抛出,但并非总是如此。在您的情况下,当ViewRootImpl.checkThread()在期间调用时它会抛出ViewRootImpl.requestLayout(),以下是来自的代码ViewRootImpl.java:
@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.");
}
}
Run Code Online (Sandbox Code Playgroud)
对于TextView,当我们更新它的文本时并不总是需要重新布局,我们可以在源代码中看到逻辑:
/**
* 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.
int oldht = mLayout.getHeight();
int want = mLayout.getWidth();
int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
/*
* No need to bring the text into view, since the size is not
* changing (unless we do the requestLayout(), in which case it
* will happen at measure).
*/
makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
mRight - mLeft - getCompoundPaddingLeft() - 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.MATCH_PARENT) {
autoSizeText();
invalidate();
return; // return with out relayout
}
// 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; // return with out relayout
}
}
// 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();
}
}
Run Code Online (Sandbox Code Playgroud)
可以看到,在某些情况下,requestLayout()不会调用 ,因此不会引入主线程检查。
所以我认为关键点不是关于 Kotlin 或 Java,而是关于TextViews 的布局参数,它决定是否requestLayout()被调用。