`
gogoalong
  • 浏览: 47782 次
  • 性别: Icon_minigender_1
  • 来自: 湖南
社区版块
存档分类
最新评论

[Android源码]Handler分析

 
阅读更多

一,Handler介绍

Handler:处理者。Handler的主要作用异步处理消息。主要方法是:发送消息、处理消息。当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

二,Handler的基本用法

public class MainActivity extends Activity  
{
    private final int WHAT = 100;
    private TextView mTextTitle;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextTitle = (TextView) findViewById(R.id.textview1);
        new Thread(runnable).start();
    }
 
    Runnable runnable = new Runnable(){
        @Override
        public void run() {
            int count = 0;
            for (int i = 0; i < 10; i++) {
                count ++;
            }
            Message msg = handler.obtainMessage();
            msg.what = WHAT;
            msg.arg1 = count;
            handler.sendMessage(msg);
        }
    };
    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case WHAT:
                    int count = msg.arg1;
                    mTextTitle.setText("总共人数:" + count);
                break;
            }
            return false;
        }
    });
}

2.1 构造方式

上述代码中构造Handler对象使用的是

[1]Handler handler = new Handler(new Handler.Callback()){

public boolean handleMessage(Message msg){}

}

Handler也允许使用

[2]Handler handler = new Handler(){

public void handleMessage(Message msg){}

};但是使用方式[2],eclipse会出现黄色警告[This Handler class should be static or leaks might occur]大致意思是:Handler类应该定义成静态类,否则可能导致内存泄露。根据不同的应用场景,如果Handler只需要使用handleMessage(Message msg)回调方法的话,可以使用方式[1]解决掉警告提示。如果需要重写Handler的其他方法,可以使用

static Handler handler = new Handler() {

public void handleMessage(Message msg){}

};[参考]:http://www.cnblogs.com/savagemorgan/archive/2013/01/23/2872371.html

2.2 功能描述

在子线程进行长时间逻辑算法操作(for循环),将算法操作结果显示到UI界面上。

2.3 流程描述

1,开启子线程进行计算;

2,Handler 通过sendEmptyMessage(int what)将消息添加到消息循环队列中;

3,消息循环队列取出消息回调Handler.dispatchMessage()分发消息;

4,回调方法handleMessage(Message msg)来处理消息;

5,将结果显示到TextView组件

2.4 使用总结

1,有三点需要注意,[1]Message对象是使用Message msg = handler.obtainMessage();携带int基本类型参数是使用[2]msg.arg1=count;这个可以看上一篇文章[android源码分析之Message]。[3]为什么不能直接在子线程修改TextView的显示信息,可以看文章Android:CalledFromWrongThreadException

2,使用Handler的对象的引用可以在任意线程发送消息。而处理消息只在构造Handler对象的那个线程中。Handler与Activity还有消息循环队列之间的关系图。

3,通过上面介绍基本懂得如何在代码中使用Handler。

那么Handler是如何将消息发送到消息循环队列的呢?sendMessage(Message msg)之后做了哪些操作。

三,Handler分析

3.1 构造函数

    private static final boolean FIND_POTENTIAL_LEAKS = false;
    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;
    /**
     * Constructor associates this handler with the queue for the
     * current thread and takes a callback interface in which you can handle
     * messages.
     */
    public Handler(Callback callback) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
    }

跟踪Handler源码,我们一起看看Handler的构造函数。因为FIND_POTENTIAL_LEAKS=false;所以if(FIND_POTENTIAL_LEAKS){}这段代码可以不用分析。接下来几行代码则是Handler的核心。[1]mLooper=Looper.myLooper();关联一下消息循环Looper。[2]mQueue=mLooper.mQueue;关联消息循环中的消息队列MessageQueue,作为自己的一个成员属性。Handler与消息循环以及消息队列建立联系后,Handler就可以发送消息到消息队列。

mLooper=Looper.myLooper(); :一个线程构造使用Handler,需要调用Looper.prepare(),将线程初始化成一个消息循环线程。如果在一个子线程创建Handler之前没有调用Looper.prepare()将该线程转变成消息循环线程。则会抛
Can't create handler inside thread that has not called Looper.prepare()异常。

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

mQueue=mLooper.mQueue;调用mQueue.enqueueMessage();将消息添加到消息队列;

3.2 常用函数

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)

public final boolean sendMessage(Message msg)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis
public final boolean sendMessageAtFrontOfQueue(Message msg)

3.3 消息发送

Handler允许post一个Runnable对象。Handler会将Runnable封装到Message对象中,msg.callback=runnable,之后添加将消息添加到消息队列,当消息循环队列取出此Message,直接调用Runnable的run()方法。

Handler允许send一个Message对象。Handler为开发者提供了很多封装的方法,跟踪源码,发现最后消息添加到消息队列,只有两个方法。一个是根据偏移时间[1]sendMessageAtTime,将消息插入到消息队列中。第二个方法是[2]sendMessageAtFrontOfQueue将消息添加到消息队列的头部。

下面跟踪handler.sendMessage(Message msg);

    /**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
  /**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

  /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    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;
    }
注:updateMillis= SystemClock.uptimeMillis() + delayMillis

跟踪代码我们可以注意到,消息发送最后到达sendMessageAtTime(Message msg, long uptimeMillis),其中uptimeMillis是当前系统闹钟时间+delayMillis是推迟发送间隔时间。

msg.target=this;将该handler引用添加到Message中,在消息循环中,通过msg.target.dispatchMessage()回调,分发消息。

queue.enqueueMessage(msg, uptimeMillis)则将消息添加进了消息队列。至此消息的发送和添加已经完成。

3.4 消息处理

当消息队列中有消息时,消息循环loop()从消息队列中取出消息

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;
        ...
        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                ...
                msg.target.dispatchMessage(msg);
                ...
                msg.recycle();
            }
        }
    }
Message msg = queue.next();从消息队列中取出消息,msg.target.dispatchMessage()回调Handler中的分发消息方法。

msg.recycle();将以及从消息队列中取出的消息进行回收,清空消息中存放数据,保留消息引用到消息池。

   /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {1,// 判断是否为Runnable消息
            handleCallback(msg);
        } else {
            if (mCallback != null) {2,// Handler构造为,Handler handler = new Handler(new Handler.Callback(){});
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);3,//继承Handler的子类
        }
    }
    private final void handleCallback(Message message) {
        message.callback.run();//直接调用Runnable的run() 方法
    }
消息分发根据Handler发送的消息形式,执行不同的回调。如[一,基本使用]中实现的代码方式,则是调用2,mCallback.handleMessage(msg) 。至此消息处理已经完成。
整体流程图:[引用脚本之家图片],这张图很形象的表达了消息循环的流程。

至此Handler的发送消息和处理消息流程分析完毕。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics