前篇总结:上一篇实现了主线程和子线程之间的通信,但是在么个线程都手动加个消息队列和消息循环的方式太强硬不够灵活
这一篇我们针对前篇的问题,使用Looper类来管理消息队列和消息循环,来解决。
第三节 使用Looper类来管理消息队列和消息循环
把上一篇中每个线程里的消息队列和消息循环方法都放在Looper类中简单封装一下,这样每个线程如果要线程间通信只要实例化一个Looper对象调用loop()就可以接收消息了。先上代码:
public class TestThreadMsg4 {
public static Looper mainLooper;
public static void main(String[] args){
Looper.prepare();
//myLooper方法在哪个线程里调用就会返回该线程对应的Looper对象
mainLooper = Looper.myLooper();
Thread1 t1 = new Thread1();
t1.start();
Looper.loop();//阻塞式,死循环遍历消息列表
}
/**
* 消息实体类,主要职责:
* 1.承载消息内容
*/
static class Message {
public int what;
public Object obj;
}
/**
* 模拟Looper,主要职责:
* 1.创建管理消息队列
* 2.不断死循环取出消息队列里的消息
* 3.处理消息(后续交给Handler处理)
* 说明:Looper用静态prepare()方法实例化自己,每个线程只能实例化一个Looper对象
* Looper的myLooper()方法,在哪个线程调用就返回哪个线程的Looper对象(通过ThreadLocal实现)
*/
static class Looper{
//ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
LinkedTransferQueue mQueue;//阻塞式消息队列,每一个线程对应一个消息队列
private Looper(){
this.mQueue = new LinkedTransferQueue();//初始化阻塞式消息队列
}
/**
* 初始化Looper,并放到线程变量里,
* 这里做了限制,每个线程只能有一个Looper对象,因为是死循环多个也无意义
*/
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
/**
* 从线程变量里取出,当前线程对应的Looper
* @return
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
/**
* 取出当前线程里的Looperd对象开始死循环遍历消息队列
*/
public static void loop(){
final Looper me = myLooper();
//模拟LOOP的循环方法
for (;;) {
Message msg = null;
try {
msg = (Message) me.mQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程:"+Thread.currentThread().getId()+" 收到:"+msg.obj);
if (msg.what == 1){
System.out.println("主线程:"+Thread.currentThread().getId()+" do work!");
System.out.println("");
System.out.println("");
}
}
}
public LinkedTransferQueue getQueue() {
return mQueue;
}
}
static class Thread1 extends Thread{
@Override
public void run() {
super.run();
for (;;) {
try {
Message msg = new Message();
msg.what = 1;
msg.obj = "主线程去更新UI吧";
System.out.println("子线程:"+Thread.currentThread().getId()+" 发送:"+msg.obj);
mainLooper.getQueue().put(msg);//向主线程的Looper的消息队列里添加消息
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
这里多了个Message实体类,就是替换了上一篇中的消息String没啥好说的。Looper类中的loop()方法也很简单就是上一篇中的消息死循环遍历消息列表的方法。
要说下Looper的实例化,Looper的实例化方法是私有的不能直接new 需要调用prepare()方法。为啥要这么做呢?我们看到prepare()方法中new 出Looper对象后放到ThreadLocal中,ThreadLocal有个特点就是哪个线程放入的变量就可以在哪个线程中取出,多个线程的变量互不干扰。对ThreadLocal不了解的可以去看下JDK!
所以在Android中我们不用显示主动的为每个线程定义Looper对象的变量引用,直接prepare()实例化就可以了用的时候在调用myLooper()方法就可以返回当前调用线程的Looper对象这就方便多了。
到这里我们已经实现了比较完善的线程间通信了,但是还是不够完美。缺点:1.要拿到其他线程的Looper对象才能给对应线程发消息。2.消息还是在Looper中处理的,这还是太硬了 我写个线程就要写一个Looper子类覆盖looper()方法我才可能处理到消息。针对这些问题就要另一个主角登场了,Handler!下一篇我们模拟Handler实现:
模拟实现Handler跨线程通信--之仿写模拟Handler(四)