【Android自助餐】Handler消息机制完全解析(四)Looper解析

来源:互联网 发布:南昌大学怎么样知乎 编辑:程序博客网 时间:2024/05/29 13:21

Android自助餐Handler消息机制完全解析(四)Looper解析

  • Android自助餐Handler消息机制完全解析四Looper解析
    • Looper
      • 初始化prepare
      • 提供looper获取接口myLooper
      • 处理消息队列loop

Looper

如果你搞过Arduino,那么你肯定知道这个loop()方法。没接触过也没关系,这个方法就是一个通过死循环来重复做某件事的方法。区别是Arduion的循环控制在loop()方法外,而Looper的循环控制在loop()方法内。这个Looper类本身则是对这个方法做了一些封装。

初始化prepare()

这里标题不是构造方法,因为其构造方法被private修饰,那么来看看它什么时候调用了构造方法。很容易就能找到private static void prepare(boolean quitAllowed)方法,该类仅在这里调用了构造方法,然而这个prepare()也是被private修饰的,那么来看看这个带参的prepare()又在哪里被调用了。结果可以找到两个public static修饰的方法:prepare()prepareMainLooper()
第一个方法原文说明如下:

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 loop() after calling this method, and end it by calling quit().

某以不才为诸君翻译如下:

作为looper初始化当前线程。提供一个机会来创建handler并使用looper。在使用之前,请在此方法之后调用loop(),并在结束时调用quit()

第二个方法原文说明如下:

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.

某再以不才为诸君奉上槽点:

作为looper初始化当前线程,并标记其为application主线程的looper。在application主线程中的looper被Android系统创建,因此开发者请永远不要手动调用这个方法。

不让掉还不让研究么,今天我们就看这个prepareMainLooper ()干了点啥。先把源码放上来:

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()方法并传了个false。这个私有方法声明为private static void prepare(boolean quitAllowed),因此判断该参数的含义是不允许退出。进来这个带参方法看看:

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字段,类型是ThreadLocal,先来看看该类的原文说明:

Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports null values.

某以不才:

实现一个线程本地存储,是什么呢?一个让每个线程都有拥有value的变量。所有的线程都共享同一个ThreadLocal对象,但每个线程在这拿到的value都不相同,并且一个线程在这里做的改变并不影响其他线程。支持值为null

如果理解不了可以类比一下View类,该类有个setTag()方法,用来让这个View携带附加值。此处的ThreadLocal就是让线程可以携带附加值,因此也有get()set()方法。两个方法中的第一句便是Thread.currentThread()来获取当前线程,所以能看出上面那句“共享同一个对象”且“各线程互不影响”。

回到Lopper的带参prepare(),可以看到new了一个Lopper并放到当前线程的“tag”中,而此处是从prepareMainLopper()进来的,因此当前线程便是主线程。
再看构造方法,里面只有两行代码:

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

创建了消息队列,并获取了当前线程。上文提到在Handler构造方法中有mQueue = looper.mQueue;,说明Handler中的队列与Lopper中的队列是同一个队列。
到这里prepareMainLooper()就执行完了,根据空参的prepare()方法说明推断,“Android Environment”在调用prepareMainLooper()后必然会调用loo()。查看prepareMainLooper()调用者可以看到,在SystemServer.run()ActivityThread.main()中都在调用Looper.prepareMainLooper()后不远就调用了Looper.loop()。而这两处可以推断一个是系统应用的主线程,一个是用户应用的主线程。

提供looper获取接口myLooper()

prepare()中提到了会将looper放到线程存储ThreadLocal中,此处只需要从中取出并返回即可,因此代码只有一行return sThreadLocal.get();

处理消息队列loop()

先上核心源码:(有删减)

for (;;) {    Message msg = queue.next();     if (msg == null) {        return;    }    msg.target.dispatchMessage(msg);    msg.recycleUnchecked();}

这里就一目了然了:一个死循环,不断从队列中取消息并分发,如果取到null就说明消息队列已经退出或被释放,此时loop终止。关于queue.next()可以看MessageQueue的队列管理了解。msg.recycleUnchecked()可以看Message中obtain()与recycle()的来龙去脉了解。msg.target.dispatchMessage(msg)中target便是在Handler.obtainMessage()时放到消息中的handler,dispatchMessage()便是对消息的处理了。

0 0
原创粉丝点击