Android Handler机制

        Android Handler消息机制,在Android中扮演了很重要的角色。通过Android Handler,能够实现延迟处理、子线程更新主线程界面等功能。(Android更新UI,一定需要在主线程中,这个规定的原因是,多个线程同时更新UI会造成UI界面更新混乱,不利于管理,因此,程序在初始化时,主线程中就初始化了UIHandler)。

        Handler机制主要涉及到几个类:
- Handler
- Looper
- ThreadLocal
- MessageQueue

        后面将从简单到复杂分析他们各自的功能,以及之间的关系。

ThreadLocal

        ThreadLocal是一个线程相关的存储类。ThreadLocal的存储,获取数据:

 public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
 public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

ThreadLocal中常用的方法主要是这两个:
- set(T value)方法,传入一个泛型对象,首先获取当前线程的对象,然后values(currentThread),这个方法其实获取currentThread.loacalValues,也就是说每个Thread都有localValues对象。如果这个values为空,则初始化,否则直接put添加这个数据。我们查看Values这个类的put方法:

  void put(ThreadLocal<?> key, Object value) {
            cleanUp();

            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;

            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];

                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }

                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }

                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        }

        首先cleanup,这个方法会调用rehashed,如果数组容纳的空间不够,会进行空间扩展。        然后外层是循环,循环的第一个判断,如果找到原来设置的key,则直接修改原来key对应的值(保存的时候,是index保存键,index+1保存值);        循环的第二个判断,如果没有找到对应的key,并且firstTombstone=-1,则在最后面保存key和value;        第三个判断,如果k!=null并且firstTombstone=-1和k==TOMBSTONE,那么firstTomebstone=index,然后在下次循环中,将数据赋值到index这个位置(因为这个位置的数据是被删除的位置,被删除的数据会被赋值为TOMBSTONE)。


  • get() 这个方法中,通过hash计算出对应的index,并返回对应的值。 不做过多说明。

总结一下ThreadLocal:
1. 每个Thread都有一个Values对象,这个对象用来存储数据。
1. ThreadLocal操作对应线程的数据,其实是操作在线程的Values中的数据。
1. put方法,会先检查数组容量,然后将数据保存在数据中,如果其中有废弃的数据,则会这个位置的数据会被覆盖。
1. get方法,在线程的values中,查找指定位置的值。这个位置通过ThreadLocal的hash和Values的mask值来计算。
2. ThreadLocal是线程级保存数据的。

MessageQueue

        它保存了由Looper分发的一系列事件Message,MessageQueue主要用来操作Message消息的,它核心的方法主要是:enqueueMessage,next,quite。

  • enqueueMessage
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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

分析一下上面方法的流程:
1. 首先判断这个Message的Handler是否为空以及消息是否已经在使用。
1. 然后同步方法中,先判断当前消息队列是否退出,如果退出了则抛出异常,并将这个消息回收掉。
2. 如果消息队列没有退出,则开始将消息添加到队列中:先将消息设置为正在使用中状态(makeInUse),如果当前头结点为空,则将这个消息Message作为头结点,头结点的名称依然还是mMessages,这时,重新将阻塞的线程激活
3. 如果头结点不为空,则先执行for循环,将p指向到队列的末尾结点的next结点,最后将Message添加末尾结点的next结点,而Message的下一个结点指向p这个空结点。这样就把新消息添加到了队列末尾。
示意图(将消息插入末尾):
这里写图片描述


  • next
    这个方法的作用是从MessageQueue中取出一条消息。下面是next方法中的部分核心代码:
...
 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;
                }
                ...
  1. 外层是一个死循环,只有当这个方法获取到一个Message时或者MessQueue调用了quite方法,才会跳出循环返回。
  2. 中间开始是一个同步的方法体,避免多线程同时访问数据。
  3. if先判断是否存在无效的信息(target为空),如果target为空,开始循环找出后面的第一个异步消息。消息里面有普通消息,同步消息,异步消息。同步消息的插入postSyncBarrier这个方法,同步消息没有target,当代码运行到这里时,只会取出异步消息来执行。
  4. 如果msg不为空,先判断现在的时间是否大于消息的时间,如果大于,则取出头部的第一个消息(mMessage=msg.next)。这里有可能因为preMsg!=null(第三步说的情况),因此 会执行prevMsg.next = msg.next。最终都是取出一条消息返回。
    下面用图来说明:
    这里写图片描述

  • quite
 void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

主要是两个判断
1. 是否运行退出循环,主线程中的Looper是不允许的。
2. 是否安全退出,非安全退出,则队列中所有的消息将会因为调用removeAllMessagesLocked()移除;安全退出只会删除时间大于当前的消息,已经在队列中可执行的消息不会删除。

Looper

        Looper的主要作用是在当前线程中运行一个消息循环队列。sMainLooper是一个静态变量,在主线程调用prepareMainLooper时候就会被赋值。因此这个的获取就很简单,调用getMainLooper就ok了。
后面主要讲讲Looper的核心方法,按照Looper的使用方法顺序,prepare(),loop(),quite()方法说明。

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

        Looper的初始化直接调用prepare静态方法,会先new一个Looper对象,然后保存在当前线程中。


loop

  public static void loop() {
     ...

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);
    ...
        }
    }

        上面是loop方法的核心代码,主要是通过死循环不断的调用queue.next()获取队列中的消息,并调用dispatchMessage将消息发送给Handler。    消息队列的next()方法当消息队列中没有消息后,会阻塞;当调用了quit方法后,next()返回null,然后loop的循环也会退出。总体来说,流程比较简单。

Handler

Handler是发送消息和处理消息的主要参与者。下面主要分析发送消息和处理消息的相关方法:

enqueueMessage

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

可以发现,Handler里面的sendMessage或者是post相关方法,最终都会调用enqueueMessage方法,最后其实是MessageQueue的enqueueMessage方法,这就回到上面MessageQUeue分析的过程了。


dispatchMessage

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

首先判断Message有没有设置callback,如果设置了直接调用callback处理,否则调用调用Handler的Callback或者是调用handleMessage方法处理。


        以上就是Android中Handler的源码分析。接下来我们通过常用的使用方式结合源码再梳理一遍。

Looper.preprare()
Looper.looper
Handler handler = new Hander(looper)
hanlder.sendMessage
handleMessage

上面是整个用法的简单写法,Looper保存在prepare方法调用的所在线程中,Handle可以在其他线程中创建,只要能拿到looper对象,Handler的方法也可以在其他线程中调用。如果不指定looper对象,Handler绑定的就是当前线程的Looper对象。Looper对象的loop方法决定了Handler的handleMessage方法处理操作所在的线程。

这里写图片描述
到此为止,Handler的机制基本就结束了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值