Android开发之异步任务消息机制

2019-08-20  本文已影响0人  大荒里种菜

前言

文章是一篇学习笔记,主要记录了阅读HandlerMessageMessageQueueLooper源码(Android 8.1)的一些心得体会,但并没有涉及到更深层次源码的分析,比如MessageQueuenative层的源码。而AsyncTask只是简单介绍其API

概述

Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueueLooper的支撑,常用于更新UI

MessageQueue用于存储消息(Message),内部的存储结构采用了链表,而不是队列,它提供插入和删除消息的功能,但不会处理消息。Looper,MessageQueue的管家,它的loop方法会一直查看MessageQueue是否存在消息(Message)需要处理,如果有,就交给Handler来处理。Handler是消息(Message)的发送者和处理者。

ThreadLocal并不是线程,它的作用是可以在每个线程中存储数据,这些数据对于其他线程是不可见的。每个线程中只会有一个Looper,可以通过ThreadLocal获取到。

另外,线程默认是没有Looper的,如果需要使用Handler就必须为线程创建,比如在子线程。而主线程已经为我们创建好了,可以查阅ActivityThreadmain方法,其中包括了MessageQueue的初始化。

它们的数量级关系是:Handler(N):Looper(1):MessageQueue(1):Thread(1)

Handler的主要作用是将一个任务切换到某个指定的线程中执行,这样设计主要是为了解决Android只能再主线程中更新UI

系统为什么不允许再子线程中访问UI呢?这是因为AndroidUI控件不是线程安全的,如果再多线程中并发访问可能会导致UI控件处于不可预期的状态。

加锁的缺点有两个:首先加上锁机制会让UI访问的逻辑变得复杂;其次,锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。

创建方式

在此只列出一种创建Handler的方式,其他的创建方式可以自行百度,但是,一定要注意:如果在没有Looper的线程里创建Handler会报错的

    // 在主线程创建
    public class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            // 处理的代码
        }
    }

    MyHandler handler = new MyHandler();

    // 子线程中执行
    Message message = handler.obtainMessage();
    message.what = 1;
    message.obj = 123;
    handler.sendMessage(message);

原理图

Handler原理图(一).png handler原理图(二).png

源码分析

先从ActivityThreadmain方法看起,里面有两句需要注意的:

public static void main(String[] args) {
      Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

      // CloseGuard defaults to true and can be quite spammy.  We
      // disable it here, but selectively enable it later (via
      // StrictMode) on debug builds, but using DropBox, not logs.
      CloseGuard.setEnabled(false);

      Environment.initForCurrentUser();

      // Set the reporter for event logging in libcore
      EventLogger.setReporter(new EventLoggingReporter());

      // Make sure TrustedCertificateStore looks in the right place for CA certificates
      final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
      TrustedCertificateStore.setDefaultUserDirectory(configDir);

      Process.setArgV0("<pre-initialized>");

      // 注意
      Looper.prepareMainLooper();

      ActivityThread thread = new ActivityThread();
      thread.attach(false);

      if (sMainThreadHandler == null) {
          sMainThreadHandler = thread.getHandler();
      }

      if (false) {
          Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
      }

      // End of event ActivityThreadMain.
      Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
      // 注意
      Looper.loop();

      throw new RuntimeException("Main thread loop unexpectedly exited");
}

大致就是说,主线程的Looper已经准备好了。通常,我们创建Handler会选择继承Handler并重写handleMessage,因为父类的handleMessage什么也不做。这里,我们关注其构造方法(无参的):

public Handler() {
    this(null, false);
 }

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
        }
    }

    // 注意以下几句
    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;
}

假如我们再子线程中创建Handler,但该线程并没有Looper,它会抛出异常:

Can't create handler inside thread that has not called Looper.prepare()

可见,Handler的创建需要Looper。那这个异常如何解决呢?举例如下:

new Thread("new Thread") {
        @Override
        public void run() {
            Looper.prepare();
            Handler handler = new Handler();
            Looper.loop();
        }
    }.start();

现在,我们回想一下,为什么在主线程可以直接创建Handler?正如前面所讲述的,当我们的App启动后,会调用ActivityThreadmain的方法,而LooperprepareMainLooper方法会被调用,也就是创建了Looper,也就是满足了Handler的创建条件。其源码如下:

/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        // 一个线程只能创建一个Looper
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
 }

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
  * to call this function yourself.  See also: {@link #prepare()}
  */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

不管是prepareMainLooper()方法还是prepare()方法,最后都是通过prepare(boolean quitAllowed)来创建Looper。我们先来看sThreadLocal

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

详细的介绍可以参考这篇博客:Android的消息机制之ThreadLocal的工作原理。它的作用是保存当前线程的Looper,且线程间互不干扰。

再看Looper的构造方法,它完成了MessageQueue的创建:

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

总的来说,Android的主线程就是ActivityThread,主线程的入口方法为main,再main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环。

Message:消息载体

先从Message的创建入手,官方更推荐使用obtain来创建,而不是其构造方法。它的构造方法什么也不做,只是完成了Message的创建,而obtain可以复用Message,且有很多多种重载。从内存、效率来看,obtain更优。

/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
 */
public Message() {
}

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
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

private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;

private static final int MAX_POOL_SIZE = 50;

下面是Message的回收处理:

/**
 * Return a Message instance to the global pool.
 * <p>
 * You MUST NOT touch the Message after calling this function because it has
 * effectively been freed.  It is an error to recycle a message that is currently
 * enqueued or that is in the process of being delivered to a Handler.
 * </p>
 */
public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */
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;
            sPoolSize++;
        }
    }
}

前面也提到,Message可以自行处理,处理的逻辑交给一个Runnable,也就是callback,读者可以自行查阅obtain的其他重载,看看callback赋值的情况,这里就不帖代码了。

Handler:发送、处理、移除消息

前面已经分析过Message的相关代码,该小节将从发送Message开始分析。其实,也可以通过Handler来创建Message,其内部也是调用Message.obtain来创建Message对象。

public final Message obtainMessage() {
    return Message.obtain(this);
}

Handler发送消息的方式有多种方式,这里选了一种最常见的,它的调用流程:

发送过程.png
源码如下:
public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0); // 延时发送时间为0
}

// 延时发送
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    // 容错
    if (delayMillis < 0) {
        delayMillis = 0;
    }
     // 当前时间加上延时时间就是真正发送Message的时间
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // Looper创建时,MessageQueue也创建了
    // mQueue在Handler创建是就初始化了,代码在前面已经贴过了
    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) {
    // Message的成员变量,用来存储Handler的对象引用
    // 也就是记录了由哪个Handler来处理这个Message
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 将Message插入到MessageQueue中,完成发送
    return queue.enqueueMessage(msg, uptimeMillis);
}

另外,Handlerpost方法也可以发送Message,且该Message将交给它自身来处理,当读者看过dispatchMessage方法后就明白了。

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

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r; // 可以回忆下讲述Message的那一节
    return m;
 }

下面是dispatchMessage的源码,它在Looper.loop方法中被调用:

public void dispatchMessage(Message msg) {
    // 如果Message.callback不为null,就交给它自身处理
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 如果mCallback不为null,交给该Handler处理
        // mCallback是Handler内部的一个接口
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 最后是我们最常见的,重写handleMessage
        handleMessage(msg);
    }
}

接着来看看Callback,这个接口长啥样:

public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    public boolean handleMessage(Message msg);
}

它也是在Handler创建的时候初始化的,但调用的构造方法不一样,导致最后的初始化不一样。无参的构造方法会导致mCallbacknull

public Handler(Callback callback) {
    this(callback, false);
}

最后来看看Handler移除Message的实现,但真正的实现交给了MessageQueue

public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}

MessageQueue:存储消息的,以message的when排序优先级

MessageHandler发送后到达MessageQueue,它采用链表的数据结构来存储Message。下面是其构造方法:

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    // 如果需要理解为什么loop方法不会卡住主线程,可以从这个本地方法开始入手
    mPtr = nativeInit();
}

MessageQueue中最主要的两个方法是nextenqueueMessage

next方法也会阻塞(当消息队列为空时或当前时间还没到达Message要处理的时间点时)但不会卡住主线程,它的工作就是根据需求从消息队列中取一个Message。注意,next方法是一个无限循环的方法。

enqueueMessage方法的工作就是将一个Message插入到消息队列且位置是合适的,因为它会根据Message要处理的时间点进行排序,从它的插入操作也可以了解到MessageQueue采用了链表。

由于next方法和enqueueMessage方法的源码过长,下面只贴出enqueueMessage排序Message的源码:

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;
}

关于阻塞但不会卡住主线程的问题在下一小节会提到。

Looper:从MessageQueue中获取当前需要处理的消息,并提交给Handler处理

关于Looper在前面也讲述了一部分,现在只剩下loop方法了,很重要。在它的内部也开启了一个无限循环。

for无限循环中,有一句表明了会阻塞:

Message msg = queue.next(); // might block

导致它阻塞的原因在MessageQueue.next方法。当消息队列退出或正在退出时,loop方法会结束,也就是跳出无限循环。

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

在这个循环里还有一句需要注意的:

msg.target.dispatchMessage(msg);

意思时交给Handler处理这个Message。至此,整个流程就分析完了。

面试的时候可能会遇到:为什么loop方法会阻塞主线程但不会卡住呢?以下的文章也许可以解答你心中的疑问:

异步任务

AsyncTask

相关API

AsyncTask在具体的使用过程中也是有一些条件限制的,主要有以下几种(摘自《Android开发艺术探索》):

总结

文章以Handler的创建过程为参照,简单介绍了Handler的原理和源码。文章末尾对AsyncTask进行了简单的介绍。读者有空可以读一读《Android开发艺术探索》的第10章和第11章。

上一篇下一篇

猜你喜欢

热点阅读