UI框架设计文档事件处理问题的补充

本文探讨了事件调度线程(EDT)和单线程方案在UI更新中的应用,强调了同步异步处理的重要性,并详细介绍了如何利用callSerially和callSeriallyAndWait方法保证UI更新的顺序性和及时响应。

关于系统设计某些问题的补充:

今天早上精神不太好,对一些问题没有组织清楚。

回来想了想,做出了调整,现在实现的方案大致如下:

n         方案研究:EDT和单线程

n         背景知识:同步异步,串行并行,生产者/消费者模式,事件队列

n         使用目的:顺序可预见性

n         单线程:将同步操作转为异步操作将并行处理转换为串行顺序处理

n         四大类UI回调方法:

* 抽象的命令,这是高层界面API的一部分
* 单个按键按下和释放的底层事件(如果触摸点设备可用的话,那么还包括触摸点事件)
* 回调Canvas类的paint()方法
* Display类的callSerially()方法引发的,对Runnable.run()方法的回调
所有的UI回调方法都是序列化的,所以它们绝不会并行发生。也就是,任何MIDP实现都不能在前一个回调未返回的情况下调用另外一个回调。这个性质确保了当一个事件发出的时候,它的前一个事件肯定已经处理完成。如果有多个未处理的UI回调,那么只要前一个UI回调返回了,下一个就会紧接着被调用。MIDP实现同时也要保证,使用callSerially()方法引发的run()方法要在所有未处理的repaint请求都得到满足之后才被调用。

n         一个程序应该有三种线程:

u       初始化线程:该线程主要目的是启动程序的图形用户界面(GUI)。一旦GUI启动后,对于大多数事件驱动的桌面程序来说,初始化线程的工作就结束了

u       UI事件调度线程EDT:该线程负责GUI组件的绘制和更新,通过调用程序的事件处理器来响应用户交互。所有事件处理都是在EDT上进行的,程序 UI组件和其基本数据模型的交互只允许在EDT上进行,所有运行在EDT上的任务应该尽快完成,以便UI能及时响应用户输入

u       任务线程:

n         EDT的作用:

u       会调用处理事件的listener,如单击一个按钮,会产生一个按钮事件放到事件队列中,然后EDT从事件队列中获得该事件,并且调用相应的listener进行处理,该部分代码已经完成

u       重绘画面paint或者update:当我们想重绘画面的时候,会去调用repaint.其实调用repaint并不是马上去画,而是记录下要绘制的区域,具体的绘制还是由EDT操刀完成,其实就是调用callSerially,产生一个线程,放到pendingSerialCall队列当中,等待每次edt loop的最后processingSerialCalls的调用,这个方案参照了J2ME底层defaultEventHandler的实现,该部分的代码已经完成

n         使用EDT的两个原则:任何GUI请求都应该在EDT中调用;任何与GUI无关的处理不要由EDT来负责

n         使用要点

u       从其他线程访问UI组件及其事件处理器会导致界面更新和绘制错误。

u       EDT上执行耗时任务会使程序失去响应,这会使GUI事件阻塞在队列中得不到处理。

u       .应使用独立的任务线程来执行耗时计算或输入输出密集型任务,比如同数据库通信、访问网站资源、读写大树据量的文件。

n         扩展思考:

u       不好的实现:

u       好的实现:

u       按照SwingWorkerinvokeLaterinvokeAndWait的设计思想,我的实现是做出两个函数,在Display.java里面

    /**

     * Causes the runnable to be invoked on the event dispatch thread. This method

     * returns immediately and will not wait for the serial call to occur

     *

     * @param r runnable (NOT A THREAD!) that will be invoked on the EDT serial to

     * the paint and key handling events

     */

    public void callSerially(Runnable r) {

        synchronized (sLock) {

            sPendingSerialCalls.addElement(r);

            sLock.notify();

        }

    }

 

    /**

     * Identical to callSerially with the added benefit of waiting for the Runnable method to complete.

     *

     * @param r runnable (NOT A THREAD!) that will be invoked on the EDT serial to

     * the paint and key handling events

     * @throws IllegalStateException if this method is invoked on the event dispatch thread (e.g. during

     * paint or event handling).

     */

    public void callSeriallyAndWait(Runnable aRunnable) {

        RunnableWrapper iWrapper = new RunnableWrapper(aRunnable, 0);

        callSerially(iWrapper);

        synchronized (sLock) {

            while (!iWrapper.isDone()) {

                try {

                    // poll doneness to prevent potential race conditions

                    sLock.wait(50);

                } catch (InterruptedException err) {}

            }

        }

}

 

callSerially负责创建一个含有Runnable的特定事件,并让其在EDT中排队等待调用,当被调用时就会运行Runnable中的run方法进行派发

callSerially一样,callSeriallyAndWait也把可运行对象排入事件派发线程的队列中,callSerially在把可运行的对象放入队列后就返回,而callSeriallyAndWait一直等待知道已启动了可运行的run方法才返回。如果一个操作在另外一个操作执行之前必须从一个组件获得信息,则callSeriallyAndWait方法是很有用的。

一般我们用到callSerially,都是为了执行事件派发线程中的代码,将一段更新UI事件的代码派发到EventQueue中,但是我们可以从事件派发线程中调用callSeriallyAndWait,却不能从事件派发线程中调用callSeriallyAndWait

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值