Android子线程中更新UI的方法

来源:互联网 发布:速拓必达医疗器械软件 编辑:程序博客网 时间:2024/06/02 19:36

1.handler


2.通过runOnUiThread方法

方法内部实现如下:
    public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }
使用方法如下:
        new Thread(new Runnable() {            @Override            public void run() {                // 此处执行耗时操作,结束后,执行runOnUiThread将线程切换到主线程去更新ui                                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        // 更新ui操作                    }                });            }        }).start();
如果在非上下文中环境中,可以通过一下方法来实现:
        final Activity activity = (Activity) mTextView.getContext();        new Thread(new Runnable() {            @Override            public void run() {                activity.runOnUiThread(new Runnable() {                    @Override                    public void run() {                                            }                });            }        }).start();

在其他地方,则需要传递Activity对象,因为runOnUiThread方法是Activity的方法。


3.通过view.post(runnable)来实现

        mTextView.post(new Runnable() {            @Override            public void run() {            }        });

以上不管哪种方法,原理都是将更新ui的消息从子线程中传递到主线程中,因为,更新view只能在主线程,这个是无法改变的。


4.子线程直接更新ui的极端情况

android的UI访问是没有加锁的,这样会导致多个线程访问ui会不安全,那么既然这样,为什么不加锁呢,因为加上锁机制会让ui访问的逻辑变得复杂,其次锁机制会降低ui访问的效率,锁机制会阻塞某些线程的执行。所以android规定,只能在主线程更新ui。

子线程真的无法更新ui吗,答案是no,因为有些极端情况,还是可以更新ui的。

我们在onCreate中直接执行以下代码:

        Log.i("niejianjian", " -> onCreate -> " + Thread.currentThread().getId());        new Thread(new Runnable() {            @Override            public void run() {                Log.i("niejianjian", " -> Thread -> " + Thread.currentThread().getId());                mTextView.setText("fjsdlj l");            }        }).start();
发现更新ui是可以成功的,我们可以打印当前的线程,发现确实是在子线程中,因为主线程的id是1。但是我们在将Thread睡眠200ms,
        Log.i("niejianjian", " -> onCreate -> " + Thread.currentThread().getId());        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(200);                } catch (InterruptedException e) {                    e.printStackTrace();                }                Log.i("niejianjian", " -> Thread -> " + Thread.currentThread().getId());                mTextView.setText("fjsdlj l");            }        }).start();
结果就报错了,log如下:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6257)        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:868)
ViewRootImpl是ViewRoot的实现类,异常主要是checkThread抛出的,因为里面对线程做了判断:
void checkThread() {    if (mThread != Thread.currentThread()) {        throw new CalledFromWrongThreadException(                "Only the original thread that created a view hierarchy can touch its views.");    }}
因为view的绘制过程中,都会执行ViewRootImpl的checkThread来检查是否是主线程更新,所以onCreate中的子线程可以更新ui,主要是因为ViewRootImpl还没有创建,所以无法进行检查。

ViewRootImpl的创建在onResume方法回调之后,而我们一开篇是在onCreate方法中创建了子线程并访问UI,在那个时刻,ViewRootImpl是没有创建的,无法检测当前线程是否是UI线程,所以程序没有崩溃一样能跑起来,而之后修改了程序,让线程休眠了200毫秒后,程序就崩了。很明显200毫秒后ViewRootImpl已经创建了,可以执行checkThread方法检查当前线程。

参考:http://www.cnblogs.com/xuyinhuan/p/5930287.html















0 0
原创粉丝点击