Android非UI线程无法更新UI?

发布于 2022-09-02 10:23:47 字数 1216 浏览 18 评论 0

使用HandlerThread获取Looper对象,后创建Handler(mHandlerThread.getLooper),此时的HandleMessage()在子线程中运行,为何此处可以更新UI?

public class MainActivity extends AppCompatActivity {

    private TextView mTvText ;
    private Handler mHandler;
    // 使用这个新线程获取Looper对象
    private HandlerThread mHandlerThread = new HandlerThread("my_handler_thread");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTvText = (TextView)findViewById(R.id.id_tv_text);
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                // 此处显示在子线程中执行 handleMessage()
                // 为何在此处(子线程中)更新UI组件不会抛出异常?
                Log.v("LOG","CurThread: "+Thread.currentThread());
                // CurThread:Thread[my_handler_thread,5,Main]
                mTvText.setText("update this textView!");
            }
        };
        mHandler.sendEmptyMessage(1);
    }
}

图片描述

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

慵挽 2022-09-09 10:23:47

并不是所有对View的操作都认为是更新UI,更新UI主要可以通过这个操作内部是否调用了View.invalidate()来判断,也就是说,所谓的更新UI是指程序代码主动要求马上更新UI,这时候才会因为检查线程而出错。
而类似TextView.setText()等一些方法,虽然替换了显示内容,但内部没有马上申请UI更新,而只是进行一下标记,等到下次主线程更新UI时才会在主线程更新UI。在内部方法中,可以体现为使用View.invalidate()View.postInvalidate()的区别。

优雅的叶子 2022-09-09 10:23:47

这是一个非常好的问题,谢谢 @luffer 邀请。
题主的代码确实是在子线程中更新的,可以打印 thread id 验证(luffer 理解错了这个细节)

// Use the provided Looper instead of the default one.
public Handler(Looper looper) {}

所以在子线程中常常可以通过如下方法获取主线程的 handler,也可以使用题主的方法在主线程中获取子线程的 handler:

// 这是一个主线程的 handler
new Handler(getContext().getMainLooper());

至于为什么没有crash,确实很奇怪,我去查了 setText,实在复杂,没有看出端倪,但是倘若这样去做,会 crash:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mHandler.sendEmptyMessage(1);
    }
});

crash log 如下:

FATAL EXCEPTION: my_handler_thread
    Process: com.magellan.mapdemo, PID: 2302
    android.view.ViewRootImpl$CalledFromWrongThreadException:
     Only the original thread that created a view hierarchy can touch its views.
     ......
    at android.widget.TextView.setText(TextView.java:4862)

所以我猜,crash 的关键词是 touch .
结论:不应该在子线程中更新UI,非常危险!

参考:
http://stackoverflow.com/questions/14220883/accessing-ui-view-in-another-thread-does-not-cause-a-crash-why
http://www.codes9.com/mobile-development/android/about-the-update-ui-control-does-not-ui-in-non-crash-thread-problems/

懵少女 2022-09-09 10:23:47

在主线程中更新的。

美人如玉 2022-09-09 10:23:47

这个原因可能是Google的BUG了

你试试,将start后面的代码放到 Button 的点击事件中去执行

貌似onCreate方法内非UI线程更新UI不会闪退

撩发小公举 2022-09-09 10:23:47

mhandler绑定了主线程(UI线程),你这个不就是一直都在主线程中实现更新吗?Android_Message_Handler_消息处理机制总结笔记

︶葆Ⅱㄣ 2022-09-09 10:23:47

Android在Activity中更新UI界面的时候都会通过ViewRootImpl.checkThread方法检查当前线程是不是主线程,如果不是主线程就会抛出此异常。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文