我们上一篇分析过Handler
的消息机制浅析Android-Handler消息机制,这一篇,我们就来实践下,手写一套Handler
我们先看一张图
从上面这张图可以发现一共有这么几个类,ActivityThread
,Handler
,MessageQueue
,Looper
,Message
,我们简单对这些类作简要介绍:
ActivityThread
:程序的启动入口,该类就是我们说的主线程,它对Looper
进行操作的。
Handler
:字面意思是操控者就是通过Handler来发送消息,主要就是发送消息(sendMessage)
到MessageQueue
和 操作控件的更新(handleMessage)
。Handler
下面持有这MessageQueue
和Looper
的对象。
MessageQueue
:字面意思是消息队列,就是封装Message
类,对Message
进行插入和取出操作。
Message
:这个类是封装消息体并被发送到MessageQueue
中的,这个类是通过链表实现的,其好处方便MessageQueue的
插入和取出操作。还有一些字段是(int what,Object obj,int arg1,int arg2)
。what
是用户定义的消息和代码,以便接收者Handler
知道这个是关于什么的。obj
是用来传输任意对象的,arg1
和arg2
是用来传递一些简单的整数类型的。
Looper
: 这个类是消息循环,Looper
可以让一个线程具有循环工作的特性,也就说可以把线程编程Looper
线程。每个线程只能也最多有一个Looper
对象,这个Looper
对象是一个Thredlocal
,可以保证当前线程操作的Looper
对象一定是当前线程自己的。Looper
内部有一个MessageQueue
,调用loop()
方法后线程开始不断的从MessageQueue
中去消息交给后面的Handler
处理。
下面这张图是启动顺序图,我们接下来就是按照这张图来编写我们自己的Handler
第一步:我们先创建好这几个类
/**
* @author HuangFusheng
* @date 2019-10-20
* description 主线程
*/
public class ActivityThread {
}
/**
* @author HuangFusheng
* @date 2019-10-20
* description 自定义Handler
*/
public class LatteHandler {
}
/**
* @author HuangFusheng
* @date 2019-10-20
* description 消息循环
*/
public class Looper {
}
/**
* @author HuangFusheng
* @date 2019-10-20
* description 消息队列
*/
public class MessageQueue {
}
/**
* @author HuangFusheng
* @date 2019-10-20
* description 消息
*/
public class Message {
}
第二步:创建方法
我们先写一下Message
这个类,它本身有两个基本属性what
和obj
,同时它需要知道这条消息的主人,所以它还有一个属性Handler
public class Message {
public int what;
public Object obj;
public LatteHandler target;
}
这个类基本上就完成了,接下来我们写一下LatterHandler
这个类.看过上一篇文章的同学应该知道,Handler
里面持有MessageQueue
和Looper
引用,同时它主要作用就是发送消息和处理消息,所以大概应该就是下面这样:
public class LatteHandler {
private Looper mLooper;
private MessageQueue mMessageQueue;
/**
* 发送消息
*
* @param message 消息
*/
public void sendMessage(Message message) {
}
/**
* 分发消息
*
* @param message 消息
*/
public void dispatchMessage(Message message) {
handleMessage(message);
}
/**
* 处理消息
*
* @param message 消息
*/
public void handleMessage(Message message) {
}
}
这里我们发现那两个成员变量还没有初始化,所以还缺少一个构造方法初始化MessageQueue
和Looper
,经过上一篇文章的分析,这里的MessageQueue
取得是Lopper
中的MessageQueue
,同时Looper
是从Looper
中ThreadLocal
中获取的,所以我们先把Lopper
中这一部分写完
public class Looper {
public MessageQueue mQueue;
private static ThreadLocal<Looper> sLooperThreadLocal = new ThreadLocal<>();
private Looper() {
mQueue = new MessageQueue();
}
public static Looper myLoop() {
return sLooperThreadLocal.get();
}
}
这样的话我们的Handler
构造方法就可以写了
public LatteHandler() {
mLooper = Looper.myLoop();
mMessageQueue = mLooper.mQueue;
}
我们这里只写了get
方法,有get
就有set
,那set
方法在哪写呢?这里就得写Looper
中一个重要的方法了prepare
,这个方法也是ActivityThread
中main
方法要用的
/**
* 初始化
*/
public static void prepare() {
if (sLooperThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sLooperThreadLocal.set(new Looper());
System.out.println("looper初始化");
}
OK,Looper
中还有一个loop
方法,我们一会再说,我们继续回到Handler
中,因为我们在Handler
中发送方法只写了一个空实现,里面具体内容还没写,我们把它补齐.我们看源码其实知道,Handler
调用发送方法实际上是调用的MessageQueue
中的enqueueMessage
方法,同时把Message
中的taget
赋值为自己
/**
* 发送消息
*
* @param message 消息
*/
public void sendMessage(Message message) {
message.target = this;
mMessageQueue.enqueueMessage(message);
}
这样的话我们整个Handler
就写完了,完整代码如下
public class LatteHandler {
private Looper mLooper;
private MessageQueue mMessageQueue;
public LatteHandler() {
mLooper = Looper.myLoop();
mMessageQueue = mLooper.mQueue;
}
/**
* 发送消息
*
* @param message 消息
*/
public void sendMessage(Message message) {
message.target = this;
mMessageQueue.enqueueMessage(message);
}
/**
* 分发消息
*
* @param message 消息
*/
public void dispatchMessage(Message message) {
handleMessage(message);
}
/**
* 处理消息
*
* @param message 消息
*/
public void handleMessage(Message message) {
}
}
好了,接下里我们就要写MessageQueue
中相关代码了.这个类是消息队列,主要就是有入队和出队两个方法,前面提到的enqueueMessage
就是入队,还有一个next
就是出队,我们把这两个方法不全,这里要用到生产者-消费者模式,不可能说无限制的可以往队列里面添加消息,我们假定上限是50,如果此时消息队列里面已经有50条消息,这个时候又进来一条消息,那么就需要等待取走一条消息后它才能进来,否则就锁住;同样如果此时消息队列一条消息都没有,那么取消息时就会等待,等一条消息进来否则就锁住,这里需要Lock
和Condition
,想详细了解的自行查阅下
public class MessageQueue {
/**
* 消息集合
*/
private Message[] mItems;
/**
* 添加消息索引
*/
private int mPutIndex;
/**
* 取消息索引
*/
private int mTakeIndex;
/**
* 所有消息数量
*/
private int mCount;
/**
* 锁
*/
private Lock mLock;
/**
* 取消息条件
*/
private Condition mTakeCondition;
/**
* 添加消息条件
*/
private Condition mPutCondition;
public MessageQueue() {
mItems = new Message[50];
mLock = new ReentrantLock();
mPutCondition = mLock.newCondition();
mTakeCondition = mLock.newCondition();
}
/**
* 入队
*
* @param message
*/
public void enqueueMessage(Message message) {
try {
mLock.lock();
while (mCount >= mItems.length) {
System.out.println("MessageQueue:队列满了,阻塞...");
mPutCondition.await();
}
mItems[mPutIndex] = message;
mPutIndex = (++mPutIndex >= mItems.length) ? 0 : mPutIndex;
mCount++;
mTakeCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mLock.unlock();
}
}
/**
* 取消息出队
*/
public Message next() {
Message msg = null;
try {
mLock.lock();
while (mCount <= 0) {
System.out.println("MessageQueue:队列空了,阻塞...");
mTakeCondition.await();
}
msg = mItems[mTakeIndex];
mItems[mTakeIndex] = null;
mTakeIndex = (++mTakeIndex >= mItems.length) ? 0 : mTakeIndex;
mCount--;
mPutCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mLock.unlock();
}
return msg;
}
}
我们知道ActivityThread
调用完Looper.prepare()
后还有一个方法,就是Looper.loop()
,让整个消息队列循环起来,所以我们需要把Looper
中另外的那个重要方法补上,里面就是一个死循环,无限从MessageQueue
中取消息,然后执行Handler
的dispatchMessage
方法
/**
* 循环
*/
public static void loop() {
Looper me = myLoop();
Message msg;
for (; ; ) {
msg = me.mQueue.next();
if (msg == null || msg.target == null) {
System.out.println("Looper:消息为空...");
continue;
}
System.out.println("Looper:消息不为空,发送消息...");
msg.target.dispatchMessage(msg);
}
}
到此Looper
中的代码也都写完了,帖一下Looper
的完整代码
public class Looper {
public MessageQueue mQueue;
private static ThreadLocal<Looper> sLooperThreadLocal = new ThreadLocal<>();
private Looper() {
mQueue = new MessageQueue();
}
/**
* 初始化
*/
public static void prepare() {
if (sLooperThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sLooperThreadLocal.set(new Looper());
System.out.println("looper初始化");
}
public static Looper myLoop() {
return sLooperThreadLocal.get();
}
/**
* 循环
*/
public static void loop() {
Looper me = myLoop();
Message msg;
for (; ; ) {
msg = me.mQueue.next();
if (msg == null || msg.target == null) {
System.out.println("Looper:消息为空...");
continue;
}
System.out.println("Looper:消息不为空,发送消息...");
msg.target.dispatchMessage(msg);
}
}
}
Ok,接下来我们测试下
public class ActivityThread {
public static void main(String[] args) {
Looper.prepare();
final LatteHandler handler = new LatteHandler() {
@Override
public void handleMessage(Message message) {
super.handleMessage(message);
System.out.println("" + Thread.currentThread().getName() + "线程接收到:" + message.obj);
}
};
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = Thread.currentThread().getName() + "发送了消息...";
handler.sendMessage(message);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = Thread.currentThread().getName() + "发送了消息...";
handler.sendMessage(message);
}
}).start();
Looper.loop();
}
}
结果
looper初始化
Looper:消息不为空,发送消息...
main线程接收到:Thread-0发送了消息...
Looper:消息不为空,发送消息...
main线程接收到:Thread-1发送了消息...
MessageQueue:队列空了,阻塞...