从零开始写一套简易版Handler

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Greathfs/article/details/102644017

我们上一篇分析过Handler的消息机制浅析Android-Handler消息机制,这一篇,我们就来实践下,手写一套Handler

我们先看一张图

从上面这张图可以发现一共有这么几个类,ActivityThreadHandlerMessageQueueLooperMessage,我们简单对这些类作简要介绍:

ActivityThread:程序的启动入口,该类就是我们说的主线程,它对Looper进行操作的。

Handler:字面意思是操控者就是通过Handler来发送消息,主要就是发送消息(sendMessage)MessageQueue和 操作控件的更新(handleMessage)Handler下面持有这MessageQueueLooper的对象。

MessageQueue:字面意思是消息队列,就是封装Message类,对Message进行插入和取出操作。

Message:这个类是封装消息体并被发送到MessageQueue中的,这个类是通过链表实现的,其好处方便MessageQueue的插入和取出操作。还有一些字段是(int what,Object obj,int arg1,int arg2)what是用户定义的消息和代码,以便接收者Handler知道这个是关于什么的。obj是用来传输任意对象的,arg1arg2是用来传递一些简单的整数类型的。

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这个类,它本身有两个基本属性whatobj,同时它需要知道这条消息的主人,所以它还有一个属性Handler

public class Message {

    public int what;
    public Object obj;
    public LatteHandler target;
}

这个类基本上就完成了,接下来我们写一下LatterHandler这个类.看过上一篇文章的同学应该知道,Handler里面持有MessageQueueLooper引用,同时它主要作用就是发送消息和处理消息,所以大概应该就是下面这样:

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) {

    }
}

这里我们发现那两个成员变量还没有初始化,所以还缺少一个构造方法初始化MessageQueueLooper,经过上一篇文章的分析,这里的MessageQueue取得是Lopper中的MessageQueue,同时Looper是从LooperThreadLocal中获取的,所以我们先把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,这个方法也是ActivityThreadmain方法要用的

 /**
     * 初始化
     */
    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条消息,这个时候又进来一条消息,那么就需要等待取走一条消息后它才能进来,否则就锁住;同样如果此时消息队列一条消息都没有,那么取消息时就会等待,等一条消息进来否则就锁住,这里需要LockCondition,想详细了解的自行查阅下

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中取消息,然后执行HandlerdispatchMessage方法

 /**
     * 循环
     */
    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:队列空了,阻塞...

本文源码地址

猜你喜欢

转载自blog.csdn.net/Greathfs/article/details/102644017