android 子线程更新视图

首先
再主线程中弹出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 直接在子线程初始化,之后就可以在初始化线程进行更新视图;

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。