java中手写handler_教你手写Java层handler机制

handler消息框架

0818b9ca8b590ca3270a3433284dd417.png

1.Looper是消息循环类,负责从消息队列取消息,然后通过handler转发给UI线程,它包含mQueue成员变量,mQueue是一个消息队列MessageQueue。

2.MessageQueue是消息队列类,它包含了mMessages成员;mMessages是消息Message的实例。MessageQueue提供了next()方法来获取消息队列的下一则消息。

3.Message是一个消息结构体。包含next,next是一个Message实例,可以看出Message其实是一个链表。包含target成员,target是Handler实例。此外,它还包括了arg1,arg2,what,obj等参数,它们都是用于记录消息的相关内容。

4.Handler是消息句柄类。Handler提供了sendMessage()来向消息队列发送消息; 此外,Handler还提供了handleMessage()来由子类处理消息队列的消息;这样,用户通过覆盖handleMessage()就能处理相应的消息。

消息机制位于Java层的框架主要就有上面4个类所组成。在C++层,比较重要的是NativeMessageQueue和Loop这两个类。

模拟handler消息机制

1.编写Message类:

这类为了简便,很多方法不去实现,只是简单的模拟通信机制,具体细节可以取查看官方handler源码。

public class Message {

public int what;

public int arg1;

public int arg2;

public Object obj;

public Handler target;

@Override

public String toString() {

//这类只模拟String类型数据,为了方便日志输出

return obj.toString();

}

}

只是一个简单的javaBean,没有实现单链表,没什么好解释的。

MessageQueue类:

public class MessageQueue {

private static final String TAG = MessageQueue.class.getName();

Message[] mItems;

public MessageQueue() {

mItems = new Message[50];

}

/** * 消息队列取消息 出队 * *@return */

Message next() {

return null;

}

/** * 添加消息进队列 * *@param message */

public void enqueueMessage(Message message) {

}

}

这类MessageQueue内部使用数组来模拟消息队列,定义一个mItems数组存放消息,源码使用的是链表。两个重要的方法:入队enqueueMessage();出队 next()。

Looper类:

static final ThreadLocal sThreadLocal = new ThreadLocal<>();

public MessageQueue mQueue;

public Looper() {

mQueue = new MessageQueue();

}

/** * 实例化一个属于当前线程的looper对象 */

public static void prepare() {

if (sThreadLocal.get() != null) {

throw new RuntimeException("Only one Looper may be created per thread");

}

sThreadLocal.set(new Looper());

}

public static Looper myLooper() {

return sThreadLocal.get();

}

/** * 轮询消息队列 */

public static void loop() {

Looper me = myLooper();

MessageQueue queue = me.mQueue;

//轮询

Message msg;

for (; ; ) {

msg = queue.next();

//获取到发送消息的 msg.target (handler)本身,然后分发消息

if (msg == null || msg.target == null) {

continue;

}

msg.target.dispatchMessage(msg);

}

}

mQueue:消息的队列,在Looper初始化的时候创建这个队列,在loop方法中不断的从队列中取消息,然后把消交给handler

queue.next();

......

msg.target.dispatchMessage(msg)

prepare方法。给ThreadLoacl设置Looper值,这样保证每个现场访问的Looper都是私有的Looper备份

sThreadLocal.set(new Looper());

myLooper方法。从sThreadLocal中取得私有的Looper变量

loop方法。 轮询消息队列

Handler类:

public class Handler {

private Looper mLooper;

private MessageQueue mQueue;

public Handler() {

mLooper = Looper.myLooper();

mQueue = mLooper.mQueue;

}

public void sendMessage(Message message) {

message.target = this;

mQueue.enqueueMessage(message);

}

/** * 子类处理消息 * *@param message */

public void handleMessage(Message message) {

}

/** * 分发消息 * *@param message */

public void dispatchMessage(Message message) {

handleMessage(message);

}

}

handler也很简单,持有Looper,MessageQuene,通过sendMessage方法不断的给MessageQuene中添加消息,同时把handler对象绑定到Message上,方便消息处理完成后分发消息。

dispatchMessage方法分发消息,调用了handleMessage方法,这个方法大家都很熟悉,没有实现,交给子类重写,在这里处理消息。

整个handler处理流程都写完了,来看看整个流程的时序图(懒的画,网上找了张,感谢【hnust_癫狂】共享)

0818b9ca8b590ca3270a3433284dd417.png

首先生成Message,丢给Handler,通过sendMessage()方法将Message发送到消息队列MessageQueue中去;还有一点需要说明的是,轮询器Looper是一直在轮询状态的,一直对消息队列MessageQueue进行轮询,如果一旦发现有Message,将Message返回;然后通过Message中Target拿到Handler对象,进行调用dispatchMessage() 将Looper拿到的message分发出去,最后Handler拿到消息,执行handlerMessage()方法。

上面遗留了一个很核心的问题—–消息队列是怎样处理这些消息的?

接下来通过生产者/消费者模型来实现消息队列,这也是handler跨线程的核心部分

要实现生产者/消费者模型,首先的有锁,这里使用ReentrantLock

主要考虑的重写入,它可以根据设定的变量来唤醒不同类型的锁,也就是说当我们队列有数据时,我们需要唤醒read锁;当队列有空间时,我们需要唤醒写锁。

public class MessageQueue {

private static final String TAG = MessageQueue.class.getName();

Message[] mItems;

int mPutIndex;

//队列中消息数

private int mCount;

private int mTakeIndex;

//锁

Lock mLock;

//条件变量

Condition mNotEmpty;//可取

Condition mNotFull;//可添加

public MessageQueue() {

mItems = new Message[50];

mLock = new ReentrantLock();

mNotEmpty = mLock.newCondition();

mNotFull = mLock.newCondition();

}

/** * 消息队列取消息 出队 * *@return */

Message next() {

Message msg = null;

try {

mLock.lock();

//检查队列是否空了

while (mCount <= 0) {

//阻塞

mNotEmpty.await();

Log.i(TAG, "队列空了,读锁阻塞");

}

msg = mItems[mTakeIndex];//可能空

//消息被处理后,置空数组中该项

mItems[mTakeIndex] = null;

//处理越界,index大于数组容量时,取第一个item

mTakeIndex = (++mTakeIndex >= mItems.length) ? 0 : mTakeIndex;

mCount--;

//通知生产者生产

mNotFull.signalAll();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

mLock.unlock();

}

return msg;

}

/** * 添加消息进队列 * *@param message */

public void enqueueMessage(Message message) {

try {

mLock.lock();

//检查队列是否满了

while (mCount >= mItems.length) {

//阻塞

mNotFull.await();

Log.i(TAG, "队列满了,写锁阻塞");

}

mItems[mPutIndex] = message;

//处理越界,index大于数组容量时,替换第一个item

mPutIndex = (++mPutIndex >= mItems.length) ? 0 : mPutIndex;

mCount++;

//通知消费者消费

mNotEmpty.signalAll();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

mLock.unlock();

}

}

}

自己手写一次handler消息处理机制,再回过头来看看handler是不是很简单了,再也不怕面试中被问到。当然android源码中的handler处理机制移值到C层处理了,我们不管它在c层还是java层,原理都是一致的,有兴趣可以去翻翻c层的源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值