【Android 消息处理】Handler、Looper、Message 源码浅析

Android 消息处理机制核心的四个类是 Handler、Looper、Message 和 MessageQueue

Message

属性

Message 类的几个成员变量

//前面四个字段是 public 的,可以直接修改属性值,Bundle 对象需要使用 set 和 get 方法。
 public int what;    
 public int arg1;  
 public int arg2;
 public Object obj;
 Bundle data;

 long when;
 Handler target;
 Runnable callback;
 Message next;

 private static final Object sPoolSync = new Object();
 private static Message sPool;
 private static int sPoolSize = 0;

创建对象

创建一个 Message 对象可以通过无参的构造方法,但是推荐使用 Message 类提供的 static 的 obtain 方法。

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

因为 Message 内部维护了一个 Message 的对象池,上面提到的 sPoolSync 字段主要是同步用的,保证多线程安全,如果对象池不为空,那么栈顶的 Message 就被取出重用,指针指向下一个 Message 对象,队列长度减1。
如果对象 池为空,就 new 一个新的 Message 对象返回。
Message 有个 recycle 方法,每次 Looper 轮询完都会调用 Message 的该方法进行回收,该方法内部调用了 recycleUnchecked 方法,把 Message 的所有属性都还原,并入栈。
Message 还重载了很多带参数的 obtain 的方法,这些方法都先调用了无参的 obtain 方法获取一个 Message 对象,然后对属性进行赋值:

public static Message obtain(Message orig)
public static Message obtain(Handler h) 
public static Message obtain(Handler h, Runnable callback)//callback 字段没有提供 set 方法,只能在创建对象时传入,callback 并不是一个回调接口,而是一个 Runnable 对象
public static Message obtain(Handler h, int what)
public static Message obtain(Handler h, int what, Object obj)
public static Message obtain(Handler h, int what, int arg1, int arg2)
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)

我们平时发送 Message 都是通过 Handler 对象的相关方法,也可以调用 Message 自身的 sendToTarget 方法,当然前提是在创建 Message 时传入了 Handler 参数或者通过 setTarget 方法为 target 属性赋值,该内部也是调用了 Handler 的 sendMessage 方法:

public void sendToTarget() {
        target.sendMessage(this);
    }

Handler

Handler 也重载了构造方法,这些构造方法可以分为两类,一类是传入了 Looper 对象的,一类是没有传入 Looper 对象的。

public Handler()
public Handler(Callback callback)
public Handler(boolean async)
public Handler(Callback callback, boolean async) 

public Handler(Looper looper)
public Handler(Looper looper, Callback callback)
public Handler(Looper looper, Callback callback, boolean async)

传入 Looper 对象的最后都调用了三个参数的构造方法,在构造方法中对 mLooper、mQueue、mCallback、mAsynchronous 成员变量进行了初始化,mCallback 可以为 null,mAsynchronous 默认为 false:

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

没有传入 Looper 对象的最后都调用了两个参数的构造方法,

 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;
    }

在该构造函数中通过 Looper.myLooper() 方法获取到一个 Looper 对象对 mLooper 属性进行赋值,也就是说在 Handler 的构造函数中必须绑定一个 Looper 对象,否则会抛异常。Looper.myLooper() 方法获得的是当前线程的 Looper 对象,也就是创建 Handler 的代码所在的线程,所以在使用没有传入 Looper 对象的构造函数构造 Handler 时,当前线程必须有一个 Looper 对象,在 UI 线程中已经创建了 Looper 对象,而在子线程中默认情况下是没有 Looper 对象的。如果通过构造函数传入 Looper 的话则可以是任意线程的 Looper。
Handler 内部也提供了创建了 Message 的构造方法,内部其实也是调用了 Message 的 obtain 方法,Message 对象的 target 属性就是该 Handle 对象:

public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)

Handler 发送消息主要有 sendXXX 和 postXXX 两个系列,先来看 send 开头的方法:

public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessage(Message msg)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)

sendEmptyMessageXXX 就是内部创建一个只有 what 属性的 Message 对象,这些方法最后都是调用了 sendMessageAtTime 方法:

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);
    }

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

方法内部就是将 Message 对象加入在构造函数中绑定的 MessageQueue,同时将自身赋值给 Message 的 target 字段,最终由这个 Handler 来处理该 Message。
post 开头的方法最终也是调用了 sendMessageAtTime 方法,不同之处是传入了一个 Runnable 对象

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

postXXX 方法内部调用了 getPostMessage 方法创建了一个 Message 对象,传入的 Runnable 类型的参数就是赋值给这个 Message 对象的 callback 字段。所以使用 postXXX 方法时,在 Handler 所在的线程要执行代码直接写在 Runnable 的 run 方法里面,不需要在重写 Handler 的 handleMessage 方法。

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

Handler 在接收到 Messgae 后会调用 dispatchMessage 方法

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

可以看到,如果 Message 的 callback 属性不为 null,则执行 Message 的 callback 属性的 run 方法,也就是我们在创建 Message 对象时传入的 Runnable 参数或者 postXXX 方法中传入的 Runnable 参数;
如果 callback 为 null,则调用 Handler 的 mCallback 的 handleMessage 方法,这个 mCallback 也是创建 Handler 对象时传入的参数,只是这个 mCallback 不是 Runnable 对象,是一个Handler 内部声明的回调接口 Callback。
如果 mCallback 也为 null 的话,则调用 handleMessasge 方法,这个方法是在定义 Handler 时重写的方法。

Looper

前面说过,通过 Looper.myLooper 方法可以获得当前线程的 Looper 对象

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

这里的 ThreadLocal 成员变量使每一个线程保持一个各自独立的对象,为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
在非 UI 线程中并没有 Looper 对象,需要调用 prepare 方法,创建一个 Looper 对象,这样 myLooper 方法就是 null le。

public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

可以看到 prepare 方法就是 new 了一个 Looper 对象然后存储到 ThreadLocal 中,每个线程只能创建一个 Looper 对象。

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

在 Looper 的构造方法里面就是创建了一个 MessageQueue 成员变量,然后将当前线程(Looper.prepare 方法执行的线程)赋值给 mThread 成员变量,把 Looper 和当前 Thread 绑定。
有了 Looper 对象就可以不断地轮询消息队列处理消息了

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

在 loop 方法里面分别先获取当前线程(loop 方法执行的线程)的 Looper 对象,然后得到该 Looper 对象的 MessageQueue,从 MessageQueue 中取出队首的 Message,调用该 Message 目标 Handler 的 diapatchMesssage 方法进行消息处理,在消息处理完毕后对 Message 对象进行了回收。
Looper 也提供了获取主线程 Looper 的方法:

 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

MessageQueue

MessageQueue 主要的两个方法 enqueueMessage 和 next 分别在 Handler 和 Looper 中调用,这里不作讨论。

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

推荐阅读更多精彩内容