3.2 Handler消息处理机制
Android在设计的时候引入了Handler消息机制,每一个消息发送到主线程的消息队列中,消息队列遵循先进先出原则,发送消息并不会阻塞线程,而接收线程会阻塞线程。我们接着来看看在Android中对Handler是如何定义的?
3.2.1 Handler概念分析
Handler的官方定义如下:
A Handler allows you to send and process Message and Runnable objects associated with a thread抯 MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
Handler允许发送并处理Message消息,Message对象通过主线程的MessageQueue消息队列相关联的 Message 和 Runnable 对象进行存取。每个Handler实例对Message消息发送和接收与对应主线程和主线程的消息队列有关。当创建一个新Handler时,Handler就属于当前主线程,主线程的MessageQueue消息队列也同步创建,即Handler会绑定到创建该Handler主线程/消息队列中。然后,Handler就可以通过主线程的消息队列发送和接收Message消息对象了。
3.2.2 Handler特性
1)Android里没有全局Message Queue消息队列,每个Activity主线程都有一个独立的Message Queue消息队列,消息队列采用先进先出原则。不同APK应用不能通过Handler 进行Message通信,同一个APK应用中可通过Handler对象传递而进行Message通信。
2)每个Handler实例都会绑定到创建它的线程中(一般位于主线程,即Activity线程),但Handler实例均可在主线程或子线程中创建。
3)Handler发送消息使用Message Queue消息队列,每个Message发送到消息队列里面,遵循先进先出原则;发送消息采用异步方式不会阻塞线程,而接收线程采用同步方式会阻塞线程,所以当Handler 处理完一个 Message 对象后才会接着去取下一个消息进行处理。
3.2.3 常用Handler消息处理机制讲解
我们根据几种常用Handler消息传递的使用方式用一个实例来对照讲解。
创建一个新Activity,包含4个button按钮,分别是button1、button2、button3和button4,设置它们的onClick点击事件,本例中每个button在响应点击事件中实现不同方式的Handler消息处理机制。
button1:完成主线程中传递Handler消息处理机制。
button2:在子线程中完成Handler消息处理机制。
button3:在其他线程中传入Handler对象并完成其消息处理机制。
button4:一个验证,在其他线程中更新ActivityUI界面会抛出异常。
1. 新建一个Activity
首先,创建一个新Activity,在布局文件中加入button1到button4 4个Button按钮,然后在代码中加入这4个Button的创建及响应onClick事件代码,关键代码如下:
public class AndroidTestActivity extends Activity implements OnClickListener {
Button button1 = null;
Button button2 = null;
Button button3 = null;
Button button4 = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//设置Button的onClick事件
button1 = (Button)findViewById(R.id.button1);
button1.setOnClickListener(this);
……//button2、button3、button4的创建和设置其点击事件的代码参考button1即可
}
//实现OnClickListener接口未实现的onClick方法,此方法响应前面创建的4个Button点击事件
public void onClick(View v) {
dealOnClick(v.getId());//进入onClick事件处理
}
- 自定义MyHandler类
接着,在Activity类中自定义MyHandler类,继承自Handler类,覆盖父类handleMessage方法,并在此方法中实现接收到Handler消息后的处理逻辑,使用Toast显示Handler传递的消息内容,其关键代码如下:
public class MyHandler extends Handler {
//重写构造方法可通过Looper创建MyHandler对象
public MyHandler(Looper myLooper) {
super(myLooper);
}
public MyHandler() {
}
//重写handleMessage方法,定义接收到MyHandler对象发送的Message消息后由此方法进行响应
@Override
public void handleMessage(Message msg) {
String str = "";//Toast显示的文本内容
switch(msg.what){//根据Handler消息的不同消息类型,组合不同的String字符串
case 1:
//msg.obj为Message对象传递的Object对象,本例中为String对象
str = "1:" + msg.obj;
break;
……//省略对Handler消息类型为2和3的代码,读者可结合前面的代码自行加入处理逻辑代码
}
//使用str字符串内容构造Toast对象
Toast toast = Toast.makeText(AndroidTestActivity.this, str, Toast.LENGTH_SHORT);
toast.show();//Toast显示Handler接收消息
}
}
至此,本例的基本框架已经基本完成。回想一下,从button1到button4共4个按钮的创建及响应onClick事件处理;自定义的MyHandler及handleMessage响应Handler消息处理逻辑;这些都已经完成,还差什么呢?还没有完成在onClick方法中调用的dealOnClick方法处理逻辑,接下来完成dealOnClick方法的处理逻辑。 - Handler消息发送和处理方法
本例中选择三种比较常用的Handler消息发送和处理方法,当然Android Handler的消息发送处理机制不只限于本例的这三种方法,其他的方法留给读者去学习和总结。
(1)dealOnClick方法加入针对button1点击事件处理逻辑
在主线程中创建Handler和Message对象,最后Handler发送消息也在主线程中完成,其代码详情如下:
switch (buttonId) {
case R.id.button1://button1处理机制
mHandler = new MyHandler();//主线程中创建自定义MyHandler对象,这里还需要在//AndroidTestActivity类中增加mHandler属性的声明
// mHandler创建Handler消息类型为1、消息对象为字符串的Message对象
m = mHandler.obtainMessage(1, (Object)"Main thread send message by Message Object");
m.sendToTarget();//Handler发送消息
break;
回顾button1点击事件的响应逻辑代码,自定义的Handler对象和Message对象均在Activity主线程中创建,Handler发送消息后,在自定义的Handler中handleMessage方法中响应并接收消息。我们可运行此实例验证button1的逻辑是否正确。
注意 MyHandler的构造方法有两种,区别是不带Looper对象和带Looper对象,在这里为什么使用不带Looper对象的构造方法呢?有什么特殊处理吗?既然有Android源代码,我们进去看看就清楚了。
在Eclipse中打开Android 4.0.3源代码工程,接着打开android.os.Handler类,Handler的不带Looper对象的构造方法代码如下:
mLooper = Looper.myLooper();//获取了Looper对象
if (mLooper == null) { //我们只能在Activity主线程中使用不带Looper对象的构造方法创建
//Handler对象,否则获取不了Looper,接着便会抛出RuntimeException的异常
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
(2)dealOnClick方法加入针对button2点击事件处理逻辑
在子线程中异步创建Handler和Message对象,并发送Handler消息,其代码详情如下:
switch (buttonId) {
case R.id.button2:
InnerMyThread imt = new InnerMyThread();
imt.start();//内部线程对象异步处理button2事件
break;
接着完成InnerMyThread的处理逻辑,其代码详情如下:
private class InnerMyThread extends Thread {
@Override
public void run() {
mHandler = new MyHandler();//仅能在Activity主线程内采用不带Looper对象创建Handler//对象,此处会抛出异常
mHandler = new MyHandler(Looper.myLooper());//Looper.myLooper获取looper为//null,产生异常
mHandler = new MyHandler(Looper.getMainLooper());//通过Looper.getMainLooper()//获取父类的looper可成功创建Handler对象并将Message发送到父类
Message m = mHandler.obtainMessage(2, (Object)"Inner thread send message by Message Object");//子线程中创建Message对象
m.sendToTarget();//子线程中发送Message
}
}
回顾button2点击事件的响应逻辑代码,子线程中异步创建Handler和Message对象,并在子线程中发送Handler消息,接着在主线程中的handleMessage方法中响应子线程发出的Handler消息。
(3)dealOnClick方法加入针对button3点击事件处理逻辑
在主线程中创建Handler对象,子线程创建Message消息并发送Handler消息,代码如下:
switch (buttonId) {
case R.id.button3:
mHandler = new MyHandler();
MyThread mt = new MyThread(mHandler);//传递mHandler对象给子线程
mt.start();//子线程对象,用于异步处理Handler Message消息
break;
主线程创建的Handler对象传递给子线程,在子线程中通过Handler对象创建Message对象,并在子线程中发送Handler消息,主线程在handleMessage中响应接收消息。代码如下:
public class MyThread extends Thread {
private Handler mHandler;
public MyThread (Handler mHandler) {
this.mHandler = mHandler;
}
@Override
public void run() {
Message m = mHandler.obtainMessage(3, (Object)"Other thread send message by mHandler");
m.sendToTarget();
}
}
(4)dealOnClick方法中加入针对button4点击事件处理逻辑
在子线程中更新Activity界面,请读者自己完善代码,验证在子线程中更新主线程的界面究竟会有什么异常出现。
3.2.4 Handler的作用
根据Handler处理Message消息相关特性和前面的实例,可以得出这样的结论:Handler主要作用是异步处理较费时的逻辑,优先将界面返回给用户,异步处理完成后再去更新用户界面。
在Android中当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程专门管理界面中的UI控件,对事件进行分发, 比如点击一个 Button按钮,Android会分发事件到具体的Button按钮上,来响应用户的操作。
如果此时需要一个耗时的操作,例如联网读取数据或者读取本地较大的一个文件,不能把这些操作放在主线程中,放在主线程中的话,界面会出现假死现象, 如果5秒还没有完成,会收到Android系统的一个错误提示“强制关闭”,在这样的情况下需要把这些耗时的操作放入子线程中去处理,子线程处理完成后涉及再去更新主线程的UI界面,而Android主线程是线程不安全的。也就是说,更新UI只能在主线程中更新,子线程中无法更新主线程的界面。
这时,Android设计出Handler来解决这个复杂的问题,由于Handler运行在主线程中(Activity UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接收子线程传过来的Message对象(包含消息标识和数据对象)的责任,从而配合主线程更新UI。而这些消息都通过主线程的消息队列按照先进先出的方式进行发送和接收并做相关处理。