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

Android Handler 异步消息处理机制

2015-08-13 20:08 561 查看

引言

首先说明我是怎么接触到这个Handler对象的,当我尝试在Activity中处理网络传输的时候,报了异常。上网查了下资料,知道了再Android 4.0 版本以上是不能在主线程(UI线程 | Activity)中进行网络连接, 网络连接通常执行耗时操作,这样会造成主线程阻塞,主线程阻塞在手机上看就是卡死机了,这样会带来很不好的用户体验。好,既然不能在主线程执行,那我就创建一个线程呗,一运行问题又来了。经过查询得知,Android 系统也不允许在子线程执行UI操作,既然不能再子线程操作,那就回Activity操作吧。经过查询,获知Handler这个对象。 然后经过上网大量查询资料,下面列出我的一些理解。

概念

Handler 翻译过来就是一个处理器,负责消息处理,Android 系统中UI线程是不安全的,并且不能通过子线程更新UI,所以引入了Handler这个东西。那么Handler是怎么东西?Handler负责子线程和主线程之间发送消息。

Handler机制

首先我要引入4个对象:

MessageQueue

Looper

Handler

Message

MessageQueue 是一个消息队列,在Activity创建的时候会自动创建一个MessageQueue 对象和一个Looper对象。然后Looper又是什么鬼?Looper 我们暂且称为轮询器,Handler 是处理消息的处理器,Message就是传说中的消息。

下面我来理一理他们之间的关系:

首先Activtity创建,自动创建MessageQueue 和 Looper

第二步是Looper 会一直循环检测MessageQueue 里面是否有消息,如果有消息,就通知给Handler处理

第三步,Handler 收到通知,然候执行
handleMessage(Message msg)
方法(下面会讲到这个方法)

这是系统内部自动执行的操作,我这里暂且不研究源代码怎么做的,大家有兴趣的话,网上有很多分析的,我这里就只是宏观上展示一下Handler处理的过程。

下面通过一个图来看看他们的关系:



图可能画得有点丑,不过也能表达我的意思了,就是Looper一直在检查MessageQueue是否有消息,如果有,就把消息给Handler处理,而在我们开发中,实际要使用的仅仅是一个Handler 对象,Handler对象不是系统创建的,需要我们在Activity中创建,然后在子线程中调用, 在创建的时候需要复写一个
HandleMessage(Message msg)
方法,这个方法是收到消息的时候调用。

static Handler handler = new Handler() {
public void handleMessage(Message msg) {
//处理代码
}
};


子线程调用方法是
sendMessage(Message msg)
其实有很多重载的方法,这里只列举一个。

Message msg = Message.obtain();
handler.sendMessage(msg);


通常我们需要携带数据,这时候就需要说到Message对象了,Message对象有一个obj 属性,是一个Object类型了,你可以赋值任何的数据类型。然后如果有多个消息发送个Handler,怎么区别呢? Message还有一个成员叫做 what ,你可以指定一个int的值给它,在handlerMessage中就可以通过
switch(msg.what)
方法来分别处理不同的消息了。

使用Handler需要注意的地方

除了在Activity中会自动创建MessageQueue 和 Looper 之外,子线程是没有这两个对象的,所以在使用的时候要小心。

调用了Looper的
Looper.prepare()
Looper.loop()
两个才会创建MessageQueue对象并且开始检测消息队列的。具体源码我就不分析了。

最后我给大家一个完整的Handler使用实例,该类是用于下载网络图片的。

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends Activity {
static ImageView im;
static Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what){
case 0:
Bitmap bitmap = (Bitmap) msg.obj;
im.setImageBitmap(bitmap);
break;
case 1:

}

}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
im = (ImageView) findViewById(R.id.imageView);

}

/**
* Android 4.0 以上不允许在UI线程执行网络等耗时操作 并且只有主线程才能操作UI,所以要用到Handler
*
*/
public void click(View v) {
final String path = "http://192.168.32.113/crm/ui/images/ban_1.gif";
//判断缓存中是否存在图像
final File file = new File(getCacheDir(),getFileName(path));
if(file.exists()){
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
Message msg = Message.obtain();
msg = handler.obtainMessage();
// 以上两种方式获取的message对象是从message池里面获取,可以节省内存
msg.obj = bitmap;
msg.what = 0;
handler.sendMessage(msg);
System.out.println("从缓存获取");
}else{
System.out.println("从网络中获取");
Thread t = new Thread() {
public void run() {
try {
URL url = new URL(path);
// 强转为httpconnection
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 设置请求方式
conn.setRequestMethod("GET");
// 设置连接超时
conn.setConnectTimeout(5000);
// 设置读取超时
conn.setReadTimeout(5000);
// 发送请求,与服务器建立连接
conn.connect();
System.out.println(conn.getResponseCode());
if (conn.getResponseCode() == 200) {
//获取输入流
InputStream is = conn.getInputStream();
//缓存图片
File file = new File(getCacheDir(),getFileName(path));
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len = 0 ;
while((len=is.read(b))>0){
fos.write(b, 0, len);
}
fos.close();
Message msg = Message.obtain();
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
// msg = handler.obtainMessage();
// 以上两种方式获取的message对象是从message池里面获取,可以节省内存
msg.obj = bitmap;
msg.what = 0;
}else{
Message msg = Message.obtain();
msg.what = 1;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
};
};
t.start();
}
}
public String getFileName(String path){
return path.substring(path.lastIndexOf("/"));

}
}


以上代码为了能在一行显示,有些缩进太难看,不要介意哈。

如果大家有兴趣剖析一下源代码的话,网上有很多资料。各种解析,各种牛逼哄哄的分析,我写这个适合初学者宏观上理解一下Handler是运行机制,并没有深入解析。

最后我在加一点,通常在项目中,Handler对象都是定义为静态的,这样有很大好处,方便Android其他组件和Activity线程通信,而且Google也是支持我们这样做的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: