您的位置:首页 > 理论基础 > 计算机网络

从头学android_网络图片查看器

2016-05-30 14:32 495 查看

需求:

一个文本框,一个按钮,在文本框中输入一个图片的url,点击按钮,在按钮下方显示图像内容。

layout:



manifest:

申请使用网络的权限

<uses-permission android:name="android.permission.INTERNET"/>


activity

String path = editText.getText().toString();
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(8000);
conn.setConnectTimeout(8000);
if (conn.getResponseCode() == 200){
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
imageView.setImageBitmap(bitmap);
}else{
Toast.makeText(MainActivity.this, "url错误", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
}


结果



API14+

但是在API 14 后的android系统中,主线程不允许访问网络,以预防网络耗时所造成的阻塞报错是

android.os.NetworkOnMainThreadException。

那么就用子线程来访问网络吧

editText = (EditText) findViewById(R.id.et_url);
imageView = (ImageView) findViewById(R.id.imageView);
final String path = editText.getText().toString();
Thread thread = new Thread(){
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(8000);
conn.setConnectTimeout(8000);
if (conn.getResponseCode() == 200){
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
imageView.setImageBitmap(bitmap);
}else{
Toast.makeText(MainActivity.this, "url错误", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
thread.start();


结果还是不行,报错Only the original thread that created a view hierarchy can touch its views

大致意思应该就是说这个view不是你这个线程创建的,你不能更改它,那么刷新UI就只能在主线程中执行了,但是网络又不能在主线程,如果两个线程直接并行的话,无法保证给ImageView设置Bitmap时,已经拿到了服务器发送的Bitmap,这怎么办呢?

android提供了messageQueue,looper 机制来解决线程间的通信问题,消息队列中存放着消息,一旦有新的消息,轮巡器就会发现,而默认情况下,轮巡器只是发现有新消息,并不做任何事情的,如果我们希望子线程已经得到了Bitmap,发送个主线程一个消息,并附带Bitmap对象,那么就要在主线程中添加一个Handler成员,并且重写它的HandleMessage方法,在这个方法中判断消息是否是子线程发过来的得到Bitmap的消息。当子线程拿到了Bitmap时,用Handler对象发送一个消息,打上标志,附加上Bitmap对象,发送出去,那么消息队列中就会有一个新消息,轮巡器就会发现它,并调用handler的handleMessage方法执行之。真是完美,不得不膜拜谷歌大神的智慧啊!

activity(API14+)

private final static int GOTBITMAPSUCCESS = 0;
private final static int WRONGURL = 1;
private EditText editText;
private ImageView imageView;

private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case GOTBITMAPSUCCESS:
Bitmap bm = (Bitmap) msg.obj;
imageView.setImageBitmap(bm);
break;
case WRONGURL:
Toast.makeText(MainActivity.this, "url错误", Toast.LENGTH_SHORT).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

editText = (EditText) findViewById(R.id.et_url);
imageView = (ImageView) findViewById(R.id.imageView);
}

public void showImage(View v){
final String path = editText.getText().toString();
Thread thread = new Thread(){
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(8000);
conn.setConnectTimeout(8000);
if (conn.getResponseCode() == 200){
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);

Message msg = new Message();
msg.what = GOTBITMAPSUCCESS;
msg.obj = bitmap;
handler.sendMessage(msg);
}else{
handler.sendEmptyMessage(WRONGURL);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
thread.start();
}


总结:

1.耗时操作不能放在主线程中,网络是谷歌强制要求剥离主线程的,其他的耗时操作,如读取数据等,应该自觉遵守规范,写在其他线程中去。

2.子线程不能刷新UI,刷新UI必须要用主线程。

3.线程间的通讯可以使用MessageQueue-looper-Handler机制来实现。其中message可以携带对象传送数据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: