💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
#### 10.2.3 Looper的工作原理 在10.2.2节中介绍了消息队列的主要实现,本节将分析[Looper](https://www.androidos.net.cn/android/6.0.1_r16/xref/frameworks/base/core/java/android/os/Looper.java)的具体实现。**Looper在Android的消息机制中扮演着消息循环的角色**,具体来说就是**它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里**。首先看一下它的构造方法,在构造方法中它会创建一个MessageQueue即消息队列,然后将当前线程的对象保存起来,如下所示。 ``` private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } ``` 我们知道,**Handler的工作需要Looper,没有Looper的线程就会报错,那么如何为一个线程创建Looper呢**? 其实很简单,**通过Looper.prepare()即可为当前线程创建一个Looper,接着通过Looper.loop()来开启消息循环**,如下所示。 ``` new Thread("Thread#2") { @Override public void run() { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); }; }.start(); ``` Looper除了prepare方法外,还提供了**prepareMainLooper**方法,这个方法**主要是给主线程(UI线程)也就是ActivityThread所在的线程创建Looper使用的,其本质也是通过prepare方法来实现的**。由于主线程的Looper比较特殊,所以**Looper提供了一个getMainLooper方法,通过它可以在任何地方获取到主线程的Looper**。 **Looper也是可以退出的**,Looper提供了**quit和quitSafely**来退出一个Looper,二者的区别是:**quit会直接退出Looper,而quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出**。 Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false。**在子线程中,如果手动为其创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待的状态,而如果退出Looper以后,这个线程就会立刻终止,因此建议不需要的时候终止Looper**。 **Looper最重要的一个方法是loop方法,只有调用了loop后,消息循环系统才会真正地起作用**,它的实现如下所示。 ``` /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { final Looper me = myLooper();//获取TLS存储的Looper对象 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } 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(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked();//将Message放入消息池 } } ``` Looper的loop方法的工作过程也比较好理解,**loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null。当Looper的quit方法被调用时,Looper就会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null**。也就是说,**Looper必须退出,否则loop方法就会无限循环下去**。 loop方法会调用MessageQueue的next方法来获取新消息,而**next是一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,这也导致loop方法一直阻塞在那里**。**如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:`msg.target.dispatchMessage(msg)`,**这里的**msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法来处理了。但是这里不同的是,Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功地将代码逻辑切换到指定的线程中去执行了**。 **在Looper的构造方法中会去保存当前的线程,Looper在哪个线程创建,Handler的dispatchMessage方法终究会在那个线程执行**。