Android子线程中更新UI的方法

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















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值