非ui线程更新ui问题
2015-08-31 11:55 指针空间 阅读(533) 评论(0) 编辑 收藏 举报android初学者,刚开始并没有意识到,android中更新UI只能用UI线程,写了一个下载线程,在线程里更新progessbar,并用textview显示下载进度
public void listenProgress(){
new Thread(new Runnable() {
@Override
public void run() {
while(progress < MsgService.MAX_PROGRESS){
progress = msgService.getProgress();
progressbar.setProgress(progress);
text.setText("下载" + progress + "%");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//text.setText("下载" + progress + "%");
}
}
}).start();
}
总会报错 Only the original thread that created a view hierarchy can touch its views
后来才意识到,在非ui线程里不能直接更新ui。子线程中数据发生改变来更新主线程的ui,一般是通过消息机制,message和handler结合的方式。
但是,我继续测试发现,把TextView更新注释掉(//text.setText("下载" + progress + "%");),就没有上面报错,运行后progress能正常更新。
android里面的准则是非ui线程是不能去修改ui线程里面的ui控件的, 但是为什么不在主线程调用setProgress 方法没有报错呢,查看源代码可以发现,setProgress 会调用到private synchronized void refreshProgress(int id, int progress, boolean fromTouch) 方法,首先会判断当前线程是否为主UI线程,若是主UI线程则直接调用 doRefreshProgress 方法更新进度;若不是主UI线程则会先创建一个RefreshProgressRunnable 对象,然后调用 view 的 post(Runnable action) 方法,将 RefreshProgressRunnable 放到主UI线程的消息队列等待处理。从表面上看似乎是在非UI线程里面去修改了主线程的控件,而实际上并非这样。同理,SeekBar、ProgressDialog 也是可以在子线程直接更新进度的。
参考博客:http://blog.csdn.net/androidzhaoxiaogang/article/details/8136222
参考源码:
private synchronized void refreshProgress(int id, int progress, boolean fromTouch) {
if (mUiThreadId == Thread.currentThread().getId()) {
doRefreshProgress(id, progress, fromTouch);
} else {
RefreshProgressRunnable r;
if (mRefreshProgressRunnable != null) {
// Use cached RefreshProgressRunnable if available
r = mRefreshProgressRunnable;
// Uncache it
mRefreshProgressRunnable = null;
r.setup(id, progress, fromTouch);
} else {
// Make a new one
r = new RefreshProgressRunnable(id, progress, fromTouch);
}
post(r);
}
}