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

来源:互联网 发布:大数据登录页面素材 编辑:程序博客网 时间:2024/04/30 01:55

以前在做安卓程序开发的时候,心中有一目标——更新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处理。

0 0