当前位置: 萬仟网 > IT编程>移动>Android > Handler,Looper,MessageQueue流程梳理

Handler,Looper,MessageQueue流程梳理

2019年01月12日 09:35  | 萬仟网IT编程  | 我要评论

 

目的:handle的出现主要是为了解决线程间通讯。

  举个例子,android是不允许在主线程中访问网络,因为这样会阻塞主线程,影响性能,所以访问网络都是放在子线程中执行,对于网络返回的结果则需要显示在主线程中,handler就是连接主线程和子线程的桥梁。

 

1.handler基本使用方法

  看一下使用方法:

 public static final int empty_msg = 0;
    @suppresslint("handlerleak")
    handler handler = new handler(){
        @override
        public void handlemessage(message msg) {
            switch (msg.what){
                case 0:
                    toast.maketext(mainactivitys.this, "接受到消息", toast.length_short).show();
                    break;
            }
        }
    };
    @override
    protected void oncreate(@nullable bundle savedinstancestate) {
        super.oncreate(savedinstancestate);
        setcontentview(r.layout.activity_main);

        new thread(new runnable() {
            @override
            public void run() {
                handler.sendemptymessage(0);
            }
        }).start();
    }

  通过上边代码就完成了子线程向主线程发送消息的功能。

 

2. handler,looper,messagequeue 解释

  handler:负责发送和处理消息

  looper:消息循环器,也可以理解为消息泵,主动地获取消息,并交给handler来处理

  messagequeue:消息队列,用来存储消息

 

3.源码分析

  程序的启动是在activitythread的main方法中

public static void main(){
   looper.prepare(); //1
   handler handler = new handler();//2
   looper.loop();      //3
}

  looper.prepare()会初始化当前线程的looper

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

  会调用到sthreadlocal.set()方法,threadlocal是线程安全的,不同的线程获取到的值是不一样的,下面先分析一下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);
    }

  不同的线程会设置不同的looper,下面看一下threadlocalmap是如何存储数据的

  

 threadlocalmap(threadlocal firstkey, object firstvalue) {
      table = new entry[initial_capacity];
      int i = firstkey.threadlocalhashcode & (initial_capacity - 1);
      table[i] = new entry(firstkey, firstvalue);    
}

  threadlocalmap会创建一个数组,key是通过特殊的算法来创建出来,一个线程中会有一个threadlocalmap,这个map中会存多个threadlocal和values。

  下面看下threadlocalmap是如何set一个值的

  

private void set(threadlocal key, object value) {

            // we don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            entry[] tab = table;
            int len = tab.length;
            int i = key.threadlocalhashcode & (len-1);

            for (entry e = tab[i];
                 e != null;
                 e = tab[i = nextindex(i, len)]) {
                threadlocal k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replacestaleentry(key, value, i);
                    return;
                }
            }

            tab[i] = new entry(key, value);
            int sz = ++size;
            if (!cleansomeslots(i, sz) && sz >= threshold)
                rehash();
        }

 

  其实是遍历threadlocalmap中的table,如果当前table中存在threadlocal这个key就更新,不存在就新建。threadlocal的set方法到此结束。

 

  下面看下handler handler = new handler()中执行了哪些操作:

  public handler(callback callback, boolean async) {
        mlooper = looper.mylooper();
        mqueue = mlooper.mqueue;
      
    }

  重要的就是构造函数中这两个方法,在handler中初始化looper和messagequeue。这个就不展开讲了。

  

 

  下面看一下looper.loop()这个步骤,我做了一些精简,把无关的代码去掉了。

   public static void loop() {
        final looper me = mylooper();
        final messagequeue queue = me.mqueue;

        for (;;) {
            message msg = queue.next(); // might block
            if (msg == null) {
                // no message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchmessage(msg);
            msg.recycleunchecked();
        }
    }

  queue.next()是个无限for循环,其实也是个阻塞方法,其中比较重要的是下面这个方法,其作用是不会一直循环。底层采用的是pipe/epoll机制。

nativepollonce(ptr, nextpolltimeoutmillis);
 message next() {
        // return here if the message loop has already quit and been disposed.
        // this can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mptr;
        if (ptr == 0) {
            return null;
        }

        int pendingidlehandlercount = -1; // -1 only during first iteration
        int nextpolltimeoutmillis = 0;
        for (;;) {
            if (nextpolltimeoutmillis != 0) {
                binder.flushpendingcommands();
            }

            nativepollonce(ptr, nextpolltimeoutmillis);

            synchronized (this) {
                // try to retrieve the next message.  return if found.
                final long now = systemclock.uptimemillis();
                message prevmsg = null;
                message msg = mmessages;
                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;
                        if (prevmsg != null) {
                            prevmsg.next = msg.next;
                        } else {
                            mmessages = msg.next;
                        }
                        msg.next = null;
                        if (debug) log.v(tag, "returning message: " + msg);
                        msg.markinuse();
                        return msg;
                    }
                } else {
                    // no more messages.
                    nextpolltimeoutmillis = -1;
                }

                // process the quit message now that all pending messages have been handled.
                if (mquitting) {
                    dispose();
                    return null;
                }

                // if first time idle, then get the number of idlers to run.
                // idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingidlehandlercount < 0
                        && (mmessages == null || now < mmessages.when)) {
                    pendingidlehandlercount = midlehandlers.size();
                }
                if (pendingidlehandlercount <= 0) {
                    // no idle handlers to run.  loop and wait some more.
                    mblocked = true;
                    continue;
                }

                if (mpendingidlehandlers == null) {
                    mpendingidlehandlers = new idlehandler[math.max(pendingidlehandlercount, 4)];
                }
                mpendingidlehandlers = midlehandlers.toarray(mpendingidlehandlers);
            }

            // run the idle handlers.
            // we only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingidlehandlercount; i++) {
                final idlehandler idler = mpendingidlehandlers[i];
                mpendingidlehandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueidle();
                } catch (throwable t) {
                    log.wtf(tag, "idlehandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        midlehandlers.remove(idler);
                    }
                }
            }

            // reset the idle handler count to 0 so we do not run them again.
            pendingidlehandlercount = 0;

            // while calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextpolltimeoutmillis = 0;
        }
    }

   message.next()返回消息之后会接着调用 msg.target.dispatchmessage(msg);在这个方法里边会进行判断,来决定执行哪一种回调。

  

  public void dispatchmessage(message msg) {
        if (msg.callback != null) {
            handlecallback(msg);
        } else {
            if (mcallback != null) {
                if (mcallback.handlemessage(msg)) {
                    return;
                }
            }
            handlemessage(msg);
        }
    }

到此整个handler的流程就结束了。最后附上一张handler的时序图。

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章: