通过一个例子来熟悉下 Android 消息机制的使用,这个例子中主线程中有一个EditText、ImageView 和一个 Button,在 EditText 中输入图片地址后,点击 Button,主线程向后台线程发送消息请求下载图片,后台线程下载完图片后向主线程发送消息请求更新UI以显示下载的图片。通过这个例子一方面熟悉消息机制过的使用,另一方面理解 Handler 的双向传递。最终的效果如下:
主线程向后台线程发消息
后台线程向主线程发消息
示例源代码
布局文件 res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/edittext_url"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button_download"
android:text="Download Image"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/imageview_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
/>
</LinearLayout>
MainActivity 用于显示界面,作为主线程,它接收和处理更新界面的消息。
package com.wuhui.testhandler;
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
/**
* Created by Administrator on 2015/5/6.
*/
public class MainActivity extends Activity {
private EditText mEditText;
private Button mButton;
private ImageView mImageView;
private Downloader mDownloader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = (EditText)findViewById(R.id.edittext_url);
mImageView = (ImageView)findViewById(R.id.imageview_image);
/* 创建一个与主线程关联的 Handler,并传作为构造函数参数递给 Downloader 对象 */
mDownloader = new Downloader(new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case Downloader.MESSAGE_DOWNLOAD:
if (msg.obj != null) {
Bitmap bitmap = (Bitmap) msg.obj;
// 更新图片
mImageView.setImageBitmap(bitmap);
}
break;
default:
break;
}
}
});
mDownloader.start(); // 启动后台线程
mDownloader.getLooper(); // 确保后台线程中的消息循环初始化
mButton = (Button)findViewById(R.id.button_download);
mButton.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v) {
// 下载图片
mDownloader.downloadImage(mEditText.getText().toString());
}
});
}
}
Downloader 类是一个后台线程,其继承自 HandlerThread 类,所以它具有消息循环。Downloader类负责接收和处理下载图片的消息。
package com.wuhui.testhandler;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by Administrator on 2015/5/6.
*/
public class Downloader extends HandlerThread{
private static final String mName = "Downloader";
public static final int MESSAGE_DOWNLOAD = 1;
private Handler mHandler; // 当前线程的 Handler
private Handler mResponseHandler; // 主线程的 Handler
Downloader (Handler handler) {
super(mName);
mResponseHandler = handler; // 保存主线程的 Handler
}
// 该函数在消息循环开始前被调用
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
// 创建当前线程的 Handler
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String imageUrl = (String)msg.obj;
// 下载图片
handleDownloadImage(imageUrl);
}
};
}
public void downloadImage(String imageUrl) {
/**
* 这里没有直接调用 handleDownloadImage 来下载图片,而是发送消息,
* 这样做的好处是,如果连续有多张图片要下载,该方法也可以立即返回,
* 由后台线程根据消息队列中的请求依次下载图片。
* */
// 将下载图片的消息发送给当前线程的消息队列
mHandler.obtainMessage(MESSAGE_DOWNLOAD, imageUrl)
.sendToTarget();
}
private void handleDownloadImage(String imageUrl) {
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return;
}
InputStream in = connection.getInputStream();
byte[] buffer = new byte[512];
int readSize = 0;
ByteArrayOutputStream out = new ByteArrayOutputStream();
while ((readSize = in.read(buffer)) > 0) {
out.write(buffer, 0, readSize);
}
out.close();
byte[] bitmapBytes= out.toByteArray();
// 从下载的字节数据中解码出图片
Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
// 向主线程发送消息,将下载的图片作为消息参数
mResponseHandler.obtainMessage(MESSAGE_DOWNLOAD, bitmap)
.sendToTarget();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面就是全部代码了。