Android消息机制(Handler原理)-完全解析

一 概述

Handler主要被用来在子线程中访问UI线程,在ViewRootImpl中有一个checkThread()方法,对UI的操作都会有此验证。所以操作UI只能在主线程中进行。
概念:
Handler的运行由底层MessageQueue和Looper支撑。
MessageQueue消息队列,单链表存储消息列表。它是消息的存储单元。
Looper循环,它会无限循环查找是否有消息,有就处理掉。Looper创建一般被保存在TheadLoacal中。
创建:
Handler创建采用当前线程的Looper来构造消息循环系统。线程默认没有Looper
主线程(UI线程)即:ActivityThread,主线程创建时会初始化Looper,所以主线程可直接使用Handler

二 运行机制

2.1 TheadLoacal

TheadLoacal的用来保存数据,以线程作用域,不同线程具有不同的数据副本。
以一个例子来简单粗暴的说明:

//定义一个Integer泛型的ThreadLocal
private ThreadLocal<Integer> mIntTheadLocal = new ThreadLocal<Integer>();

mIntTheadLocal.set(0);
Log.d("yink","UI Thead, mIntTheadLocal = " + mIntTheadLocal.get());//UI线程

new Thread("thead 1") {
    @Override
    public void run() {
        mIntTheadLocal.set(1);
        Log.d("yink","thead 1, mIntTheadLocal = " + mIntTheadLocal.get());//线程1
    }
}.start();

new Thread("thead 2") {
    @Override
    public void run() {
        Log.d("yink","thead 2, mIntTheadLocal = " + mIntTheadLocal.get());//线程2
    }
}.start();

mIntTheadLocal是同一个对象,我们分别在UI线程、线程1和线程2中对其进行set,get并打印Log如下:

07-06 23:24:33.151 12661-12661/com.example.android.myapplication D/yink: UI Thead, mIntTheadLocal = 0
07-06 23:24:33.153 12661-16454/com.example.android.myapplication D/yink: thead 1, mIntTheadLocal = 1
07-06 23:24:33.154 12661-16455/com.example.android.myapplication D/yink: thead 2, mIntTheadLocal = null

由于TheadLoacal的特性,它其中之一的功能就是被用来保存Looper对象

2.2 MessageQueue

MessageQueue,单链表数据结构,主要包含两个操作:插入(enqueueMessage)和读取(next)
先看enqueueMessage,它之做了单链表插入数据操作

boolean enqueueMessage(Message msg, long when) {
    ...
    if (p == null || when == 0 || when < p.when) {
        // New head, wake up the event queue if blocked.
        msg.next = p;
        mMessages = msg;
        needWake = mBlocked;
    } else {
        // Inserted within the middle of the queue.  Usually we don't have to wake
        // up the event queue unless there is a barrier at the head of the queue
        // and the message is the earliest asynchronous message in the queue.
        needWake = mBlocked && p.target == null && msg.isAsynchronous();
        Message prev;
    //单链表插入数据操作。
        for (;;) {
            prev = p;
            p = p.next;
            if (p == null || when < p.when) {
                break;
            }
            if (needWake && p.isAsynchronous()) {
                needWake = false;
            }
        }
        msg.next = p; // invariant: p == prev.next
        prev.next = msg;
    }
    ...
}

再看next,查询到msg后,移除这个msg,并返回这个msg

Message next() {
        ...
    if (msg != null && msg.target == null) {
        // Stalled by a barrier.  Find the next asynchronous message in the queue.
        //被阻塞,在队列中查询下一个消息
        do {
        prevMsg = msg;
        msg = msg.next;
        } while (msg != null && !msg.isAsynchronous());
    }
    
    if (msg != null) {
        if (now < msg.when) {
        // Next message is not ready.  Set a timeout to wake up when it is ready.
        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
        } else {
        // Got a message.
        mBlocked = false;
        //msg不为空,移除msg
        if (prevMsg != null) {
            prevMsg.next = msg.next;
        } else {
            mMessages = msg.next;
        }
        msg.next = null;
        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
        msg.markInUse();
        //返回msg
        return msg;
        }
    } else {
        // No more messages.
        nextPollTimeoutMillis = -1;
    }
        ...
}

2.3 Looper

Looper作用:不停的从MessageQueue中查询是否有消息,有就处理,没有就一直阻塞。
我们先来一个demo,为子线程创建Looper,来理解Looper的作用:

//主线程中定义
private Handler mHandler;

new Thread("Thread 1") {
    @Override
    public void run() {
        //创建Looper
        Looper.prepare();
        //在Thread 1中创建Handler
        mHandler =  new Handler(){
            @Override
            public void handleMessage(Message msg) {
                Bundle b = new Bundle();
                b = msg.getData();
                Log.d("yink","receive the number = " + b.getInt(KEY));
                switch (b.getInt(KEY)) {
                    case 3:
                        //子线程处理完事情后,退出循环
                        //Looper还提供一个quitSafely(),这个方法是设置标记位,把队列中消息处理完后再退出。
                        getLooper().quit();
                        break;
                    default:
                        break;
                }
                super.handleMessage(msg);
            }
        };
        //循环,关键函数
        Looper.loop();
    }
}.start();

