handler的dispatchMessage方法是如何被调用的? 探索 m.target是如何被附加上handler的

首先来点基础描述:
主线程的handler的looper,也就是Loop.getMainLooper()返回的loop,默认在ActivityThread类的main方法初始化了 ,在main方法里面调用了Loop.perpareMainLoper();初始化了一个loop,所以一般主线程的looper实际上就是那里初始化的looper具体ActivityThread类的main```代码如下:


    public static void main(String[] args) {
        CloseGuard.setEnabled(false);
        Environment.initForCurrentUser();
        Process.setArgV0("<pre-initialized>");
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

创建了一个默认的loop又维护了一个messageQuare主线程的looper轮训方法loop()是早早就被调用了的,

  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;
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
          
                return;
            }


      
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

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

 
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
          
            }

            msg.recycleUnchecked();
        }
    }

那么handler类里面的dispatchMessage触发是因为添加到消息队列中的消息Message类的``target被设置了handler,然后在Looper类的loop方法里面处理这个消息的时候触发,也就是下面的这句代码 msg.target.dispatchMessage(msg);```

         try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

msg.target.的成员变量为handler,也就是代码是从这里调用的。

Looper相关方法
构建loop静态对象 保证一个线程只有一个

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


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

至于Looper.loop()方法上面已经贴过了,也就是开启轮训,当没有的时候cpu进入等待。这个和某个酷Q机器人易语言源码当中的消息处理惊人的相似。他那个差不多就是消息队列的死循环,没有的时候就进行睡眠,然后有消息的时候线程唤醒。
里面用到了ThreadLocal通过线程名作为键,保证了一个线程只有一个ThreadLocal一个Looper

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal的关键方法

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

源码太深,这里只讲如何被设置上去的,
根据handler的源码分析,post postDelay实际上都最终调用了sendMessageAtTime然后调用了
return enqueueMessage(queue, msg, uptimeMillis);

那么继续,先看看handler的一些方法,

    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

    public final Message obtainMessage(int what)
    {
        return Message.obtain(this, what);
    }
    

    public final Message obtainMessage(int what, Object obj)
    {
        return Message.obtain(this, what, obj);
    }

这里都把handler传递进去了
Message的obtain方法设置了 m.target

  public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;

        return m;
    }

既然是这样的,那么自己创建一个纯message类会触发吗?

 Message message=new Message();
                message.what=1;
                message.obj="测试纯message";
                handler.sendMessage(message);

结果令人震惊,还是触发了,这message.taget他是如何被设置的呢?

然后又看了看,原来那报错的红色代码没有被我注意到,也就是说追踪走的sendMessageAtTime方法走的enqueueMessage方法中强制给message的targer设置了handler为自己。

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

这句话明显的写这句话msg.target = this; 所以 明白为什么可以回调了,ok讲解完毕,知其然也知其所以然了。

其它相关

1、handler类的queue成员变量是从Looper中获取到的,handler发消息实际上是拿到Looper里面的静态消息队列字段
mLooper.mQueue

2、而mQueue = new MessageQueue(quitAllowed);也就是消息队列对象的初始化是通过prepare的调用导致sThreadLocal.set(new Looper(quitAllowed));这句代码被执行,然后导致Looper构造方法执行,然后触发这个队列的初始化。···mQueue = new MessageQueue(quitAllowed);···

sThreadLocal也就是ThreadLocal默认在成员变量就初始化了sThreadLocal而且是静态的 这个字段也是放到Looper类里面存起来的
3、真正负责处理消息的是在looper()开启一个死循环for,不断的0调用MessageQueuenext() 知道返回null才终止,,在这个方法几乎是没法出来了,永远卡在这个方法,除非满足特定条件,`比如messagequarequit方法被调用。
4、looperlooper死死循环外,消息队列MessageQueue next也嵌套了一个死循环,所以即使消息没有也不会进行立马返回,而是等待,只有当有消息的时候才返回给looper()处理这里面主要做一些时间的处理,比如那些延时时间发送的。

有些东西还是没法理解,深度对我来说还是遥不可及

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

推荐阅读更多精彩内容