Handler消息机制

来源:互联网 发布:2k16捏脸数据 詹姆斯 编辑:程序博客网 时间:2024/06/03 19:47

Handler消息机制的主要用于Android线程之间的通讯,它的常用用法有两种:一种就是在子线程发送消息,主线程(UI线程)处理消息,这种用法一般用于更新UI;另一种是主线程发送消息,子线程处理消息,这种用法一般用于子线程进行耗时操作。本文将从以下几个方面对handler消息机制进行介绍:

  • 各参与者的职责
  • 通讯过程
  • 示例
  • 补充说明

-----------------------------------------------------------------------------------------------------------------------------

1.各参与者的职责:

Message:消息,它包含了消息ID,消息的传输的Target对象以及待处理的数据,消息发出后统一进入MessageQueue中。

MessageQueue:消息队列,用于存储发出后的消息。

Handler:负责消息的发送处理。

Looper:封装消息队列和消息循环过程,主要用于消息的收集和分发。

Thread:消息循环的执行场所。


2.通讯过程

消息的创建

Message msg = mHandler.obtainMessage();msg.obj = obj;msg.what = what;
       public final Message obtainMessage()    {        return Message.obtain(this);    }
       public static Message obtain(Handler h) {        Message m = obtain();        m.target = h;        return m;    }
       public static Message obtain() {        synchronized (mPoolSync) {            if (mPool != null) {                Message m = mPool;                mPool = m.next;                m.next = null;                return m;            }        }        return new Message();    }

由上面的代码我们可以知道Message类有一个实例链表,它的头结点用mPool表示,这里我们称它为消息池,它的里面存储的消息都为空白消息,它的目的就是用于穿件消息时提供一个空白的消息,消息的创建首先判断消息池是否为空,如果不为空则取它的头结点返回,如果为空则新建一个Message实例返回。


消息的发送

msg.sendToTarget();
或者我们也可以使用handler发送消息

mHandler.sendMessage(msg);
    public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }
    public boolean sendMessageAtTime(Message msg, long uptimeMillis)    {        boolean sent = false;        MessageQueue queue = mQueue;        if (queue != null) {            msg.target = this;            sent = queue.enqueueMessage(msg, uptimeMillis);        }        else {            RuntimeException e = new RuntimeException(                this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);        }        return sent;    }

不管是直接通过handler发送消息还是调用Message的类方法最终都会通过Handler类的sendMessageAtTime方法将消息放入消息队列中,需要指出的是如果消息队列不存在则会报错,这点后面会有说明。


消息的分发

消息的分发是由Looper进行的,因此如果需要使用Handler进行消息的传递与处理则必须为Handler指定相应的Looper对象,默认情况下只有主线程(UI)线程创建了Looper对象,因此如果我们需要使用自定义线程的Looper进行消息的分发则必须为其创建Looper对象。Looper对象的创建采用Looper.prepare()方法实现,

class CustomThread extends Thread {  @Override  public void run() {  Looper.prepare();mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {}};Looper.loop();}  }
Looper.prepare()方法的具体实现为:
    public static final void prepare() {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());    }
prepare方法首先判断当前线程是否已经创建了Looper对象,如果没有则创建一个Looper对象,然后将其与当前线程关联起来,需要注意的是如果当前线程已经创建了Looper对象调用prepare方法则会抛出异常。

为线程创建了Looper对象后就可以通过Looper.loop()方法进行消息循环了,其具体实现为:

