Android消息机制系列(2)——Handler源码解析及用法实例

本文可以和另外一篇文章结合起来看,Android消息机制之——Handler Looper源码分析

在Handler源码开篇,惯例地,有关该类的说明。强烈建议阅读理解10遍+,比任何解说的文章都好用。这里只摘出重要的部分:

     * 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.

1、Handler通过和一个线程的MessageQueue(俗称消息队列)关联,来进行Message、Runnable的发送和处理;

2、每个Handler实例对应唯一的一个线程以及这个线程对应的MessageQueue;依据Handler实例被创建方式的不同,Handler和创建它一个线程或者一个线程的MessageQueue进行绑定;

3、Handler将消息发送到它绑定的MessageQueue; 同时接收从MessageQueue中派发的消息并进行处理。

这里体现出了Handler的主要作用,即将消息切换到绑定的MessageQueue所在的线程里执行,而不管消息是从哪个线程发送过来的。

一、Handler创建方式:

根据是否指定Looper,分为两种:
(1)我们通常使用的方法,直接new Handler的形式;比如,在某个线程中:

class MyThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare(); //当前线程绑定一个MessageQueue
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
        Looper.loop();
    }
}

我们知道,Looper.prepare() 是为当前线程绑定一个Looper实例及MessageQueue,在new Handler时,用的就是当前线程的Looper实例和MessageQueue;

Looper的相关细节,可以参见另一篇文章,Android消息机制之——Handler Looper源码分析

调用的构造函数:

public Handler() {
    this(null, false);
}


public Handler(Callback callback, boolean async) {
    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;
    mAsynchronous = async;
}

这里面比较核心的两句:
mLooper = Looper.myLooper(); //和当前线程的Looper实例相关联;mQueue = mLooper.mQueue; //和当前线程的Looper实例的MessageQueue相关联。

同时我们发现,构造函数里面是可以指定callback的,这个callback会在处理消息时,也就是dispatch函数里面用到。

而我们通常的做法,在一个Activity的onCreate()函数里面new Handler(),绑定的是主线程的Looper实例和MessageQueue。具体代码可看ActivityThread源代码。

(2) 指定Looper,这是Handler不太常用的,比较特殊的构造方法,通过这种方法,Handler将和指定的Looper以及Looper的MessageQueue进行绑定;

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

二、Handler两大作用:发送消息 处理消息

发送消息的方法有:
post Runnable方式: post, postAtTime(Runnable, long), postDelayed;
send Message方式: sendEmptyMessage, sendMessage, sendMessageAtTime, sendMessageDelayed;

Runnable对象最终也是被包装成Message对象发送到Handler所绑定的MessageQueue中去:

Runnable 会被包装成设有callback的Message对象:

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

最终发送消息的方法:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

将消息插入到消息队列中去,并且将消息的target设为本Handler实例(this):

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

我们知道,Handler绑定了一个Looper实例,loop方法会不停地从MessageQueue.next()中看是否有消息,如果有消息,就去调用msg.target.dispatchMessage()进行处理,也就是message对应的Handler实例的dispatchMessage()方法进行处理:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);  // post一个Runnable,Runnable设有callback
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {  //构造函数传入callback
                return;
            }
        }
        handleMessage(msg); // 重写Handler的handleMessage()方法
    }
}

三、推荐使用obtainMessage方法去获得一个消息对象

我们一般会这样创建一个Message:

    Message msg = new Message();
    msg.what = xxx;
    msg.arg1 = xxx;
    msg.arg2 = xxx;
    handler.sendMessage(msg);

然而查看源码会发现, obtainMessage方法是获取一个Message对象效率更高的方式:

/*** Returns a new {@link android.os.Message Message} from the global  message pool. More efficient than* creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).* If you don't want that facility, just call Message.obtain() instead.*/

使用obtainMessage()方法,是从全局的MessagePool中去拿一个Message, 而不是重新创建一个,省去了创建对象申请内存的开销。

因此应尽量使用:

Message msg = handler.obtainMessage();

而不要使用:

Message msg = new Message();

参考:
任玉刚 Android开发艺术探索
http://blog.csdn.net/singwhatiwanna/article/details/48350919

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容