在Android中,只有主线程(UI线程)才能修改界面,包括Toast显示,改变Imageview中的图片等操作必须通过消息机制通知主线程修改界面
如不采用handler消息机制极有可能出现如下错误
CalledFromWrongThreadException
发生原因:谁创的view对象 谁才能修改它
即只有主线程(UI线程)才能修改界面,包括Toast显示,改变Imageview中的图片等操作必须通过消息机制通知主线程修改界面
为什么只有主线程才能修改界面:如果多个线程同时修改界面,追造成界面混乱
内部实现:
Toast在子线程显示汇报如下错误
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
以上俩种错误均可以采用消息机制避免
案例:网络图片浏览器
1页面布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<ImageView
android:layout_weight="20"
android:id="@+id/iv"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<EditText
android:id="@+id/et_path"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="http://pic.baike.soso.com/p/20090717/20090717194041-49323064.jpg"
android:hint="请输入图片路径"/>
<Button
android:onClick="click"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="浏览" />
</LinearLayout>
EditText中的路径大家最好自己找一个,不保证这个图片地址一直有效
2编写主界面 具体编写点击事件
package com.example.a49_internetpicwatcher;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
protected static final int CHANGE_UI = 1;
protected static final int ERROR = 0;
private EditText et_path;
private ImageView iv;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == CHANGE_UI) {
Bitmap bitmap = (Bitmap) msg.obj;
iv.setImageBitmap(bitmap);
} else if (msg.what == ERROR) {
Toast.makeText(MainActivity.this, "获取图片失败", Toast.LENGTH_SHORT)
.show();
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_path = (EditText) findViewById(R.id.et_path);
iv = (ImageView) findViewById(R.id.iv);
}
public void click(View view) {
final String path = et_path.getText().toString().trim();
if (TextUtils.isEmpty(path)) {
Log.e("chj", "路径不能为空");
// Toast.makeText(MainActivity.this, "路径不能为空",
// Toast.LENGTH_SHORT).show();
} else {
new Thread() {
public void run() {
// 连接服务器 get获取图片
try {
URL url = new URL(path);
// 根据Url发送http请求
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 设置请求方式
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
// conn.setReadTimeout(timeoutMillis); //与上面的方法不同
// 设置浏览器参数 如浏览器类型 防盗链等
conn.setRequestProperty("User-Agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.51");
// 得到服务器返回码
int code = conn.getResponseCode();
if (code == 200) {
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
// iv.setImageBitmap(bitmap);
Message msg = new Message();
msg.what = CHANGE_UI;
msg.obj = bitmap;
handler.sendMessage(msg);
} else {
Log.e("chj", "显示图片失败");
Message msg = new Message();
msg.what = ERROR;
handler.sendMessage(msg);
// Toast.makeText(MainActivity.this, "显示图片失败",
// Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("chj", "获取图片失败");
// Toast.makeText(MainActivity.this, "获取图片失败",
// Toast.LENGTH_SHORT).show();
}
};
}.start();
}
}
}
实际开发中下载图片需要经过如下几个步骤:
一、第一次开始下载图片:下载图片是耗时操作,必须放到子线程中(应该在Service中启动一个子线程去下载图片)
二、根据实际情况,处理下载下来的图片。例如,用户希望图片的大小时10dp、10dp,应该通过代码去裁剪图片
三、下载后的图片需要放到内存的缓存中
第四步:如果再次查看列表中的图片,首先到缓存中查找图片是否存在,若存在,从缓存中取出图片,若不存在,
再次下载(Cache)
第五步:项目开发中,数据库中的数据,图片是单独下载的,其他数据可以通过JSON获取;而且项目开发中,
一般不要自己写代码去下载图片,使用第三方框架去下载