首页 > 代码库 > Android Handler消息机制

Android Handler消息机制

在上一篇文章《Android AsyncTask异步任务》中我们介绍了如何使用AsyncTask异步处理网络通信和UI更新。在本文中将使用Handler消息机制来异步处理网络通信和UI更新。

Google参考了Windows的消息机制,在Android系统中实现了一套类似的消息机制。学习Android的消息机制,有几个概念(类)必须了解:
1、Message
消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。
2、Message Queue
消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
3、Handler
Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。
4、Looper
循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。
5、Thread
UI Thread 通常就是Main Thread,而Android启动程序时会替它建立一个Message Queue。
每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。

运行机理:
每个线程都可以并仅可以拥有一个Looper实例,消息队列MessageQueue在Looper的构造函数中被创建并且作为成员变量被保存,也就是说MessageQueue相对于线程也是唯一的。Android应用在启动的时候会默认会为主线程创建一个Looper实例,并借助相关的Handler和Looper里面的MessageQueue完成对Activities、Services、Broadcase Receivers等的管理。而在子线程中,Looper需要通过显式调用Looper. prepare()方法进行创建。prepare()方法通过ThreadLocal来保证Looper在线程内的唯一性,如果Looper在线程内已经被创建并且尝试再度创建"Only one Looper may be created per thread"异常将被抛出。
 
Handler在创建的时候可以指定Looper,这样通过Handler的sendMessage()方法发送出去的消息就会添加到指定Looper里面的MessageQueue里面去。在不指定Looper的情况下,Handler绑定的是创建它的线程的Looper。如果这个线程的Looper不存在,程序将抛出"Can‘t create handler inside thread that has not called Looper.prepare()"。
 
整个消息处理的大致流程是:
1. 包装Message对象(指定Handler、回调函数和携带数据等);
2. 通过Handler的sendMessage()等类似方法将Message发送出去;
3. 在Handler的处理方法里面将Message添加到Handler绑定的Looper的MessageQueue;
4. Looper的loop()方法通过循环不断从MessageQueue里面提取Message进行处理,并移除处理完毕的Message;
5. 通过调用Message绑定的Handler对象的handlerMessage()方法完成对消息的处理。

下面仍然以聚合数据空气质量城市空气PM2.5指数数据接口为例来演示Handler的使用。
实例:HandlerDemo
运行效果:
技术分享

代码清单:
布局文件:activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:orientation="horizontal" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="城市:"
            android:textSize="23sp" />

        <EditText 
            android:id="@+id/city"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="3"
            android:inputType="text" />
    </LinearLayout>

    <Button
        android:id="@+id/query"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="查询" 
        android:textSize="23sp" />
    
    <TextView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

Java源代码文件:MainActivity.java
package com.rainsong.handlerdemo;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
    private static final String JUHE_URL_ENVIRONMENT_AIR_PM = 
                                    "http://web.juhe.cn:8080/environment/air/pm";
    private static final String JUHE_APPKEY = "你申请的APPKEY值";

    private static final int MSG_SUCCESS = 0;
    private static final int MSG_FAILURE = 1;

    EditText et_city;
    Button btn_query;
    TextView tv_result;
    String city;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
            case MSG_SUCCESS:
                tv_result.setText((String)msg.obj);
                break;
            case MSG_FAILURE:
                Toast.makeText(MainActivity.this, "查询失败",
                                    Toast.LENGTH_LONG).show();
                tv_result.setText("");
                break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et_city = (EditText)findViewById(R.id.city);
        tv_result = (TextView)findViewById(R.id.result);
        btn_query = (Button)findViewById(R.id.query);

        btn_query.setOnClickListener(new OnClickListener() {
            public void onClick(View view) {
                city = et_city.getText().toString();
                if (city.length() < 1) {
                    Toast.makeText(MainActivity.this, "请输入城市名",
                            Toast.LENGTH_LONG).show();
                    tv_result.setText("");
                    return;
                }
                
                new Thread() {
                    public void run() {
                        ArrayList<NameValuePair> headerList = new ArrayList<NameValuePair>();
                        headerList.add(new BasicNameValuePair("Content-Type", "text/html; charset=utf-8"));

                        String targetUrl = JUHE_URL_ENVIRONMENT_AIR_PM;

                        ArrayList<NameValuePair> paramList = new ArrayList<NameValuePair>();
                        paramList.add(new BasicNameValuePair("key", JUHE_APPKEY));
                        paramList.add(new BasicNameValuePair("dtype", "json"));
                        paramList.add(new BasicNameValuePair("city", city));

                        for (int i = 0; i < paramList.size(); i++) {
                            NameValuePair nowPair = paramList.get(i);
                            String value = http://www.mamicode.com/nowPair.getValue();>
API知识点
public class
Handler
extends Object

android.os.Handler

Class Overview
A Handler allows you to send and process Message and Runnable objects associated with a thread‘s 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.

Public Constructors
Handler()
Default constructor associates this handler with the Looper for the current thread.

void handleMessage(Message msg)
Subclasses must implement this to receive messages.

final Message obtainMessage()
Returns a new Message from the global message pool.

final Message obtainMessage(int what, Object obj)
Same as obtainMessage(), except that it also sets the what and obj members of the returned Message.

public final class
Message
extends Object
implements Parcelable

android.os.Message

Class Overview
Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.

void sendToTarget()

Sends this Message to the Handler specified by getTarget().



Android Handler消息机制