handler 的消息机制

在日常开发中,我们经常要用到消息的通信机制,比如网络请求,在子线程中请求到数据后,切换到主线程(也叫ui线程-activityThread)去更新数据。
在这一过程中,有几个比较重要的类是我们要熟悉或者了解的,分别是–handler Looper Message Messagequeue ThreadLocal。
这里我们就不详细的去分析每个类的底层逻辑了,只会在要用到的相关方法时再去分析下。
那我们就直接切到正题吧,我们平时需要用到handler的时候绝大部分都是直接在主线程中创建handler并初始化的,然后在子线程中去把数据消息发送到主线程去处理,比如这样。。

  Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 0:
                        //todo
                        break;
                }
            }
        };

发送一条消息

  new Thread(new Runnable() {
            @Override
            public void run() {
                //这里请求网络数据,然后把数据通过message发送

                Message message=Message.obtain();
                message.obj="如果不是基本数据类型的可以通过此字段来发送";
                message.what=0;
                handler.sendMessage(message);

            }
        }).start();

好了,这是一条很基本的消息发送,很容易理解,接下来,我们去分析为什么在子线程通过 handler.sendMessage(message);就能把消息给发送到了主线程呢?我们跟着他的方法一步步的下去看,点进handler的sendMessage(message)去看

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

ok,调用了 this.sendMessageDelayed(msg, 0L),这里的第二个参数是延迟发送的参数,如果我们想延迟发送消息的话,可以一开始直接就调用handler.sendMessageDelayed(),这样就可以延迟发送消息了。
我们接着点进去sendMessageDelayed()这个方法里面看看,

 public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if(delayMillis < 0L) {
            delayMillis = 0L;
        }

        return this.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

好吧,判断了一下延迟的时间,然后调用了this.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);,接着点进去look look

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

嗯,主角之一MessageQueue粉墨登场了,让我们我们来会会它,可以看到这里直接就把 this.mQueue赋值给了新创建的queue了,那 this.mQueue是在哪创建的呢,这是我想写这篇博客的原因之一。其实 this.mQueue是主线程的MessageQueue,而非这个子线程自己的MessageQueue,因为主线程在调用main函数之后就已经创建好了MessageQueue,具体我们看下代码

 public static void main(String[] args) {
        Trace.traceBegin(64L, "ActivityThreadMain");
        CloseGuard.setEnabled(false);
        Environment.initForCurrentUser();
        EventLogger.setReporter(new ActivityThread.EventLoggingReporter(null));
        File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
        Process.setArgV0("<pre-initialized>");
        

> Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if(sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        Trace.traceEnd(64L);
    

>     Looper.loop();

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

这里先说下,其实MessageQueue是Looper的一个成员变量,它在Looper的构造函数中初始化,当调用Looper.prepare时

 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");
        } else {
            sThreadLocal.set(new Looper(quitAllowed));
        }
    }

这里可以看到Looper是在 sThreadLocal.set(new Looper(quitAllowed));中被创建,这个sThreadLocal是ThreadLocal,也是我们的主角之一,它主要是用来存储线程内变量的,接下来我们进去Looper的构造函数中看。

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

嗯哼,终于看到,原来this.mQueue是在这里被创建的,到这里我们先捋一下思路,调用了Looper.prepare之后才创建MessageQueue,但是我们好像从来没有调用过Looper.prepare()函数呀,那MessageQueue是哪来的呀,别急,慢慢来,喝口水消消性子,等贫僧念念经。。。

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

    public Handler(Handler.Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, (Handler.Callback)null, false);
    }

    public Handler(Looper looper, Handler.Callback callback) {
        this(looper, callback, false);
    }

    public Handler(boolean async) {
        this((Handler.Callback)null, async);
    }

    public Handler(Handler.Callback callback, boolean async) {
        this.mLooper = Looper.myLooper();
        if(this.mLooper == null) {
            throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
        } else {
            this.mQueue = this.mLooper.mQueue;
            this.mCallback = callback;
            this.mAsynchronous = async;
        }
    }

这里看到handler有几个构造函数,我们创建handler的时候并没有传入任何参数,所以调用了handler的无参构造函数,然后无参构造函数又调用有两个参数的那个构造函数 public Handler(Handler.Callback callback, boolean async),然后看到这行 this.mLooper = Looper.myLooper();
看到没,Looper是在这里被赋值的,我们进去Looper.myLooper()这个方法看

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

哦,原来Looper是在这里获得的,那为什么通过(Looper)sThreadLocal.get()就能获得呢,因为我们说过ThreadLocal是专门存储线程内信息的,而Looper也属于线程内的变量,当然也就被存储在ThreadLocal里面咯,而在每个线程内调用(Looper)sThreadLocal.get()所获得的值是不同的,因为在(Looper)sThreadLocal.get()的内部会通过Thread.currentThread去获取每个线程对应的值,这里我们就不贴代码了,有需要了解的爷请自行查看源码哈,这里请允许小弟偷下懒呗。
接着回到话题,因为我们是在主线程创建的handler,所以理所当然的最后获取到的Looper就可以一口咬定是属于主线程的啦,而更理所当然的MessageQueue也是属于主线程的啦,因为它是在Looper的构造函数中被创建的嘛!那么,通过handler.sendMessage()发送的message自然就是存进主线的MessageQueue中咯,而在

 Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 0:
                        //todo
                        break;
                }
            }
        };

