从互联网获取图片
一个移动的平台开发,很多资源是不可能一直保存在本地的,更多实时性的东西,是需要直接通过网络及时获取的。这里通过一个从网上获取图片展示到ImageView的例子,来讲解一下这个功能的实现。
在Android4.0之后,增加了一些新特性,也增加了一些限制。其中有一个限制就是不能在主线程中访问网络,必须另开一条线程访问。但是这里又存在另外一个问题,在子线程中,无法直接操作UI控件的属性。如果你们使用的开发平台是Android4.0之下,就不存在这个问题,直接在主线程中访问网络操作UI控件即可。
我的解决方案就是,通过Thread类,进行多线程访问网络,再通过Handler类,进行消息传递。对于Thread类,是Java的知识,不再详细讲解,对于Handler类,这里简要说明一下。
在Android平台下,不允许Activity新启动的线程访问该Activity里的界面UI控件,这样就会导致新启动的线程无法动态改变界面UI控件的属性值。所以就需要借助Handler的消息传递机制来实现。Handler类的主要作用有两个:
- 在新启动的线程中发送消息。
- 在主线程中获取、处理消息。
上面描述的两个作用,发送消息好说,在需要的时候发送,那怎么确定什么时候接收消息呢?为了让主线程能接受并处理新启动的线程发送的消息,Android通过回调的方式来实现,开发人员只需要重写Handler类中处理消息的方法,handleMessage(Message)即可,其中Message封装了发送过来的消息。
Handler包含如下方法,用于实现发送和处理消息:
- void handleMessage(Message msg):处理消息的方法,用于被重写。
- final boolean hasMessage(int what):检查消息队列中是否包含what属性为指定值的消息。
- sendEmptyMessage(int what):立即发送空消息。
- final boolean sendEmptyMessageDelayed(int what,long delayMillis):指定delayMillis毫秒之后发送空消息。
- final boolean sendMessage(Message msg):立即发送消息。
- final boolean sendMessageDelayed(Message msg,long delayMillis):指定delayMillis毫秒之后发送消息。
Message封装了线程中传递的消息,如果对于一般的数据,Message提供了getData()和setData()方法来获取与设置数据,其中操作的数据是一个Bundle对象,这个Bundle对象提供一系列的getXxx()和setXxx()方法用于传递基本数据类型,对于基本数据类型,使用起来很简单,这里不再详细讲解。而对于复杂的数据类型,如一个对象的传递就要相对复杂一些。在Bundle中提供了两个方法,专门用来传递对象的,但是这两个方法也有相应的限制,需要实现特定的接口,当然,一些Android自带的类,其实已经实现了这两个接口中的某一个,可以直接使用。方法如下:
- putParcelable(String key,Parcelable value):需要传递的对象类实现Parcelable接口。
- pubSerializable(String key,Serializable value):需要传递的对象类实现Serializable接口。
还有另外一种方式在Message中传递对象,那就是使用Message自带的obj属性传值,它是一个Object类型,所以可以传递任意类型的对象,Message自带的有如下几个属性:
- int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。
- int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。
- Object obj:传递一个任意的对象。
- int what:定义的消息码,一般用于确定消息的来源。
下面这个示例,使用了两种方式获取传递消息,以一个随机数确定。在这个示例中,访问网络的代码会附上但是不会详解,如果对于Android中访问网络不熟悉的朋友,可以参见我另外一篇博客,Android--Http协议。
布局代码:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 8 <Button android:id="@+id/btnInternet" android:layout_width="wrap_content" 9 android:layout_height="wrap_content" android:text="下载网络图片"/> 10 <TextView android:id="@+id/tbMsgType" android:layout_width="match_parent" 11 android:layout_height="wrap_content"/> 12 <ImageView android:id="@+id/ivInternet" android:layout_width="wrap_content" 13 android:layout_height="wrap_content"/> 14 </LinearLayout>
实现代码:
1 package com.bgxt.imageviewdemo; 2 3 import java.io.InputStream; 4 import java.net.HttpURLConnection; 5 import java.util.Random; 6 7 import com.bgxt.httputils.HttpUtils; 8 9 import android.app.Activity; 10 import android.graphics.Bitmap; 11 import android.graphics.BitmapFactory; 12 import android.os.Bundle; 13 import android.os.Handler; 14 import android.os.Message; 15 import android.view.View; 16 import android.widget.Button; 17 import android.widget.ImageView; 18 import android.widget.TextView; 19 import android.widget.Toast; 20 21 public class InternetImageActivity extends Activity { 22 private Button btnInternet; 23 private ImageView ivInternet; 24 private TextView tvMsgType; 25 private Handler handler; 26 27 @Override 28 protected void onCreate(Bundle savedInstanceState) { 29 // TODO Auto-generated method stub 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_internetimage); 32 33 btnInternet = (Button) findViewById(R.id.btnInternet); 34 ivInternet = (ImageView) findViewById(R.id.ivInternet); 35 tvMsgType = (TextView) findViewById(R.id.tbMsgType); 36 37 // 定义一个handler,用于接收消息 38 handler = new Handler() { 39 @Override 40 public void handleMessage(Message msg) { 41 Bitmap bmp = null; 42 // 通过消息码确定使用什么方式传递图片信息 43 if (msg.what == 0) { 44 bmp = (Bitmap) msg.obj; 45 tvMsgType.setText("使用obj传递数据"); 46 } else { 47 Bundle ble = msg.getData(); 48 bmp = (Bitmap) ble.get("bmp"); 49 tvMsgType.setText("使用Bundle传递数据"); 50 } 51 // 设置图片到ImageView中 52 ivInternet.setImageBitmap(bmp); 53 } 54 }; 55 56 btnInternet.setOnClickListener(new View.OnClickListener() { 57 @Override 58 public void onClick(View v) { 59 //清空之前获取的数据 60 tvMsgType.setText(""); 61 ivInternet.setImageBitmap(null); 62 //定义一个线程类 63 new Thread() { 64 @Override 65 public void run() { 66 try { 67 //获取网络图片 68 InputStream inputStream = HttpUtils 69 .getImageViewInputStream(); 70 Bitmap bitmap = BitmapFactory 71 .decodeStream(inputStream); 72 Message msg = new Message(); 73 Random rd = new Random(); 74 int ird = rd.nextInt(10); 75 //通过一个随机数,随机选择通过什么方式传递图片信息到消息中 76 if (ird / 2 == 0) { 77 msg.what = 0; 78 msg.obj = bitmap; 79 } else { 80 Bundle bun = new Bundle(); 81 bun.putParcelable("bmp", bitmap); 82 msg.what = 1; 83 msg.setData(bun); 84 } 85 //发送消息 86 handler.sendMessage(msg); 87 } catch (Exception e) { 88 89 } 90 } 91 }.start(); 92 } 93 }); 94 } 95 }
访问网络类,代码:
1 package com.bgxt.httputils; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.net.HttpURLConnection; 6 import java.net.URL; 7 8 public class HttpUtils { 9 private final static String URL_PATH = "http://ww4.sinaimg.cn/bmiddle/9e58dccejw1e6xow22oc6j20c80gyaav.jpg"; 10 11 public HttpUtils() { 12 } 13 14 public static InputStream getImageViewInputStream() throws IOException { 15 InputStream inputStream = null; 16 URL url = new URL(URL_PATH); 17 if (url != null) { 18 HttpURLConnection httpURLConnection = (HttpURLConnection) url 19 .openConnection(); 20 httpURLConnection.setConnectTimeout(3000); 21 httpURLConnection.setRequestMethod("GET"); 22 httpURLConnection.setDoInput(true); 23 int response_code = httpURLConnection.getResponseCode(); 24 if (response_code == 200) { 25 inputStream = httpURLConnection.getInputStream(); 26 } 27 } 28 return inputStream; 29 } 30 }