public static final void loop() {        Looper me = myLooper();        MessageQueue queue = me.mQueue;        while (true) {            Message msg = queue.next(); // might block            //if (!me.mRun) {            //    break;            //}            if (msg != null) {                if (msg.target == null) {                    // No target is a magic identifier for the quit message.                    return;                }                if (me.mLogging!= null) me.mLogging.println(                        ">>>>> Dispatching to " + msg.target + " "                        + msg.callback + ": " + msg.what                        );                msg.target.dispatchMessage(msg);                if (me.mLogging!= null) me.mLogging.println(                        "<<<<< Finished to    " + msg.target + " "                        + msg.callback);                msg.recycle();            }        }    }
loop函数取得当前线程的MessageQueue,然后通过一个while循环不断从MessageQueue中取出消息通过消息的target handler进行分发,消息处理后然后进行消息的回收,消息分发为:
    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }
首先判断Message本身是否有回调接口,如果有则调用自己的回调接口,如果没有再判断target handler是否有回调,有则使用Handler的回调处理消息,否则使用Handler的handleMessage处理消息。

消息的回收实现为:

    public void recycle() {        synchronized (mPoolSync) {            if (mPoolSize < MAX_POOL_SIZE) {                clearForRecycle();                                next = mPool;                mPool = this;            }        }    }
void clearForRecycle() {        what = 0;        arg1 = 0;        arg2 = 0;        obj = null;        replyTo = null;        when = 0;        target = null;        callback = null;        data = null;    }
首先对Message的各项属性进行清零和置空,然后将Message的next指针指向消息池(不是消息队列)的头结点,再将消息池的头结点指向当前Mesage。


3.示例

package com.example.messagehandletest;import java.lang.ref.WeakReference;import java.util.Random;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.app.Activity;import android.util.Log;import android.widget.TextView;public class MainActivity extends Activity {private static final int ADD = 0;private static final int MINUS = 1;private  int currentValue ;private TextView textView;private  CustomHandler mainThreadHandler;private CustomHandler subThreadHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = (TextView)this.findViewById(R.id.textView);currentValue = 0;mainThreadHandler = new CustomHandler(this);UIUpdateThread handlerThread =  new UIUpdateThread();handlerThread.start();LogThread logThread = new LogThread();logThread.start();}private static  class CustomHandler extends Handler{private WeakReference<MainActivity>  mainActivity ;public CustomHandler(MainActivity mainActivity){this.mainActivity = new WeakReference<MainActivity>(mainActivity);}public CustomHandler(){}public void handleMessage(Message message){if(mainActivity.get() == null){return ;}switch(message.what){case ADD:mainActivity.get().currentValue += 1;mainActivity.get().textView.setText(mainActivity.get().currentValue +"");break;case MINUS:mainActivity.get().currentValue -= 1;mainActivity.get().textView.setText(mainActivity.get().currentValue + "");break;}if(mainActivity.get().subThreadHandler == null){Log.w("MessageHandleTest", "null");return ;}mainActivity.get().subThreadHandler.sendEmptyMessage(message.what);}}private  class UIUpdateThread extends Thread{public void run(){int i = 0;while((i++) < 1000){int what = new Random(System.currentTimeMillis()).nextInt(100)%2;mainThreadHandler.sendEmptyMessage(what);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}subThreadHandler.removeCallbacksAndMessages(null);}}private class LogThread extends Thread{public void run(){Looper.prepare();subThreadHandler = new CustomHandler(){public void handleMessage(Message message){switch(message.what){case ADD:Log.w("MessageHandleTest", "add");break;case MINUS:Log.w("MessageHandleTest", "minus");break;default :Log.w("MessageHandleTest", "else");}}};Looper.loop();}}}
这里定义了两个Handler实例,其中一个是在主线程创建的,它的处理函数运行于主线程,用于读取UIUpdateThread线程发过来的消息进行UI更新,由于主线程已经默认创建了Looper对象和消息队列,并且消息循环已经启动,因此在主线程可以直接创建Handler实例;另一个是在LogThread创建的,它的处理函数运行于LogThread线程,它读取主线程发过来的消息打印Log日志。


4 补充说明

Handler对象创建于哪个线程那么它的处理函数就运行于哪个线程;

Handler对象关联的Looper对象不一定是Handler对象创建时所在线程的Looper对象,可以显示的指定Handler关联的的Looper对象;

自定义的内部Handler类最好定义为Staic类型的,因此非静态内部类在实例化的时候会隐式的引用到外部对象的一个实例,如过外部类为Activity,在其destroy的时候可能Handler对象可能依然被别的对象引用(如有延迟消息),导致外部Activity类的实例不能释放,造成内存泄露。

0 0
原创粉丝点击