new Thread("Thread 2") {
    @Override
    public void run() {
        for (int i = 0 ; i < 5 ; i++) {
            Message msg = new Message();
            Bundle b = new Bundle();
            b.putInt(KEY,i);
            msg.setData(b);
            //如果线程1的run没执行的话,mHandler还是null,所以加个判断
            if (mHandler != null) {
                Log.d("yink","send key i = " + i);
                //发送消息
                mHandler.sendMessage(msg);
            } else {
                Log.d("yink","mHandler is null, i = " + i);
            }
            SystemClock.sleep(1000);
        }
    }
}.start();

结果如下:

07-07 02:29:24.219  1151 12404 D yink    : send key i = 0
07-07 02:29:24.220  1151 12403 D yink    : receive the number = 0
07-07 02:29:25.221  1151 12404 D yink    : send key i = 1
07-07 02:29:25.223  1151 12403 D yink    : receive the number = 1
07-07 02:29:26.223  1151 12404 D yink    : send key i = 2
07-07 02:29:26.223  1151 12403 D yink    : receive the number = 2
07-07 02:29:27.225  1151 12404 D yink    : send key i = 3
07-07 02:29:27.226  1151 12403 D yink    : receive the number = 3
07-07 02:29:28.227  1151 12404 D yink    : send key i = 4
07-07 02:29:29.231  1151 12404 D yink    : send key i = 5

ActivityThread中的Looper比较特殊,它的创建是:prepareMainLooper(),获得:getMainLooper,可在任何地方获取到主线程的Looper

接下来我们来看Looper中最重要的loop()方法:

public static void loop() {
        ...
    
    for (;;) {
        //无限循环获取queue.next(),由前面描述的MesageQueue的next方法,只有获取到消息才会返回,否则一直会阻塞,所以looper也会在这里阻塞
        Message msg = queue.next(); // might block
        if (msg == null) {
        // No message indicates that the message queue is quitting.
        //只有当Looper调用退出后,queue.next()才会返回null,因为不退出,MesageQueue会阻塞在那儿一直查询
        //此无限循环中,只有当msg == null才退出循环
        return;
        }
    ...
    try {
                //target实际就是Handler,所以最终调用到创建Handler线程中去。
                msg.target.dispatchMessage(msg);
            } 
    ....

2.4 Handler

Handler主要也是两个动作:发送和接收
先看发送:

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

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

发送过程最后调用到了enqueueMessage,最后是向队列中插入了一条消息。
最后通过Looper返回给了Handler的dispatchMessage,转向接收处理。

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

第一种接收if (msg.callback != null) :
如果msg.callback != null成立,则调用handleCallback(msg)来处理

private static void handleCallback(Message message) {
        message.callback.run();
    }

这个callback其实就是Handler.post中的runable
post过程如下:

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);//调用getPostMessage
    }

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;//把post中的Runnable赋值给msg.callback
        return m;
    }

证实:

        new Thread("Thead 1") {
            @Override
            public void run() {
                Looper.prepare();
                mHandler =  new Handler(){
            @Override
                    public void handleMessage(Message msg) {
            Log.d("yink","handleMessage ");
                        super.handleMessage(msg);
            }

                    @Override
                    public void dispatchMessage(Message msg) {
                        Log.d("yink","dispatchMessage ");
                        super.dispatchMessage(msg);
                    }
                };
                Looper.loop();
            }
        }.start();

        new Thread("Thead 2") {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.d("yink","post runable");
                    }
                });
            }
        }.start();

log如下,先是调用那个dispatchMessage然后走到调用了message.callback.run();

07-07 03:42:43.862 10475 10515 D yink    : dispatchMessage 
07-07 03:42:43.862 10475 10515 D yink    : post runable

第二种接收else:
如果 if (mCallback != null) 则调用mCallback.handleMessage(msg)
这里的mCallBack为:

public interface Callback {
        public boolean handleMessage(Message msg);
    }

//Handler的一个构造函数
public Handler(Callback callback) {
        this(callback, false);
    }

可以在创建Handler的时候传递进来,这样就可以回调监听。就是一个接口回调,这里就不举例了。

如果mCallback == null就调用handleMessage,在源码中handleMessage是一个空函数,可以继承Handler实现handleMessage方法来进行监听。Looper中的demo就是实现handleMessage方法实现监听的。也可继承Handler实现,道理相同

//Handler中的handleMessage方法,一个空函数
public void handleMessage(Message msg) {
}

总结

Android的消息机制整体来说也就是Handler的消息机制,Handler、Looper和MessageQueue三者结合,实现了线程间的通信。明白这种机制,使用更加如鱼得水。

Read the fucking source code!

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

推荐阅读更多精彩内容