中取出的message也是从主线程的MessageQueue取出的,接下来我们回到上面的步伐接着看,
刚刚我们看到

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

看到调用了this.enqueueMessage(queue, msg, uptimeMillis)这个方法,点进去看看

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

        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看到,把handler本身赋值给了message的target字段,因为这个方法是在handler类里面调用的,所以this就代表了handler本身。然后看到queue.enqueueMessage(msg, uptimeMillis);这里的queue就是从主线程所获取来的MessageQueue,就是在这个方法中插入一条消息到消息队列中的。好了,消息存进去了,那么如何取出消息的呢?
取出消息的操作是在Looper.loop中,我们去看看

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

            while(true) {
                Message msg = queue.next();
                if(msg == null) {
                    return;
                }

                Printer logging = me.mLogging;
                if(logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
                }

                long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
                long traceTag = me.mTraceTag;
                if(traceTag != 0L && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }

                long start = slowDispatchThresholdMs == 0L?0L:SystemClock.uptimeMillis();

                long end;
                try {
                    msg.target.dispatchMessage(msg);
                    end = slowDispatchThresholdMs == 0L?0L:SystemClock.uptimeMillis();
                } finally {
                    if(traceTag != 0L) {
                        Trace.traceEnd(traceTag);
                    }

                }

                long newIdent;
                if(slowDispatchThresholdMs > 0L) {
                    newIdent = end - start;
                    if(newIdent > slowDispatchThresholdMs) {
                        Slog.w("Looper", "Dispatch took " + newIdent + "ms on " + Thread.currentThread().getName() + ", h=" + msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                    }
                }

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

                newIdent = Binder.clearCallingIdentity();
                if(ident != newIdent) {
                    Log.wtf("Looper", "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();
            }
        }
    }

这段代码有点长,我们挑重点来看就行了,其实在看源码的时候,我们没有必要把每行代码都看懂,只要看明白大概流程就行了,因为有些源码不是一个人写的,你很难去揣摩别人的每一行代码的作用,那样你会走火入魔的(不恰当),不扯了,回到主题

 MessageQueue queue = me.mQueue;
        
        

这里首先获取到了我们存进去消息的那个MessageQueue,然后就进入到了阻塞循环体,我们看重点

 Message msg = queue.next();
                if(msg == null) {
                    return;
                }

在循环体中通过queue.next();取出消息(同时把该message从消息队列中移除),这里可以看到,当msg ==null时,就不会往下走了,在这里就阻塞住了,接着往下看

  try {
                    msg.target.dispatchMessage(msg);
                    end = slowDispatchThresholdMs == 0L?0L:SystemClock.uptimeMillis();
                } finally {
                    if(traceTag != 0L) {
                        Trace.traceEnd(traceTag);
                    }

                }

看这句 msg.target.dispatchMessage(msg);这里的 msg.target就是我们先前发送消息的时候传入的handler对象,点进去dispatchMessage(msg)方法看看

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

            this.handleMessage(msg);
        }

    }

看这句msg.callback != null,这里的callback其实是Runnable对象,当我们通过handler.post系列函数发送消息的时候,这里的callback就不会为null,但我们是通过send系列函数发送的,所以这里它为空,接着进入到

 else {
            if(this.mCallback != null && this.mCallback.handleMessage(msg)) {
                return;
            }

            this.handleMessage(msg);
        }

this.mCallback != null && this.mCallback.handleMessage(msg)这里的mCallback是Handler.Callback mCallback;如果我们创建handler时传入Callback,它就不会为空,还记得handler有这样一个构造函数吧

  public Handler(Handler.Callback callback) {
        this(callback, false);
    }

所以它也是空,接着肯定的要调用 this.handleMessage(msg);方法,我们进去看

  public void handleMessage(Message msg) {
    }

看到没,空空如也。。。
这个方法就是我们创建handler时重写的

  Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 0:
                        //todo
                        break;
                }
            }
        };

终于,经过了九九六十一,回到了主线程的

 public void handleMessage(Message msg) {
                switch (msg.what){
                    case 0:
                        //todo
                        break;
                }

handleMessage(Message msg)方法中,这里就可以就行ui刷新的工作啦,咧咧咧,我不听,我不听,我就要在子线程刷新
额。陛下,冷静点,到饭点了,我们吃饭去吧。。。。。。。。

最后说明一点,本文纯属个人理解,可能不可避免有错误,各位看官如果发现有错误,请给指出啊,别有误人子弟我就要跳黄河洗不清啦,哈哈哈。。。。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值