💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
我们以Looper使用的一个常见例子来分析Looper类。 **例子1** ~~~ //定义一个LooperThread class LooperThread extends Thread { publicHandler mHandler; public void run() { //① 调用prepare Looper.prepare(); ...... //② 进入消息循环 Looper.loop(); } } //应用程序使用LooperThread { ...... newLooperThread().start();//启动新线程,线程函数是run } ~~~ 上面的代码一共有两个关键调用,我们对其逐一进行分析。 1. 准备好了吗? 第一个调用函数是Looper的prepare函数。它会做什么工作呢?其代码如下所示: **Looper.java** ~~~ publicstatic final void prepare() { //一个Looper只能调用一次prepare if(sThreadLocal.get() != null) { thrownew RuntimeException("Only one Looper may be created per thread"); } //构造一个Looper对象,设置到调用线程的局部变量中 sThreadLocal.set(newLooper()); } //sThreadLocal定义 private static final ThreadLocal sThreadLocal =new ThreadLocal(); ~~~ ThreadLocal是Java中的线程局部变量类,全名应该是Thread Local Variable。我觉得,它的实现和操作系统提供的线程本地存储(TLS)有关系。总之,该类有两个关键函数: - set:设置调用线程的局部变量。 - get:获取调用线程的局部变量。 * * * * * **注意**,set/get的结果都和调用这个函数的线程有关。ThreadLocal类可参考JDK API文档或Android API文档。 * * * * * 根据上面的分析可知,prepare会在调用线程的局部变量中设置一个Looper对象。这个调用线程就是LooperThread的run线程。先看看Looper对象的构造,其代码如下所示: **Looper.java** ~~~ private Looper(){ //构造一个消息队列 mQueue =new MessageQueue(); mRun =true; //得到当前线程的Thread对象 mThread =Thread.currentThread(); } ~~~ prepare函数很简单,它主要干了一件事: - 在调用prepare的线程中,设置了一个Looper对象,这个Looper对象就保存在这个调用线程的TLV中。而Looper对象内部封装了一个消息队列。 也就是说,prepare函数通过ThreadLocal机制,巧妙地把Looper和调用线程关联在一起了。要了解这样做的目的是什么,需要再看第二个重要函数。 2. Looper循环 代码如下所示: **Looper.java** ~~~ public static final void loop() { Looper me = myLooper();//myLooper返回保存在调用线程TLV中的Looper对象 //取出这个Looper的消息队列 MessageQueue queue = me.mQueue; while (true) { Message msg = queue.next(); //处理消息,Message对象中有一个target,它是Handler类型 //如果target为空,则表示需要退出消息循环 if (msg != null) { if (msg.target == null) { return; } //调用该消息的Handler,交给它的dispatchMessage函数处理 msg.target.dispatchMessage(msg); msg.recycle(); } } } //myLooper函数返回调用线程的线程局部变量,也就是存储在其中的Looper对象 public static final Looper myLooper() { return (Looper)sThreadLocal.get(); } ~~~ 通过上面的分析会发现,Looper的作用是: - Looper封装了一个消息队列。 - Looper的prepare函数把这个Looper和调用prepare的线程(也就是最终的处理线程)绑定在一起了。 - 处理线程调用loop函数,处理来自该消息队列的消息。 当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了。那么,该消息就将由和Looper绑定的处理线程来处理。那么,事件源又是怎么向Looper消息队列添加消息的呢?来看下一节。 3. Looper、Message和Handler的关系 Looper、Message和Handler之间也存在暧昧关系,不过要比RefBase那三个简单得多,用两句话就可以说清楚: - Looper中有一个Message队列,里边存储的是一个个待处理的Message。 - Message中有一个Handler,这个Handler是用来处理Message的。 其中,Handler类封装了很多琐碎的工作。先来认识一下这个Handler。