Looper是什么?

Looper是android.os包里的一个类,看名字就知道和os相关。它和handler等配合完成android的消息机制。Looper完成线程中的消息循环,即不断地读取MessageQueue中的消息。
但是呢,在Thread中默认是没有Looper的,所以想要使用Handler,就得获取一个Looper;该类提供了静态方法Looper.prepare()来获得Looper,并通过Looper.loop()无限循环获取和分发MessageQueue中的消息。

在Android中主线程在ActivityThread中已经调用了,所以在主线程使用Handler不用显示地调用Looper的方法,但是在子线程中使用Handler是需要显示调用的。

Looper怎么用?
class LooperThread extends Thread {    public Handler mHandler;    public void run() {        Looper.prepare();        // Step 1: 创建Handler        mHandler = new Handler() {            public void handleMessage(Message msg) {                //处理即将发送过来的消息            }        };        Looper.loop();    }}
为什么需要Looper?

我们看啊,在上面的代码中,在子线程中创建了Handler并重写了接收message的方法,但是如果没有一个无限循环的话,线程不是应该立马就结束了吗,而创建的Handler随之也应该被回收,那如何还能给子线程的Handler传递消息?

那我们写个while吧让线程不退出,这样试试。
结果是。。。。

2020-05-27 11:27:07.965 9366-9383/com.sensetime.myapplication E/AndroidRuntime: FATAL EXCEPTION: Thread-2    Process: com.sensetime.myapplication, PID: 9366    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()        at android.os.Handler.(Handler.java:200)        at android.os.Handler.(Handler.java:114)        at com.sensetime.myapplication.MainActivity$LooperThread$1.(MainActivity.java:728)        at com.sensetime.myapplication.MainActivity$LooperThread.run(MainActivity.java:728)

还没发消息就直接崩,跟是不是无限循环没半毛钱关系,Handler在创建的时候就会检查Looper是否创建过,不然的话就直接抛异常不干了。

    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 " + Thread.currentThread()                        + " that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

进而还可以做实验:

1、如果不执行Loop.prepare(),直接崩溃,报错和上面一样的。说明Loop.prepare是创建Looper的方法

2、如果不执行Looper.loop(),啥也收不到,说明要能收消息,必须得开启loop循环。

现在我们可以达成一致结论,不用Looper不行。下面看一下Looper的源码,看它到底有什么玄机。

分析Looper的运作机制?怎么和Handler绑定起来的?

首先要接受,一个线程对应一个looper的结论,这个在后面的代码分析中是显而易见的。

我们先看一下Looper里面有哪些重要成员变量。首先是sThreadLocal,它是一个静态对象,我们都知道静态对象是属于类的,那么不管哪个线程的Looper,都能够访问到同一个sThreadLocal,那岂不是就乱套了吗?接下来再看它的奇妙之处。

ThreadLocal.java

先说一下ThreadLocal的特点,然后再用一个例子来说明。ThreadLocal是一个容器,那么是容器就能存储数据对吧,这是第一点;第二,这个容器有什么特殊之处呢?它能实现以线程为作用域的存储,线程之间数据隔离。也就是说,如果使用threadLocal.set分别在两个线程存储不同的东西(注意操作的是同一个对象threadlocal),再用threadlocal.get取出数据来;取出来的数据与对应线程存储的数据一致。也就是在A线程调用get,会得到在A线程set的数据,在B线程调用get,会得到在B线程set的数据,是不是很神奇。看一看set和get的源码

    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }        ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }

那么从set原理就看出来了,首先通过Thread.currentThread()获取当前的线程,再获得Thread这个类里持有的threadLocals(ThreadLocalMap,不过创建还是由ThreadLocal的createMap完成的),所以能够限制访问域为线程,因为数据相当于存储到Thread类自身中的。

Thread.java

    /* ThreadLocal values pertaining to this thread. This map is maintained     * by the ThreadLocal class. */    ThreadLocal.ThreadLocalMap threadLocals = null;    /*     * InheritableThreadLocal values pertaining to this thread. This map is     * maintained by the InheritableThreadLocal class.     */    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

threadLocals对象是由ThreadLocal类定义类型和分配空间,但它并不持有;真正持有的是Thread类,所以就能解释为什么能够做到限制访问域为线程。

Looper.java

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

Looper在创建的时候就会和当前线程绑定,所以印证了刚才说的一个线程对应一个Looper,并且创建一个消息队列实例,这个实例,其实我们在上面Handler的构造就已经见到了,Handler里面也有一个消息列队,只不过是一个引用,同时还有一个looper的引用,这里我们就看到了Handler是怎么和Looper绑定起来的了。另外,Looper也需要分发消息给Handler,这个怎么做到的接下来再分析。

    //必须先执行Looper.prepare(),才能获取Looper对象,否则为null.    mLooper = Looper.myLooper();  //从当前线程的TLS中获取Looper对象    if (mLooper == null) {        throw new RuntimeException("");    }    mQueue = mLooper.mQueue; //消息队列,来自Looper对象

下面再看一下Looper中另一个比较重要的静态方法myLooper

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

这个方法有什么用呢?从本地的sThreadLocal里get出一个东东,下面看一下ThreadLocal的get方法

    public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        return setInitialValue();    }

和上面分析的set方法如出一辙,都是获取当前线程后从线程的map中取出数据,key正是threadLocal这个对象本身,而这个对象是谁创建的呢?正是Looper

    static final ThreadLocal sThreadLocal = new ThreadLocal();

下面终于要说要离用户更近的方法了,看看Looper.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));    }

首先,代码上可以看出来为什么Looper.prepare不能调用两次。然后new一个Looper对象放进ThreadLocal里,这样把Looper和当前线程给绑定在一起了。再看一下loop方法,必须要调用了looper方法后才能开启消息循环。

