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

【Android源码】Handler 机制源码分析

程序员文章站 2022-07-14 18:34:37
...

为什么要使用Handler

  1. 因为在Android中访问UI只能在主线程中进行,如果在子线程中运行,则程序会抛出异常。

    // ViewRootImpl.java
    void checkThread() {
       if (mThread != Thread.currentThread()) {
           throw new CalledFromWrongThreadException(
                   "Only the original thread that created a view hierarchy can touch its views.");
       }
    }
    复制代码
  2. 为什么不允许在子线程中访问UI?

    因为Android的UI并不是线程安全的,如果在多线程中执行UI的操作,那么UI的状态是不可控的,这个时候就会出现各种问题。 那么最好的办法就是只能在一个线程中执行UI的操作。 而Android主线程中又不能执行耗时操作,因为那样就会导致程序的ANR。 所以Android提供了Handler这样的机制,用来在子线程中执行耗时操作之后,发送消息给主线程,主线程再执行UI的更新操作。

Handler机制原理分析

这里我们以Android UI线程的Handler来分析。

Handler的创建

Android的应用入口是ActivityThread.main()函数,UI线程的Handler就是在这个函数中创建的。

public static void main(String[] args) {
    Looper.prepareMainLooper();
    thread.attach(false);
    ActivityThread thread = new ActivityThread();
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    Looper.loop();
}
复制代码

当main函数执行之后,应用就启动了,从这个时候开始,Looper就会一直从消息队列中取出消息并处理。而应用会通过Handler来不断的添加消息给消息队列。通过不断的添加消息和取出消息,整个消息机制就被运转起来了。

Looper

从上面可以看到在Handler创建前后,通过Looper的prepareloop方法,创建了Looper对象和开启消息循环。

  1. 构造函数

    private Looper(boolean quitAllowed) {
       mQueue = new MessageQueue(quitAllowed);
       mThread = Thread.currentThread();
    }
    复制代码
    1. 创建了MessageQueue对象,这个对象是消息队列,主要用来存放我们的消息,会在下面具体分析。
    2. 获取当前的线程并保存起来
  2. 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));
    }
    复制代码
    1. 判断ThreadLocal中在当前线程是否已经存在了Looper对象,如果存在,则抛出异常,所以可以得出在同一个线程中只能存在一个Looper对象。
    2. 在ThreadLocal存入当前线程的Looper对象。
    3. ThreadLocal对象是一个线程内部的数据存储类,通过它可以在指定的线程存储数据,当存储数据之后,就只能在指定的线程中获取到存储的数据,其他线程是不能获取到数据的。
  3. loop

    public static void loop() {
       final Looper me = myLooper();
       // 获取消息队列
       final MessageQueue queue = me.mQueue;
        // 死循环来轮询的从消息队列中获取消息
       for (;;) {
           Message msg = queue.next(); // might block
           if (msg == null) {
               return;
           }
           
           try {
               msg.target.dispatchMessage(msg);
           } finally {
               if (traceTag != 0) {
                   Trace.traceEnd(traceTag);
               }
           }
    
           msg.recycleUnchecked();
       }
    }
    复制代码
    1. loop函数会不断的从消息队列中取出消息,当queue.next()为空的时候就直接阻塞住
    2. msg不为空,调用msg.target.dispatchMessage(msg)处理消息,而msg.target其实就是Handler对象,取出消息之后就交给Handler来处理发送的消息了。
  4. quit

    public void quit() {
       mQueue.quit(false);
    }
    public void quitSafely() {
        mQueue.quit(true);
    }
    复制代码

    调用MessageQueue的quit方法来退出Looper。其中上面的是直接退出,下面的是安全的退出,也就是只标记一下,当消息队列中的消息全部执行完成之后安全的退出。

    当我们在子线程中创建Looper和Handler的时候,如果我们不调用quit方法的话,子线程就会一直处于等待状态,所以我们需要调用quit方法退出Looper。

MessageQueue

在Looper中大量的使用到了MessageQueue,而MessageQueue主要就是两个操作插入消息和读取消息并删除消息,我们来一起分析下:

  1. 构造函数

    MessageQueue(boolean quitAllowed) {
       mQuitAllowed = quitAllowed;
       mPtr = nativeInit();
    }
    复制代码

    在构造函数中,调用了nativeInit方法在Native层创建了NativeMessageQueue和Looper,并将Looper设置给当前的线程。这个时候Java层和Native层都有了MessageQueue和Looper。 在Native层的Looper中,创建了一个管道,本质上就是一个文件,一个管道中有读和写两个文件操作符,通过读和写来将消息写入和读取。

  2. enqueueMessage 插入消息

    boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
           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;
           }
    
           // We can assume mPtr != 0 because mQuitting is false.
           if (needWake) {
               nativeWake(mPtr);
           }
       }
       return true;
    }
    复制代码

    enqueueMessage其实就是构建好msg,并插入到单链表中。

  3. next 读取消息并删除消息

    Message next() {
        for (;;) {
            nativePollOnce(ptr, nextPollTimeoutMillis);
        }
    }
    复制代码

    next方法是一个无限循环方法,当消息队列中没有消息的时候,next方法会被阻塞在这里,当有新的消息的时候,next方法会最终返回这条消息并从单链表中移除。

Handler

  1. 构造函数

    public Handler(Callback callback, boolean async) {
    
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread that has not called Looper.prepare()");
       }
       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
    }
    复制代码
    1. 首先获取Looper对象,如果Looper对象为空,则抛出异常,所以当我们在子线程中创建Handler的时候,首先要通过Looper.prepare()来创建子线程的Looper对象。
    2. 通过Looper获取到MessageQueue
  2. 发送消息

    Handler的发送消息主要是post的方法和send的方法,而他们最终调用的都是:

    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);
    }
    复制代码

    Handler主要就是向消息队列中插入了一条消息,这个时候MessageQueue就会调用插入的方法来插入消息。

  3. 读取消息

    当Handler发送消息之后,MessageQueue就会通过next方法读取出这个消息,那么Looper的loop就不会被阻塞住,取出消息之后就调用了msg.target.dispatchMessage(msg)方法,最终交给Handler的dispatchMessage来执行。

    /**
    * Handle system messages here.
    */
    public void dispatchMessage(Message msg) {
       if (msg.callback != null) {
           handleCallback(msg);
       } else {
           if (mCallback != null) {
               if (mCallback.handleMessage(msg)) {
                   return;
               }
           }
           handleMessage(msg);
       }
    }
    复制代码
    1. 检查Message的callback是否为空,不为空则通过handleCallback来处理,而callback就是通过post传递过来的Runnable对象。直接调用run方法执行。

      private static void handleCallback(Message message) {
          message.callback.run();
      }
      复制代码
    2. 检查mCallback是否为空,不为空则调用mCallback.handleMessage方法处理消息。而mCallback就是我们在创建Handler的时候传递过去的callback。

      public interface Callback {
          public boolean handleMessage(Message msg);
      }
      public Handler(Callback callback) {
          this(callback, false);
      }
      复制代码
    3. 调用handleMessage(msg)处理消息。