子线程中更新主线程界面(UI)
一、前言
最近在多线程的使用过程中遇到一些问题,记录下来以加深印象。当在主线程中要进行某一操作比较耗时时,就需要开启新线程来处理,主线程通常等待时间在 5 秒左右,大于 5 秒的操作会报错,所以需要子线程。
其一是在子线程中更新主线程界面(UI) 时抛出异常如下:
:android.view.ViewRootImpl$CalledFromWrongThreadExc
谷歌翻译:只有创建视图层次才可以触摸其焦点原来的线程。
二、子线程更新主线程界面 (UI)的异常原因及处理
示例代码:(以下代码是本人的相关文件的部分代码,其中部分类是个人需要写的)
(1)报异常代码
<span style="font-size:14px;">new Thread(new Runnable() {
@Override
public void run() {
GetAllMessageFromWeb getAll = new GetAllMessageFromWeb();
allList = getAll.getAllMessage();//(1)数据源加载来自数据库(2)创建适配器(3)视图加载适配器
dataList = new ArrayList<Map<String,Object>>();
for(int i=0;i<allList.size();i++){
MyMessage message = new MyMessage();
message = allList.get(i);
Map<String,Object> map = new HashMap<String,Object>();
map.put("id",message.getId());
map.put("content",message.getContent());
dataList.add(map);
}
adapter = new MyStyle(MenuActivity.this,dataList,R.layout.listview_layout,new String[] {"id","content"},new int[] {R.id.msg_id,R.id.msg_content});
listView.setAdapter(adapter);//出错,报异常,原因在子线程中修改主线程UI会引起错乱,应用Handler机制来处理。
}
}).start();</span>
异常原因是直接在主线程中开一个新线程,该子线程有修改主线程 UI的操作,报异常
Only
(2)Handler 消息机制处理后的代码,不报异常,成功更新主线程界面 (UI)
<span style="font-size:14px;">final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==RIGHT){
//(2)创建适配器
adapter = new MyStyle(MenuActivity.this,dataList,R.layout.listview_layout,new String[] {"id","content"},new int[] {R.id.msg_id,R.id.msg_content});
//(3)视图加载适配器
listView.setAdapter(adapter);
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
GetAllMessageFromWeb getAll = new GetAllMessageFromWeb();
allList = getAll.getAllMessage();
dataList = new ArrayList<Map<String,Object>>();
//(1)数据源加载来自数据库
for(int i=0;i<allList.size();i++){
MyMessage message = new MyMessage();
message = allList.get(i);
Map<String,Object> map = new HashMap<String,Object>();
map.put("id",message.getId());
map.put("content",message.getContent());
dataList.add(map);
}
if(dataList!=null){
Message m = new Message();
m.what = RIGHT;
handler.sendMessage(m);
}else{
Message m = new Message();
m.what = ERROR;
handler.sendMessage(m);
}
}
}).start();</span>
1、对子线程中更新主线程 UI的异常处理,就是使用 Handler消息处理机制。新建 Haldler对象,通过 message 的发送与接收,来处理更新 UI操作。使用 Handler 机制进行子线程更新界面后返回给用户不会报异常。
2、注意:在子线程中是无法更新主线程的界面(UI)的.Android引入Handler解决这个问题。
3、原因是如果同时多个子线程更新主线程界面,会导致界面不断变化,并且不易找到原因,Handler机制就是的当子线程中有更新主线程 UI 的操作时,将通过 message 消息的发送接收来告诉主线程,handler 在主线程中运行,对发送接收的message处理,从而实现主线程页面的更新。该机制主要是规范子线程与主线程的操作,从而避免子线程的相关操作引起混乱。
三、 Handler 处理机制的简单使用
在子线程中修改主线程界面 (UI)中控件 EditText 的内容,代码如下:
其中 _handler1,_handler2 是两个按钮,对其监听,进行修改 UI操作;前者直接在新线程中更新 UI,后者使用Handler message处理。
<span style="font-size:14px;">/**
* 监听 handler 机制 (1) 会报异常,因为没用 handler 机制,在子线程修改界面 (UI)会报异常。修改 editText内容失败。
*/
_handler1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//对文本框内容修改
editText.setText("调用 handler 之前的内容。");
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);//休息3秒
} catch (InterruptedException e) {e.printStackTrace();}
editText.setText("调用 handler 机制进行修改内容1。");
}
}).start();
}
});
/**
* 监听 handler 机制,(2)子线程调用handler 处理主线程界面 (UI)成功,将 exitText对应的文本框内容修改
*/
_handler2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
editText.setText("handler 机制,子线程修改 UI之前");
final Handler handler = new Handler(){
public void handleMessage(Message m) {
super.handleMessage(m);
if(m.what==1){
editText.setText("调用 handler 机制进行修改内容2。");
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {e.printStackTrace();}
Message message = new Message();//新建消息
message.what = 1; //赋给内容
handler.sendMessage(message); //发送消息
}
}).start();
}
});</span>
在运行的程序中,点击两按钮的结果如下图:
_handler1
所报异常:
_handler2
正常运行,不报异常。
四、handler + message + looper 对于线程 Thread 详细图示