教你手写Java层handler机制

handler消息框架

这里写图片描述

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<Looper> 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_癫狂】共享)
这里写图片描述

首先生成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层的源码。

实例代码下载:https://github.com/honjane/handlerDemo

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: Java中的处理器(Handler)是一种机制,用于将任务(也称为消息)分派给线程进行处理。它可以用于在不同线程之间传递消息,从而实现线程间的通信和同步。 Java中的处理器通常与线程池配合使用。当应用程序需要执行一项任务时,它会将任务封装成一条消息,然后将该消息发送到指定的处理器中。处理器会将消息放入一个消息队列中,并由线程池中的线程不断从队列中取出消息进行处理。这种方式可以有效地利用线程资源,提高系统的并发能力。 要使用Java的处理器机制,你需要创建一个继承自`android.os.Handler`的类,并实现它的`handleMessage`方法。这个方法用于处理从消息队列中取出的消息。你还可以重写`dispatchMessage`方法来定制如何分派消息。当你需要执行一项任务时,你可以使用`sendMessage`方法将任务封装成消息并发送给处理器。 使用处理器的一个优点是,你可以使用它在不同线程之间传递消息,从而实现线程间的通信和同步。但是,你需要 ### 回答2: Java中的Handler是一个重要的处理机制,用于处理线程之间的通信和消息传递。Handler可以认为是发送消息和处理消息的中介者。 在Java中,一个Handler对象可以与一个特定的线程相关联。当一个线程想要向另一个线程发送消息时,它可以通过Handler对象发送消息。要使用Handler,我们需要首先创建一个Handler对象,并将其与接收消息的线程关联起来。一旦关联完成,我们可以使用Handler的post方法发送消息给接收线程。 接收线程可以通过重写HandlerhandleMessage方法来处理接收到的消息。当接收线程收到消息时,它会自动调用handleMessage方法,并将收到的消息作为参数传递给该方法。这样,处理线程就可以根据接收到的消息类型执行相应的操作。 Handler的处理机制还具有一些其他的特性。例如,我们可以为Handler设置定时任务,使其在指定的时间间隔内执行某个任务。此外,我们还可以通过Handler实现线程之间的同步,从而避免并发访问共享资源时的竞争条件。 总而言之,Java Handler是一种用于处理线程间通信和消息传递的重要机制。通过Handler,不同的线程可以安全地发送和接收消息,并实现特定的处理逻辑。同时,Handler还具备定时任务和线程同步的功能,使得线程之间的通信更加稳定和有序。 ### 回答3: Java Handler 是用来处理线程之间通信的机制,它允许你发送和处理消息或者任务。在 Java 中,线程之间的通信一般是通过共享内存或者锁来实现的。然而,这些方法可能会导致竞态条件或者死锁的问题。 相比之下,Java Handler 使用了消息队列的方式来处理线程之间通信。处理线程通过在消息队列中发送消息或者任务,然后由接收线程从消息队列中读取并处理这些消息或者任务。 使用 Java Handler 的好处之一是它可以帮助开发者解决线程之间通信时的竞态条件和死锁问题。由于消息队列是基于先进先出的原则,处理线程可以按顺序读取和处理消息,避免了竞态条件的发生。 另一个好处是 Java Handler 支持异步处理消息或者任务。处理线程可以继续执行其他任务,并在消息或者任务准备就绪时进行处理。这可以提升程序的响应性能和处理效率。 使用 Java Handler 还可以实现线程之间的解耦。发送消息的线程不需要知道接收消息的线程是谁,只需要发送消息到相应的 Handler。这样可以简化代码的编写和维护,并提高代码的复用性和可扩展性。 总的来说,Java Handler 提供了一种可靠且高效地处理线程之间通信的机制。它通过使用消息队列来解决竞态条件和死锁问题,并支持异步处理消息和任务,同时还可以实现线程之间的解耦。这使得开发者能够更好地管理和控制多线程程序,增强了程序的可靠性和性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值