您的位置:首页 > 产品设计 > UI/UE

Android子线程中更新UI的方法

2016-12-29 15:17 411 查看

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: