0
点赞
收藏
分享

微信扫一扫

Android 消息机制之 Looper 深入源码分析 [ 三 ]

Android 消息机制深入源码分析 [ 一 ]
Android 消息机制之 ThreadLocal 深入源码分析 [ 二 ]
Android 消息机制之 Looper 深入源码分析 [ 三 ]
Android 消息机制之 Message 与消息对象池的深入源码分析 [ 四 ]
Android 消息机制之 MessageQueue 深入源码分析 [ 五 ]
Android 消息机制之初识Handler [ 六 ]
Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ]
Android 消息机制之 MessageQueue.next() 消息取出的深入源码分析 [ 八 ]
Android 消息机制之消息的其他处理深入源码分析 [ 九 ]
Android 消息机制总结 [ 十 ]

上一章学习了消息机制中的 ThreadLocal, 本章接着来学习消息机制中的 Looper. 开篇也是先抛出几个问题.

1. 问题

    1. 可以在一个线程多次执行 Looper. prepare() 吗? 为什么 ?
    1. Looper.prepareMainLooper 是用来做什么的. 为什么我们在主线程可以直接使用 Handler, 而不需要调用 Looper. prepare() ?
    1. Looper.quit 与 Looper.quitSafely() 有什么区别.

2. 例

先来一个典型的关于 Looper 的例子. (因为本章只分析 Looper, Handler 会放到后面章节分析.)

class LooperThread extends Thread{
  public Handler mHandler;
  public void run(){
    //分析1
    Looper.prepare();
    mHandler = new Handler(){
      public void HhandleMessage(Message msg){
        Message msg = Message.obtain();
      }
    };
    //分析 2
    Looper.loop();
  }
}

3. 分析 1

进入 Looper.prepare() 方法. 代码在 Looper.java 97行.

public static void prepare() {
    prepare(true);
}
  • 官方注释:

这也正对应了我们例子中写的, 要先调用 Looper.prepare() , 然后创建一个 Handler, 最后才调用 Looper.loop() 启动循环.

继续回到 public static void prepare(), 在方法内部又调用了一个 private static void prepare(boolean quitAllowed), 注意, 这两个方法名是相同的, 不过一个是 public 无参的, 一个是 private 有参的. 而我们调用的是 public 无参的, 在这个方法内部, Android 系统又调用了 private 有参的, 并且传入的参数为 true, 实际上这个参数一直向下传递到了 MessageQueue 的构造函数中, 表示允许退出消息队列. 也就是说我们在外部调用 Looper.prepare() 后, 系统为我们创建的消息队列是允许退出的.
 

进入到 private static void Looper.prepare(true)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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 的构造函数, 看看都做了些什么.
 

进入到 Looper(boolean quitAllowed)
private Looper(boolean quitAllowed) {
  mQueue = new MessageQueue(quitAllowed);
  mThread = Thread.currentThread();
}
  • 解析

通过 Looper 的构造函数可以知道, Looper 是无法被直接创建的, (构造函数是 private 类型的). 我们必须通过 Looper 的两个静态方法, prepare()/prepareMainLooper() 来间接的创建. 那么 prepareMainLooper 是用来做什么的呢, 一起来看一下
 

进入到 prepareMainLooper()

Looper.java 114 行

public static void prepareMainLooper() {
  //设置不允许退出的 Looper
  prepare(false);
  synchronized (Looper.class) {
      //主线程有且只能调用一次 prepareMainLooper()
      if (sMainLooper != null) {
          throw new IllegalStateException("The main Looper has already been prepared.");
      }
      //赋值给 sMainlooper
      sMainLooper = myLooper();
  }
}
  • 官方注释

什么意思呢, 简单来说就是初始化主线程的 Looper, 而且只能由 Android 系统调用.

  • 解析
进入到 myLooper()

Looper.java 的 254行

public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}
  • 解析
现在完整的分析 1, 已经分析完了, 主要就是为当前线程创建一个Looper 对象放入到当前线程的 ThreadLocal中, 并同时持有 MessageQueue 对象与当前线程对象. 其中创建的消息队列, 是可以退出的. 也知道了我们为什么可以在主线程直接使用 Handler, 就是因为在ActivityThread.main 方法中调用了 Looper.prepareMainLooper() 方法, 系统已经为主线程创建好了 Looper.
Looper 已经创建好了, 那么接下来就是开始循环了. 接下来开始看分析 2 Looper.loop().

 

4. 分析 2

Looper.java 137 行, 进入 Looper.loop() 方法

public static void loop() {
    //获取当前线程 ThreadLocal 存储的 Looper
    final Looper me = myLooper();
    if (me == null) {
        //没有 Looper 直接抛出异常
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //获取当前 Looper 持有的消息队列
    final MessageQueue queue = me.mQueue;
    //确保权限检查基于本地进程, 而不是基于最初调用进程  
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    //进入 loop 主循环方法
    //一个死循环, 不听的处理消息队列中的消息,消息的获取是通过 MessageQueue 的 next 方法
    for (;;) {
        //可能会阻塞
        Message msg = queue.next();
        //注: 这里的 msg = null, 并不是说没消息了, 而是如果消息队列正在关闭的情况下, 会返回 null.
        if (msg == null) {
            return;
        }
        ...        
        //用于分发消息,调用 Message 的 target 变量(也就是 Handler)的 dispatchMessage方法处理消息
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        ...
        //将 Message 回收标记后放入消息池.
        msg.recycleUnchecked();
    }
}
  • 解析

这里面有几个重要的方法, 后面文章会解释到.

  1. MessageQueue.next 方法,读取下一条 Message, 有阻塞
  2. Message.Handler.dispatchMessage(Message msg) 方法:消息分发
  3. Message.recycleUnchecked() 方法:消息放入消息池

5. Looper.quit 与 Looper.quitSafely 问题 3 的答案.

上面只是开启了循环方法, Looper 也提供了两个退出循环的方法, 分别是 quitquitSafely
我们调用的 Looper 的两个退出循环的方法.

public void quit() {
    mQueue.quit(false);
}
public void quitSafely() {
    mQueue.quit(true);
}

MessageQueue.quit(boolean safe)

void quit(boolean safe) {
  //当 mQuitAllowed 为 false,表示不允许退出,强行调用 quit 会有异常
  //mQuitAllowed 是在 Looper 构造函数里面构造 MessageQueue() 以参数传入的.
  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);
  }
}
  • quit: 会将消息队列中的所有消息移除 (延迟消息和非延迟消息)
    • Looperquit() 方法内部的本质是调用 MessageQueuequit(boolean safe) 方法. 传入参数是 false.
  • quitSafely: 会将消息队列所有延迟消息移除, 非延迟消息则派发出去让 Handler 处理.
    • LooperquitSafely() 方法内部调用的本质也是 MessageQueuequit(boolean safe) 方法. 只不过传入参数是 true.
  • quitSafely 相比于 quit 方法安全支出在于清空消息之前会派发出去所有的非延迟消息.

到这里. 相信对 Looper 都有了自己的认识, 比如为什么要先调用 Looper.prepare(), 再调用 Looper.loop(), 以及为什么主线程可以直接使用Hander, 而不需要调用 Looper.prepare(). 还有 loop() 循环中是怎么发送消息的. 下一章接着分析消息机制中的 Message.

举报

相关推荐

0 条评论