Android开发Android知识

关于Android子线程更新UI问题

2017-11-05  本文已影响111人  chocolatezhu

昨晚任玉刚老师在时光课堂有一场直播秀,里面提到了这么一个问题:

在OnCreate里启动一个子线程更新UI为什么程序没有抛出异常?

这个问题之前也有了解过,但这一提起来我似乎又忘记了为什么,所以记录一下。

代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mTxt = (TextView) findViewById(R.id.txt);
    new Thread(new Runnable() {
        @Override
        public void run() {
            mTxt.setText("UI Test!");
        }
    }).start();

按照开发规范来说,是不建议在子线程更新UI的,这必然会引起程序闪退,但是上面在OnCreate里启动子线程更新UI却没有问题,也不抛异常。好,那我们让子线程休眠200ms再更改UI。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mTxt = (TextView) findViewById(R.id.txt);
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mTxt.setText("UI Test!");
        }
    }).start();
}

这时候你会发现程序起不来了,并且控制台里打印出了我们非常熟悉的异常信息

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                                                                  at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7680)
                                                                  at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1223)

简单解释一下,更新UI的时候系统会去检查当前更新UI的线程是否属于主线程,而该检查操作是在ViewRootImpl.checkThread()中完成的,因为在OnCreate的时候ViewRootImpl还没有被创建,所以这时候就无法检查该更新UI的线程是否属于主线程,自然程序不会报错。休眠200ms后,程序会报错是因为这时候ViewRootImpl已经初始化完成了(ViewRootImpl是在OnResume回调后完成创建的),这时候再去更新UI,自然就会报错。

为什么Android的设计子线程不能更新UI呢,这自然是有一定道理的。UI是用户和程序交互的窗口,任何涉及到UI的操作更新都是要及时响应的,如果多个线程可以同时访问UI的话,这肯定是不安全的,加个线程锁可以不可以呢?可以,但是这太影响效率了,所以Google的工程师这么设计没毛病。

如果要更加了解这个问题,那就需要跟进源码去分析了,这里就不展开了。

上一篇下一篇

猜你喜欢

热点阅读