下图是各个类之间的关联关系,比较清晰地反应了是怎样联系起来的。

Looper并没有直接关联上Handler,但是,Message本身就包含了一个Handler的引用,成员变量target,所以Looper才能把相应的Message传给对应的Handler。
下面总结一下各个类主要作用:

  • Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息;
  • MessageQueue:消息队列的主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
  • Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
  • Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者。

消息入队与分发处理流程

刚才分析完了Handler、Looper、Message等的关系,现在我们来捋一下,当我们调用Handler的sendMessage发生了什么,最终又怎样传到了Handler的handMessage回调中。

首先从sendMessage聊起,调用栈如下所示。

Handler.java

    public final boolean sendMessage(@NonNull Message msg) {        return sendMessageDelayed(msg, 0);    }        public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }    public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,            long uptimeMillis) {        msg.target = this;        msg.workSourceUid = ThreadLocalWorkSource.getUid();        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }    

整体流程下来比较简单,但是需要注意几个地方。

  • 1、到了sendMessageAtTime的时候,就把关联的Looper中的MessageQueue联系起来了。
  • 2、到了enqueueMessage的时候把该条Message的target设置成了Handler自身,这样把该条Message和对应的Handler绑定起来了。
  • 3、最后调用的是MessageQueue的enqueueMessage方法,作用是添加一条消息进Looper的消息队列我们来看一下。
boolean enqueueMessage(Message msg, long when) {    // 每一个普通Message必须有一个target,指向对应Handler    if (msg.target == null) {        throw new IllegalArgumentException("Message must have a target.");    }    if (msg.isInUse()) {        throw new IllegalStateException(msg + " This message is already in use.");    }    synchronized (this) {        if (mQuitting) {  //正在退出时,回收msg,加入到消息池            msg.recycle();            return false;        }        msg.markInUse();        msg.when = when;        Message p = mMessages;        boolean needWake;        if (p == null || when == 0 || when < p.when) {            //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支并且插队到最前面            msg.next = p;            mMessages = msg;            needWake = mBlocked; //当阻塞时需要唤醒        } else {            //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非            //消息队头存在barrier,并且同时Message是队列中最早的异步消息。            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;            prev.next = msg;        }        //消息没有退出,我们认为此时mPtr != 0        if (needWake) {            nativeWake(mPtr);        }    }    return true;}

MessageQueue是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。

现在消息已经到了Looper的消息队列中了,我们下一步就是看loop到底干了什么事以及怎么分发到Handler的HandMessage中。

public static void loop() {    final Looper me = myLooper();  //获取TLS存储的Looper对象    final MessageQueue queue = me.mQueue;  //获取Looper对象中的消息队列    Binder.clearCallingIdentity();    //确保在权限检查时基于本地进程,而不是调用进程。    final long ident = Binder.clearCallingIdentity();    for (;;) { //进入loop的主循环方法        Message msg = queue.next(); //可能会阻塞         if (msg == null) { //没有消息,则退出循环            return;        }        //默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能        Printer logging = me.mLogging;          if (logging != null) {            logging.println(">>>>> Dispatching to " + msg.target + " " +                    msg.callback + ": " + msg.what);        }        msg.target.dispatchMessage(msg); //用于分发Message         if (logging != null) {            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);        }        //恢复调用者信息        final long newIdent = Binder.clearCallingIdentity();        msg.recycleUnchecked();  //将Message放入消息池     }}

可以看到,其实最重要的就是 msg.target.dispatchMessage(msg)这句话,它取出了Message对应的的Handler并且调用它的dispatchMessage方法,看一下。

public void dispatchMessage(Message msg) {    if (msg.callback != null) {        //当Message存在回调方法,回调msg.callback.run()方法;        handleCallback(msg);    } else {        if (mCallback != null) {            //当Handler存在Callback成员变量时,回调方法handleMessage();            if (mCallback.handleMessage(msg)) {                return;            }        }        //Handler自身的回调方法handleMessage()        handleMessage(msg);    }}

真像大白了:

  • [1] 当Message的回调方法不为空时,则回调方法msg.callback.run(),其中callBack数据类型为Runnable,否则进入步骤2;
  • [2] 当Handler的mCallback成员变量不为空时,则回调方法mCallback.handleMessage(msg),否则进入步骤3;
  • [3] 调用Handler自身的回调方法handleMessage(),该方法默认为空,Handler子类通过覆写该方法来完成具体的逻辑。

我们一般使用的是3,如果调用的是Handler的post方法传的Runnable对象则是第一种,流程和上述差不多,只是把msg的callback对象赋为了你传入的runnable而已。

注意,从这里可以看出,Looper所在的线程,才是真正执行handleMessage或者你传入的Runnable的线程,现在基本把framework层的Handler机制理清楚了。

更多相关文章

  1. Flutter(三):实现Flutter代码调用Android原生代码(创建WebView Plu
  2. Android事件处理之使用异步任务执行下载
  3. android使用handlerthread创建线程示例
  4. Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高
  5. Android(安卓)Context简介
  6. 【Android(安卓)Linux内存及性能优化】(五) 进程内存的优化 - 线
  7. Android中关于SQLite数据库的一些知识
  8. Android超精准计步器开发-Dylan计步
  9. Android(安卓)Activity生命周期

随机推荐

  1. Android(安卓)JNI简单实例(android 调用C/
  2. listview android:cacheColorHint,androi
  3. Android异步处理四:AsyncTask的实现原理 .
  4. shape
  5. Android(安卓)RelativeLayout 属性
  6. 【转】Android中gravity与layout_gravity
  7. Android--控件属性汇总
  8. Android异步处理三:Handler+Looper+Messag
  9. Android——导入已存在的android工程时出
  10. Android窗口机制(三)Window和WindowManager