与多线程结合使用的消息处理类Handler、Message

1. 消息处理类——Handler 

  消息处理类(Handler)允许发送和处理Message或Runnable对象到其所在线程的MessageQueue中。Handerl有以下两个主要作用:

  1. 将Message或Runnable应用post()方法或sendMessage()方法发送到MessageQueue中,在发送时可以指定延迟时间、发送时间或者要携带的Bundle数据。当MessageQueue循环到该Message时,调用相应的Handler对象的handlerMessage()方法对其进行处理。
  2. 在子线程中与主线程进行通信,也就是在工作线程中与UI线程进行通信。

   说明:在一个线程中,只能有一个Looper和MessageQueue,但是,可以有多个Handler,而且这些Handler可以共享同一个Looper和MessageQueue。

Handler类提供的常用方法

方法  描述
handleMessage(Message msg) 处理消息的方法。通常重写该方法来处理消息,在发送消息时,该方法会自动回调。
post(Runnable r) 立即发送Runnable对象,该Runnable对象最后将被封装成Message对象
postAtTime(Runnable r, long uptimeMillis) 定时发送Runnable对象,该Runnable对象最后将被封装成Message对象
postDelayed(Runnable r, long delayMillis)

延迟多少毫秒发送Runnable对象,该Runnable对象最后将被封装成Message对象

sendEmptyMessage(int what) 发送空消息
sendMessage(Message msg) 立即发送消息
sendMessageAtTime(Message msg, long uptimeMillis) 定时发送消息
sendMessageDelayed(Message msg, long delayMillis) 延迟多少毫秒发送消息

2. 消息类——Message

   消息类(Message)被存放在MessageQueue中,一个MessageQueue中可以包含多个Message对象。每个Message对象可以通过Message.obtain()方法或者Handler.obtainMessage()方法获得。一个Message对象具有下表所示的5个属性。

Message类的属性

属性 类型 描述
arg1 int 用来存放整型数据
arg2 int 用来存放整型数据
obj Object 用来存放发送给接收器的Object类型的任意对象
replyTo Messenger 用来指定此Message发送到何处的可选Messager对象
what int 用于指定用户自定义的消息代码,这样接收者可以了解这个消息的信息

  说明:使用Message类的属性可以携带int型的数据,如果要携带其他类型的数据,可以先将要携带的数据保存到Bundle对象中,然后通过Message类的setDate()方法将其添加到Message中。

  综上所述,Message类的使用方法比较简单,只要在使用它时,注意以下3点即可:

  • 尽管Message有public的默认构造方法,但是通常情况下,需要使用Message.obtain()方法或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源。
  • 如果一个Message只需要携带简单的int型信息,应优先使用Message.arg1和Message.arg2属性来传递信息,这比用Bundle更省内存。
  • 尽可能使用Message.what来标识信息,以便用不同的方式处理Message。

 3. 用Handler和Message实现一个每隔1秒更新时间的程序

1. 布局文件activity_main.xml内容如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.tiaoshi.MainActivity" >
    
    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="每秒变化的文本" />
    <Button
        android:id= "@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/start" />
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/stop" />

</LinearLayout>

2. MainActivity内容如下

package com.example.tiaoshi;

import java.util.Date;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity implements Runnable {
    private Button startBtn = null; 
    private Button stopBtn = null;
    private Thread thread = null;
    private Handler handler = null;
    private TextView text = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.text = (TextView) this.findViewById(R.id.text);
        //处理消息
        handler = new Handler(){

            @Override
            public void handleMessage(Message msg) {
                if(msg.what==0x101){
                    text.setText("当前时间为:"+new Date());
                }
                super.handleMessage(msg);
            }
            
        };
        this.startBtn = (Button) this.findViewById(R.id.btn1);
        startBtn.setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View arg0) {
                thread = new Thread(MainActivity.this);
                thread.start();
                text.setText("当前时间为:"+new Date());
            }
            
        });
        this.stopBtn = (Button) this.findViewById(R.id.btn2);
        stopBtn.setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View arg0) {
                if(thread!=null){
                    thread.interrupt();
                    thread = null;
                }
                Log.i("提示", "中断线程");
            }
            
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void run() {
        try {
            while(!Thread.currentThread().isInterrupted()){
                Message m = handler.obtainMessage();   //获取一个空的message
                m.what = 0x101;        //设置消息标识
                handler.sendMessage(m);  //发送消息
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }

    @Override
    protected void onDestroy() {
        if(thread!=null){
            thread.interrupt();
            thread = null;
        }
        super.onDestroy();
    }
}

4. 创建Handler对象发送并处理消息

  在Eclipse中创建Android项目,创建一个继承了Thread类的LooperThread,并在重写的run()方法中,创建一个Handler对象并处理消息。

  在Android中,一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQueue(消息队列)。MessageQueue用于存放Message(消息),在MessageQueue中,存放的消息按照FIFO(先进先出)原则执行,由于MessageQueue被封装到Looper里面了,所以这里不对MessageQueue进行过多介绍。

  Looper对象用来为一个线程开启一个消息循环,用来操作MessageQueue。默认情况下Android中新创建的线程是没有开启消息循环的。但是主线程除外,系统自动为主线程创建Looper对象,开启消息循环。所以,当我们在主线程中,应用下面的代码创建Handler对象时,就不会出错,而如果在创建的非主线程中,应用下面的代码创建Handler对象时,将产生异常信息。

Handler handler = new Handler(); 

  如果想要在非主线程中,创建Handler对象,首先需要使用Looper类的prepare()方法来初始化一个Looper对象,然后创建这个Handler对象,再使用Looper类的loop()方法启动Looper,从消息队列里获取和处理消息。

  Looper类提供的常用方法如下表所示。

方法 描述
prepare() 用于初始化Looper
loop() 调用loop()方法后,Looper线程就开始真正工作了,它会从消息队列里获取消息和处理消息
myLooper() 可以获取当前线程的Looper对象
getThread() 用于获取Looper对象所属的线程
quit() 用于结束Looper循环

  注意:写在Looper.loop()之后的代码不会被执行,这个函数内部是一个循环,当调用Handler.getLooper().quit()方法后,loop()方法才会终止,其后面的代码才能得以执行。

1. 创建一个继承了Thread类的LooperThread,并在重写的run()方法中,创建一个Handler对象发送并处理消息,关键代码如下:

package com.example.tiaoshi;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class LooperThread extends Thread {
    public Handler handler; //声明一个Handler对象
    @Override
    public void run() {
        super.run();
        Looper.prepare(); //初始化Looper对象
        //实例化一个Handler对象
        handler = new Handler(){

            @Override
            public void handleMessage(Message msg) {
                Log.i("Looper", String.valueOf(msg.what));
            }
            
        };
        //获取一个消息
        Message m = handler.obtainMessage();
        //设置Message的what属性的值
        m.what = 0x11;
        // 发送消息
        handler.sendMessage(m);
        //启动Looper
        Looper.loop();
    }
    
}

2. 在MainActivity的onCreate()方法中,创建一个LooperThread线程,并开启该线程,关键代码如下:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LooperThread thread = new LooperThread(); //创建一个线程
        thread.start();  //开启线程
    }
posted @ 2015-10-30 17:11  ~风轻云淡~  阅读(1239)  评论(0编辑  收藏  举报