Handler使用原理

作者: 涛涛123759 | 来源:发表于2020-03-06 17:05 被阅读0次

    Android中UI的更新在主线程中完成,为了避免ANR异常所以耗时的操作需要在子线程中完成。由于主线程和子线程中需要消息的传递就引入了Hander消息传递机制。

    一、首先看一下handler的使用过程

    public class MainActivity extends AppCompatActivity {
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                   ***
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    mHandler.sendEmptyMessage("0");
                }
            }).start();
        }
    }
    

    二、handler运行机制

    借用别人的一张图来讲解一下:::



     Handler机制也可叫异步消息机制,它主要由4个部分组成:Message,Handler,MessageQueue,Looper,其中增加了ThreadLocal来管理Looper.

    1.Message
      Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。使用Message的arg1和arg2便可携带int数据,使用obj便可携带Object类型数据。target用来保存当前的handler。

    2.Handler
      Handler顾名思义就是处理者的意思,它只要用于在子线程发送消息对象Message,在UI线程处理消息对象Message,在子线程调用sendMessage方法发送消息对象Message,而发送的消息经过一系列地辗转之后最终会被传递到Handler的handleMessage方法中,最终在handleMessage方法中消息对象Message被处理。

    3.MessageQueue
      MessageQueue就是消息队列的意思,它只要用于存放所有通过Handler发送过来的消息。这部分消息会一直存放于消息队列当中,等待被处理。每个线程中只会有一个MessageQueue对象,请牢记这句话。其实从字面上就可以看出,MessageQueue底层数据结构是队列,而且这个队列只存放Message对象。

    4.Looper
      Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当MesssageQueue中存在一条消息,Looper就会将这条消息取出,并将它传递到Handler的handleMessage()方法中。每个线程只有一个Looper对象。

    5.ThreadLocal
    通过引用ThreadLocalMap的map对象来管理不同线程中的Looper。通过get()/set()方法来获取和存储Looper.

    三、handler源码分析

    1、发送消息

    使用Handler发送消息主要有两种,一种是sendXXXMessage方式,还有一个postXXX方式,不过两种方式最后都会调用到sendMessageDelayed方法,所以我们就以最简单的sendMessage()方法来分析。

     public final boolean post(Runnable r){
         return  sendMessageDelayed(getPostMessage(r), 0);
      }
    
       public final boolean sendMessage(Message msg) {
            return sendMessageDelayed(msg, 0);
        }
    

    调用sendMessageDelayed()方法,默认delayMillis为0

       public final boolean sendMessageDelayed(Message msg, long delayMillis) {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    

    在调用sendMessageAtTime时,传入的时间值: 系统时钟+delayMillis

        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;
            }
            // 调用enqueueMessage,把消息加入到MessageQueue中
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            //msg.target标记为当前Handler对象
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    在MessageQueue中把接受到的Message存到MessageQueue中。然后根据延迟的时间片when把不同得消息存放到Message单链表中,按延迟的时间从小到大从链表头部依次排列。

        boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    2、读取消息

    Looper.loop()的作用就是:从当前线程的MessageQueue从不断取出Message,并调用其相关的方法。代码如下:

     public static void loop() {
            //1、获取Looper对象
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            //2、获取消息队列
            final MessageQueue queue = me.mQueue;
            Binder.clearCallingIdentity();
            ***
            for (;;) {
                //3、通过死循环读取MessageQueue中的Message
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                ***
                final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
                final long dispatchEnd;
                try {
                  //4、通过Message中的target找到当前Message中的Handler分发消息
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
               
                ***
                //5、回收资源
                msg.recycleUnchecked();
            }
        }
    

    首先还是判断了当前线程是否有Looper,然后得到当前线程的MessageQueue。接下来,就是最关键的代码了,写了一个死循环,不断调用MessageQueue的next方法取出MessageQueue中的Message,注意,当MessageQueue中没有消息时,next方法会阻塞,导致当前线程挂起。

    拿到Message以后,会调用它的target的dispatchMessage方法,这个target其实就是发送消息时用到的Handler。所以就是调用Handler的dispatchMessage方法,代码如下:

     public void dispatchMessage(Message msg) {
            // 如果msg.callback不是null,则调用handleCallback
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                // 如果 mCallback不为空,则调用mCallback.handleMessage方法
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                // 调用Handler自身的handleMessage,这就是我们常常重写的那个方法
                handleMessage(msg);
            }
        }
    
        public void handleMessage(Message msg) {
        }
    
    3、ActivityThread

    主线程中在ActivityThread的main方法中创建Looper对象,所以在主线程中创建Hander时不用创建Looper对象。在子线程中需要手动创建Looper对象。

        public static void main(String[] args) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
            CloseGuard.setEnabled(false);
            Environment.initForCurrentUser();
            EventLogger.setReporter(new EventLoggingReporter());
            final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
            TrustedCertificateStore.setDefaultUserDirectory(configDir);
            Process.setArgV0("<pre-initialized>");
            //1、创建Looper,并将Looper保存到ThreadLocal中
            Looper.prepareMainLooper();
            long startSeq = 0;
            if (args != null) {
                for (int i = args.length - 1; i >= 0; --i) {
                    if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                        startSeq = Long.parseLong(
                                args[i].substring(PROC_START_SEQ_IDENT.length()));
                    }
                }
            }
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
              //2、开启死循环读取消息
            Looper.loop();
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    
    4、Looper机制

    创建Looper

       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));
        }
    
       public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    

    从上面的代码可以看出,一个线程最多只有一个Looper对象。当没有Looper对象时,去创建一个Looper,并存放到sThreadLocal中。

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

    这里主要就是创建了消息队列MessageQueue,并让它供Looper持有,因为一个线程最大只有一个Looper对象,所以一个线程最多也只有一个消息队列。然后再把当前线程赋值给mThread。
    MessageQueue的构造方法没有什么可讲的,它就是一个消息队列,用于存放Message。
    所以Looper.prepare()的作用主要有以下三点
    1、创建Looper对象
    2、创建MessageQueue对象,并让Looper对象持有
    3、让Looper对象持有当前线程

    5、ThreadLocal

    通过引用ThreadLocalMap的map对象来管理不同线程中的Looper。通过get()/set()方法来获取和存储Looper.

        public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }
    
        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
    6、Message

    保存当前线程中发送消息的信息

        /*package*/ int flags;
        /*package*/ long when;
        /*package*/ Bundle data;
      //包当前的Handler保存在target中,用区分Message在那个Hander中的
        /*package*/ Handler target;
        /*package*/ Runnable callback;
        // sometimes we store linked lists of these things
        /*package*/ Message next;
    
        *****
       public static Message obtain(Handler h) {
            Message m = obtain();
            //记录Hander对象
            m.target = h;
    
            return m;
        }
    
    

    相关文章

      网友评论

        本文标题:Handler使用原理

        本文链接:https://www.haomeiwen.com/subject/daeirhtx.html