handler机制

Handler的介绍

Handler多用于主线程与子线程之间的通信,在Android中子线程是不允许修改UI的,那么Android是如何进行子线程的加载数据后,在主线程更新界面呢?这里就使用到了handler,可以通过handler发送message(消息)的方式进行界面更新。handler整个机制的实现,还依赖于Looper(轮询器)及MessageQueue(消息队列),Looper的作用主要是不断的往消息队列中取出消息,MessageQueue用于存放handler发送的消息。

handler机制中四个重要的类型

Message(消息)
Handler
Loope(轮询器)
MessageQueue(消息队列)

消息的创建

 1.Message msg = new Message(); //可以通过handler.sendMessage 方式发送消息
 2.Message msg = Message.obtain(); 、、可以通过handler.sendMessage 方式发送消息
 3.Message msg = handler.obtainMessage(); 

发第三种方式或取消消息的方式比较特殊,通过hanler进行获取消息,说明该消息已经和handler进行绑定,如果是这种方式发送消息,可以通过msg.sendToTarget()方法进行消息的发送,因为它里面已经有handler了。

/*package*/ Message next;
  private static Message sPool;

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
Message中的Obtain方法,我们可以通过Message的静态方法Obtain获取消息,这里的sPool从字面上理解,是一个消息池,它实现的是一个单链表的形式,发送的很多消息,都会有一个先后顺序,因此它还有一个next属性。当消息为消息池为null时,它是创建了一个消息进行返回,不为null时,从消息池中获取了当前的消息进行返回。

Message m = sPool;

sPool用m这个临时变量记录 当前消息。

sPool = m.next;

sPool指向了当前消息的下一个消息,(数据链表。。)

 m.next = null;
截断数据链表,这是的m 就只是第一个消息了。然后进行了返回了这个 m 。因此懂链表数据结构,就很容易看的懂这几行代码,(作者也是刚学习 记录一下)


Message这个类中还有很多重载的obtain方法

  public static Message obtain(Handler h, int what) {  //贴出其中的一个
        Message m = obtain();
        m.target = h;
        m.what = what;
        return m;
    }

实现都是调用obtain上面的obtain方法获取一个消息,然后对传递进行的handler和消息进行了一个绑定,避免了可能同时很多个handler都在发送消息,轮询器取消息,却不知道是哪个handler发送的,当然在Looper的中也进行了handler的绑定,这些handler,都是同一个handler,形成了一个循环,始终作用于一个handler。

  void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
   		 sPool = this;++; } } }
 
 消息的回收方法,其实就是把消息的所以属性置为初始属性, 

next = sPool;
当一个消息使用完了,sPool始终都是消息中的第一个消息,它的后面可能还有很多的消息,这里就是让当前消息的下一个消息给了sPool。

 sPool = this;

sPool又等于了当前的消息,那么也可以说明,sPool前面的那个消息被抹杀掉了。

 public void sendToTarget() {
        target.sendMessage(this);
    }

<span style="font-size:14px;">这个方法就可以解释msg.sendToTarget();方法为什么可以发送消息了,</span><pre name="code" class="html"><span style="font-size:14px;">它的本质其实还是通过handler发送消息。</span>
 

Message的介绍大致就那么多,更多的方法可以去了解下Message的源码。

Handler的初始化流程

 public Handler(Callback callback, boolean async) {  //部分重要源码
       //获取了Looper轮询器
        mLooper = Looper.myLooper();
//轮询器中获取消息队列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

在handler的初始化过程中,获取了一个Looper轮询器,在从轮询器中获取消息队列,handler发送的消息都是发送到了消息队列中。

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

通过THreadLocal.get()获取
 // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
它的作用就是保证每一个线程只初始化一个Looper轮询器。

它是在myLooper方法中进行get()获取Looper,那么又是在哪里进行了set调用呢?

其实在程序运行的时候,就已经初始化好了Looer轮询器,我们使用者只用创建handler,就可以进行消息的发送了,平时我们所接触的就是handler,和message,Looper和Messag都是系统帮我们初始化好的。
是在ActivityThread类中,它是程序的主入口,在main方法调用的
Looper.prepareMainLooper();  //ActivityThread main方法里 被调用
 Looper.loop();


  public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
重点是 prepare()方法
  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()方法,创建了一Looper对象,Looper的构造方法中初始化了消息队列MessageQueue
 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
这个过程是系统帮我们初始化好的。
因此当创建好handler后,在获取到Looper轮询器,从Looper轮询器中获取MessageQueue消息队列,消息队列是从Looper中获取给handler,可以说这个消息队列是handler和Looper共享的一个容器,handler是子线程发送,Looper是主线程执行的,而子线程和主线程就是靠这个消息独立来进行数据共享的。

Looper初始化完毕后,main方法里面还调用了一个loop()方法
Looper.loop(); // 开始轮询取消息的方法

 public static void loop() {  //loop()方法的重要实现
        final Looper me = myLooper();
  
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // might block
         

            msg.target.dispatchMessage(msg);

      
            msg.recycleUnchecked();
        }
    }

