首页
登录 | 注册

[架构]消息机制

排队打饭的情景相信曾经是学生的你一定经历过,像极了消息机制的原理,也许发明消息机制的灵感就是原来于这样的生活吧!排队的学生就是消息队列,值日生分发饭菜就是消息循环并完成消息处理,学生吃饭就类似于事件处理。

什么是消息?

何为消息?消息就是带有某种信息的信号,如你用鼠标点击一个窗口会产生鼠标的消息,键盘输入字符会产生键盘的消息,一个窗口大小的改变也会产生消息。 

消息从何而来?根据冯·诺依曼的体系结构计算机有运算器、存储器、控制器和输入设备和输出设备五大部件组成,消息主要来自输入设备,如键盘、鼠标、扫描仪等,也可来自已窗口和操作系统。

消息/事件机制是几乎所有开发语言都有的机制,在某些语言称之为事件(Event),有些地方称之为消息(Message). 一个/条消息可以理解为是一个数据结构,包含以下几个基本部分:

  • 消息源 :就是消息的来源,发出这个消息的对象
  • 消息名 :就是消息的唯一标示
  • 消息数据:消息发出后附带的数据,有可能数据是空
消息从种类上又可以分为2种:
  • 系统消息 :由操作系统或deviceone系统发送出来的消息,消息的名称是固定的。
  • 自定义消息:由开发者自己定义,自己发送出来的消息,消息的名字是随意的,可以任意定义。
举例说明:比如用户点击一个do_Button按钮,就会触发一个系统消息,包含3个部分:
  • 消息源:用户点中的button对象
  • 消息名:touch
  • 消息数据:这个消息没有附带数据 

消息机制的三大要点:消息队列、消息循环(分发)、消息处理。其结构如下:
[架构]消息机制

消息队列就是存放消息的一种队列,具有先进先出的特点。每产生一个消息都会添加进消息队列中,在Window中消息队列是在操作系统中定义的。消息队列就如同一群排队打饭的少男少女,这群人中光景较好的排在前面,光景较差的排在后面,可以理解成是一种优先级队列!要想更多的了解队列的相关知识,可参见队列。 
消息循环就是通过循环(如while)不断地从消息队列中取得队首的消息,并将消息分发出去。类似于上面的例子中分发饭菜值日生。 
消息处理就是在接收到消息之后根据不同的消息类型做出不同的处理。上面例子中值日生根据学生不同类型的饭票给他们不同等级的饭菜就是消息处理,学生手中的饭票就是消息所携带的信息。 
事件是根据接收到的消息的具体信息做出的特定的处理,放在代码中是事件响应函数。上面的例子中学生拿到饭菜后吃饭就是具体的事件。

消息机制模拟

在这里我们以控制台输入信息模拟窗口、对话框接收鼠标、键盘等消息,以ArrayBlockingQueue对象存放消息队列。在控制台中输入一个数值和一个字符串代表一个消息,输入-1结束输入。模拟代码如下:
package message;

import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * 消息
 * @author luoweifu
 */
class Message {
    //消息类型
    public static final int KEY_MSG = 1;
    public static final int MOUSE_MSG = 2;
    public static final int SYS_MSG = 3;

    private Object source;  //来源
    private int type;       //类型
    private String info;    //信息

    public Message(Object source, int type, String info) {
        super();
        this.source = source;
        this.type = type;
        this.info = info;
    }

    public Object getSource() {
        return source;
    }
    public void setSource(Object source) {
        this.source = source;
    }
    public int getType() {
        return type;
    }
    public void setType(int type) {
        this.type = type;
    }
    public String getInfo() {
        return info;
    }
    public void setInfo(String info) {
        this.info = info;
    }
    public static int getKeyMsg() {
        return KEY_MSG;
    }
    public static int getMouseMsg() {
        return MOUSE_MSG;
    }
    public static int getSysMsg() {
        return SYS_MSG;
    }
}

interface MessageProcess {
    public void doMessage(Message msg);
}

/**
 * 窗口模拟类
 */
class WindowSimulator implements MessageProcess{
    private ArrayBlockingQueue msgQueue;
    public WindowSimulator(ArrayBlockingQueue msgQueue) {
        this.msgQueue = msgQueue;
    }

    public void GenerateMsg() {
        while(true) {
            Scanner scanner = new Scanner(System.in);
            int msgType = scanner.nextInt();
            if(msgType < 0) {           //输入负数结束循环
                break;
            }
            String msgInfo = scanner.next();
            Message msg = new Message(this, msgType, msgInfo);
            try {
                msgQueue.put(msg);      //新消息加入到队尾
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    /**
     * 消息处理
     */
    public void doMessage(Message msg) {
        switch(msg.getType()) {
        case Message.KEY_MSG:
            onKeyDown(msg);
            break;
        case Message.MOUSE_MSG:
            onMouseDown(msg);
            break;
        default:
            onSysEvent(msg);
        }
    }

    //键盘事件
    public static void onKeyDown(Message msg) {
        System.out.println("键盘事件:");
        System.out.println("type:" + msg.getType());
        System.out.println("info:" + msg.getInfo());
    }

    //鼠标事件
    public static void onMouseDown(Message msg) {
        System.out.println("鼠标事件:");
        System.out.println("type:" + msg.getType());
        System.out.println("info:" + msg.getInfo());
    }

    //操作系统产生的消息
    public static void onSysEvent(Message msg) {
        System.out.println("系统事件:");
        System.out.println("type:" + msg.getType());
        System.out.println("info:" + msg.getInfo());
    }
}


/**
 * 消息模拟
 * @author luoweifu
 */
public class MessageSimulator {
    //消息队列
    private static ArrayBlockingQueue<Message> messageQueue = new ArrayBlockingQueue<Message>(100);

    public static void main(String[] args) {
        WindowSimulator generator = new WindowSimulator(messageQueue);
        //产生消息
        generator.GenerateMsg();

        //消息循环
        Message msg = null;
        while((msg = messageQueue.poll()) != null) {
            ((MessageProcess) msg.getSource()).doMessage(msg);
        }
    }
}
这里模拟用例中只有一个消息输入源,且是一种线程阻塞的,只有输入结束后才会进行消息的处理。真实的Windows操作系统中的消息机制会有多个消息输入源,且消息输入的同时也能进行消息的处理。

事件机制与消息机制的区别

Windows API可以开发窗口(界面)程序,Java通过Swing/AWT包也可以开发窗口(界面)程序,那么他们之间有什么异同呢? 
1. 实现方式不同,Windows API主要是通过回调,提供对外的接口由用户去实现对应的处理,内部由操作系统实现,我们看不到;Java中的Swing/AWT主要源-监听器(观察者)模式,实现窗口(控件)对象与事件处理对象的邦定。 
2. Windows API的消息机制有一个消息循环一直在接收消息,它是线程阻塞的。而Java的的Swing/AWT是一个通知方式,只有窗口(控件)有变化(被鼠标、键盘等触发)时才会通知监听者去处理,是非阻塞的。 

3. 相同点:都有消息源——窗口(控件),都有消息处理,Windows API是窗口处理函数,Java中是监听者的处理方法,都有消息(Java叫事件Event)。如果把Windows API中消息队列和消息循环去掉,两者就很像了,就如同使用SendMessage直接把消息发送到窗口处理函数。所以,事件机制也可以认为是特殊的消息机制。

消息机制有消息队列(先进先出的特点)、消息循环(分发)、消息处理。而事件机制有事件的添加、事件的移除和事件的分发,没有while消息循环。



2020 jeepxie.net webmaster#jeepxie.net
10 q. 0.008 s.
京ICP备10005923号