欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Handler,Looper,MessageQueue流程梳理

程序员文章站 2022-04-13 15:48:53
目的:handle的出现主要是为了解决线程间通讯。 举个例子,android是不允许在主线程中访问网络,因为这样会阻塞主线程,影响性能,所以访问网络都是放在子线程中执行,对于网络返回的结果则需要显示在主线程中,handler就是连接主线程和子线程的桥梁。 1.handler基本使用方法 看一下使用方 ......

 

目的: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的时序图。

Handler,Looper,MessageQueue流程梳理