在loop方法中通过myLoop();获取当前的Looper对象,在从Looper中获取消息队列,这里的消息队列和handler中获取的消息队列是同一个消息队列,然后开启了一个for的死循环,重要的方法来了,那就是消息对了中的queue.next()方法,这里的注释了说了 会阻塞, 因此,当没有消息处理时,会阻塞,它是通过这个next方法来完成阻塞的, next()所做的工作就是从中获取一个消息,如果要是没有消息,会进行阻塞.
  nativePollOnce(ptr, nextPollTimeoutMillis); //中断的方法 
<pre name="code" class="java"> private native void nativePollOnce(long ptr, int timeoutMillis); //进入阻塞的方法 
<pre name="code" class="java"> private native static void nativeWake(long ptr);  //被唤醒的方法 
 
 
 
 
都是native方法,说明这个获取消息,如果没有消息进入阻塞,有了消息则会被唤醒的实现是通过jni调用底层的实现的,是一个linux的进程间通信,管道通信.这个管道通信,这个管道其实就是一个特殊的文件,该文件有两个描述符,一个是读的描述符,一个是写的描述符,说白了就是文件的引用.
主线程拿着读的描述符等待读取数据,子线程拿着写的描述符开始写数据,写完之后通知拿着读的描述符的主线程,然后主线程被唤醒了,开始轮询取消息。处理消息,但是处理完消息后,接下去没有消息了,那么它又会进入阻塞。针对handler机制的原理,就是handler发送message,唤醒Looper,然后开始轮询遍历messageQueue,获取消息,处理消息。

Handler的发送消息流程

  public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
<pre name="code" class="java">    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
 
  
 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
   public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

几个发送消息的方法,最后都是调用了sendMessageDelayed,没有延迟的消息,默认的时间是0,但是delayMillis如果是小于0的时间 这里同时做的判断,避免时间是负数的情况的发生。
  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;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

发消息的过程又点漫长,调用了一个一个方法,重点是最后把消息对象放入到了消息队列中去,进行了顺序排序,它的排序方式是根据时间的先后顺序进行排序的,这里也把延迟的时间传递了过去,调用sendMessage时,默认就是0,否则是延迟的时间,时间先的消息先执行,延迟的等待时间延迟结束执行。
boolean enqueueMessage(Message msg, long when) {
 
            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;
    }

  msg.when = when;
绑定消息的时间
 Message p = mMessages;
mMessage是一个成语变量,这个的作用就类似Message类中 Message next的一个单链表的数据结构,消息队列也是一个单链表,因此在消息队列中有这么一个属性,通过p临时变量进行记录.
 boolean needWake;
声明了一个boolean,这个的作用是,判断是否唤醒Looper进行消息处理,可以看到下面调用了
 if (needWake) {  //这里就是唤醒的方法了。
      nativeWake(mPtr);
    }
如果p == null,说明了并没有消息,而这个消息了成了一个消息头,或者 when == 0 ,消息并没有延迟,说明就算之前有消息,那么也处理完毕了,这个新来的消息成为了消息头,在或者 when < p.when, 当前消息的延迟时间,小于了p消息的延迟时间,说明比当前这个延迟的时间早,那么时间先结束的消息会先进行处理,因此这个消息成了第一个消息,也就成为了消息头。else 里,说明可能已经有很多消息了,又新来了一个消息,那么就需要将这个消息放入到消息队列中去,这里又声明了一个变量 Message prev,
<pre name="code" class="java"> prev = p;
 
 
进入一个for的无限循环,prev 指向了p,(p 是当前的消息,第一个消息)
 p = p.next;
p 指向了它的下一个消息了,(它的下一个位置的消息)
 if (p == null || when < p.when) {
                        break;
                    }
如果p == null 也就是 当前消息的下一个消息如果是 null,就退出for循环,或者是 当前消息的延迟时间,小于了 这个下一个消息的延迟时间,退出for循环,
 msg.next = p; // invariant: p == prev.next
<pre name="code" class="java"> prev.next = msg;
 当满足其中一个条件中,说明就找到了新来消息的一个位置,然后进行消息连接,插入到链表中去。进行了一个排序。msg.next指向p,一条链子连了过去,p又连上了之前记录的他的前一个消息,形成了一个链表,(可以自行画一下图,就很好的理解了)下面调用了唤醒方法。 
 
取出消息,之前的next阻塞了消息,现在有了消息,next方法中,
  synchronized (this) {
            
                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;
                    }
mMessage跟Message类中的写法一样,定义个临时变量指向mMessage,最后return的还是个msg,从消息队列里把这个Message消息头返回回去,同过next方法获取到消息后,
for (;;) {
            Message msg = queue.next(); // might block
       
            msg.target.dispatchMessage(msg);

            msg.recycleUnchecked();
        }
然后就是通过handler的dispatchMessage()方法进行处理 消息,最后调用了消息的回收方法,回收当前消息,

  public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
handlerMessage,就是处理消息的方法了,熟悉的方法,熟悉的味道,这就是整个handler的消息机制了,

第一次写博文,肯定有很多的不足,这次的博文也是作者学习handler进行的学习记录,加深对handler机制的理解。



































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值