handler机制

来源:互联网 发布:ap100编程教程 编辑:程序博客网 时间:2024/06/08 02:08

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();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

只是一个简单的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) {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

这类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);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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

queue.next();......msg.target.dispatchMessage(msg)
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

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

  sThreadLocal.set(new Looper());
  • 1
  • 1

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);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

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();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

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

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

原创粉丝点击