Android只有主线程才能更新UI?

传统的观念认为Android中更新UI必须在主线程,但实际测试显示,只要在创建UI的线程(通常是主线程)中,就可以更新UI。文章通过代码示例揭示了这一现象,并指出在子线程创建并更新View也是可行的,但常规做法仍是使用Handler进行跨线程通信。

以前在做安卓程序开发的时候,心中有一目标——更新UI只能在主线程。这个思想或许是许多安卓开发者都觉得应该是这样,事实Google也建议我们这样做,它提供了Handler机制,通过传递Message实现主线程与子线程之间的通信。

那依据上面据所说,我们来做一个测试。先看下面代码,就是在SetContentView中设置一个TextView,并开启一个线程来是更新TextView的内容。

public class MainActivity extends AppCompatActivity {


    private TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mTextView = new TextView(this);
        mTextView.setText("旧内容");

        setContentView(mTextView);
        new Thread(new CThread()).start();
    }

    class CThread implements Runnable {

        @Override
        public void run() {
            mTextView.setText("新内容");
        }
    }
}

看到这段代码,很多人看后会说,程序肯定会Crash,因为我们子线程中直接更新UI了,这是“犯法”的事,我们运行程序看一下结果:

这里写图片描述

从图中可以看到,程序正常运行,没有Crash,并正确更新了UI,到这里也许有会有点迷惑,但确实更新了,这点我没有太了解,猜测应该是跟CPU时间片有关。这时候我们再在子线程里做一些事情,比如休眠100毫秒,这时运行程序会发现程序Crash了。并抛出一个运行时异常:ViewRootImpl$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.这个异常的意思,只有创建这个视图层次结构的原始线程才能更改它的view。这句话中可以读出一个意思,即更新UI并不一定要在主线程,只要在创建UI的那个线程中,就可以更新线程。下面再写一个例子来验证一下:

public class SecondActivity extends AppCompatActivity {

    TextView mMainThreadTextView;

    TextView mChildThreadTextView;

    private CHandler mUpdateHandler = new CHandler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mMainThreadTextView = new TextView(this);
        mMainThreadTextView.setText("主线程TextView");
        setContentView(mMainThreadTextView);

        new Thread(new CThread()).start();

    }

    class CThread implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            mChildThreadTextView = new TextView(SecondActivity.this);
            mChildThreadTextView.setText("子线程创建UI并更新");

            Message message = mUpdateHandler.obtainMessage();
            message.sendToTarget();

        }
    }

    class CHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            setContentView(mChildThreadTextView);
            super.handleMessage(msg);
        }
    }

这个例子也很简单,即在子线程中创建一个TextView,并且为这个TextView赋值,然后发送消息在Handler里面设置View,先来看一下运行结果 :

这里写图片描述

最终发现,子线程成功更新TestView的值,这就验证了Only the original thread that created a view hierarchy can touch its 这句话。也就是更新UI只要在创建UI的线程中就可以了,我们通常初始化View的时候, 一般是在OnCreate方法里面,也可以说是主线程中,所以会形成一个思维,线程更新UI只能在主线程。

当然通常我们很少在子线程中创建View,所以正常情况都还是子线程处理耗时操作,操作完毕发送Message给Handler处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值