首先
再主线程中弹出dialog,没有问题;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ui_test)
btn.setOnClickListener {
MyDialog(this).show()
}
}
inner class MyDialog(context: Context) : Dialog(context) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.dialog_ui_test)
btn_cancle.setOnClickListener { cancel() }
btn_change.setOnClickListener {
tv.text = "1 (${tv.text})"
Toast.makeText(context, tv.text, Toast.LENGTH_SHORT).show()
}
}
}
第二版,子线程弹dialog
这个没有弹出dialog,也竟然没有崩溃,之前的报错是
Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
btn.setOnClickListener {
thread { // ++
MyDialog(this).show()
} // ++
}
第三版,
子线程通过添加looper.prepare(),Looper.loop(),成功弹出了
btn.setOnClickListener {
thread {
Looper.prepare()
MyDialog(this).show()
Looper.loop()
}
}
继续尝试
在弹出的dialog,尝试主线程修改UI
val mainHandler = Handler(Looper.getMainLooper())
btn_change.setOnClickListener {
mainHandler.post{
tv.text = "1 (${tv.text})"
Toast.makeText(context, tv.text, Toast.LENGTH_SHORT).show()
}
}
运行报错,
Only the original thread that created a view hierarchy can touch its views.
子线程创建的dialog,然后主线程修改UI就报错了,那么到底是哪个线程可以修改UI呢?报的错误提示只有original thread 可以修改,现在找下original thread
再修改View,会调用requestLayout,
android.view.View
public void requestLayout() {
***
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
***
}
android.view.ViewRootImpl
@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.");
}
}
public ViewRootImpl(Context context, Display display) {
***
mThread = Thread.currentThread();
***
}
mThread 是 ViewRootImpl创建时侯初始化的,这个viewRootimple 是dialog 创建时初始化的
所以这里不可以切换线程,只有再创建viewRootimple的线程才可以修改UI
要在子线程修改UI 有2个办法:
1.在onResume之前,可以在非UI线程更新视图,因为这个时候不会发生线程校验;线程校验实在ViewRootImpl中进行的,ViewRootImpl实在onResume之后初始化
2.ViewRootImpl 直接在子线程初始化,之后就可以在初始化线程进行更新视图;
网友评论