您的位置:首页 > 移动开发 > Android开发

android.view.ViewRootImpl$CalledFromWrongThreadException异常处理

2013-10-20 11:10 519 查看
一般情况下,我们在编写android代码的时候,我们会将一些耗时的操作,比如网络访问、磁盘访问放到一个子线程中来执行。而这类操作往往伴随着UI的更新操作。比如说,访问网络加载一张图片

new Thread() {
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
// 设置请求方式
connection.setRequestMethod("GET");
// 设置超时时间
connection.setConnectTimeout(10000);
int code = connection.getResponseCode();
if (code == 200) {
InputStream is = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);

iv_beauty.setImageBitmap(bitmap);
} else {
Toast.makeText(getApplicationContext(), "图片获取失败!",
0).show();
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "图片获取失败!", 0)
.show();
}
}

}.start();


如果是这样去操作,就会抛出

10-20 02:50:38.219: W/System.err(497): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.


后边那句英文的大概是说只有创建了UI对象的主线程才能去修改UI

原因在于,在android里面不是线程安全的,所以android有阻止子线程更新组件的机制。

于是我们应该将UI的更新交给主线程来完成,如何去完成,android为我们提供了一套消息处理机制。

它大概的实现步骤是这样子的

1.子线程利用Handler类发送一条消息,消息会被送完主线程的消息队列里

2.主线程里边有一个叫looper的轮询器,它会循环遍历消息队列

3.如果loopler发现消息队列里有新的消息,android就会调用Handler的handleMessage()方法来处理消息

我们来看一下实例代码吧

1.首先我们先定义好两个常量

private static final int UPDATE_UI = 1;
private static final int ERROR = 2;


2.修改上边那段有异常的代码

new Thread() {
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
// 设置请求方式
connection.setRequestMethod("GET");
// 设置超时时间
connection.setConnectTimeout(10000);

// connection.setRequestProperty(field, newValue)

int code = connection.getResponseCode();
if (code == 200) {
InputStream is = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);

// 告诉主线程一个消息,帮我更新ui,内容:bitmap
Message msg = new Message();
// 消息的代号,是一个int类型
msg.what = UPDATE_UI;
// 要传递的消息对象
msg.obj = bitmap;
// 利用handler发送消息
handler.sendMessage(msg);
} else {
Message msg = new Message();
msg.what = ERROR;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
Message msg = new Message();
msg.what = ERROR;
handler.sendMessage(msg);
}
}

}.start();


3.在主线程中新建一个Handler类来处理消息

// 主线程创建消息处理器
Handler handler = new Handler() {
// 但有新消息时调用
@Override
public void handleMessage(Message msg) {
if (msg.what == UPDATE_UI) {
// 获取消息对象
Bitmap bitmap = (Bitmap) msg.obj;
// 更新UI
iv_beauty.setImageBitmap(bitmap);
} else if (msg.what == ERROR) {
// Toast也是属于UI的更新
Toast.makeText(getApplicationContext(), "图片获取失败!", 0).show();
}
}
};


再次运行代码,就不会抛出android.view.ViewRootImpl$CalledFromWrongThreadException异常了

另外我们还可以利用Activity里边提供的一个runOnUiThread()方法来更新UI

runOnUiThread()的官方文档是这么描述其作用的:

Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.

于是我们只需要修改上边有异常那段代码:

new Thread() {
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
// 设置请求方式
connection.setRequestMethod("GET");
// 设置超时时间
connection.setConnectTimeout(10000);

// connection.setRequestProperty(field, newValue)

int code = connection.getResponseCode();
if (code == 200) {
InputStream is = connection.getInputStream();
final Bitmap bitmap = BitmapFactory
.decodeStream(is);

// // 告诉主线程一个消息,帮我更新ui,内容:bitmap
// Message msg = new Message();
// // 消息的代号,是一个int类型
// msg.what = UPDATE_UI;
// // 要传递的消息对象
// msg.obj = bitmap;
// // 利用handler发送消息
// handler.sendMessage(msg);

runOnUiThread(new Runnable() {

@Override
public void run() {
iv_beauty.setImageBitmap(bitmap);

}
});
} else {
// Message msg = new Message();
// msg.what = ERROR;
// handler.sendMessage(msg);
runOnUiThread(new Runnable() {

@Override
public void run() {
Toast.makeText(getApplicationContext(), "图片获取失败!", 0).show();

}
});
}
} catch (Exception e) {
e.printStackTrace();
// Message msg = new Message();
// msg.what = ERROR;
// handler.sendMessage(msg);
runOnUiThread(new Runnable() {

@Override
public void run() {
Toast.makeText(getApplicationContext(), "图片获取失败!", 0).show();

}
});
}
}

}.start();


同样能够正常运行
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 异常处理
相关文章推荐