`
snake1987
  • 浏览: 72745 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java的gui(一)

    博客分类:
  • java
阅读更多
注:该文章只是一个尝试。
一直在想办法以一种良好的方式省时省事地记录自己的思维,系统地管理学到的知识,试过不少方法,都不凑效
本文将试着从开始着手看一个问题开始,每一步都跟踪自己的思维,记录相关的资料的出处




因为工作中会经常用到java的gui,一直在找一个契机,去学习一下java的gui相关知识。

在看到《java concurrency in practice》的第九章第一节:Why are GUIs Single-threaded?时,这个等待了许久的契机终于出现了。


这里先简介一下该章第一节的内容(根据自己的理解,着重点可能与该章节不相同):
1.单线程的gui框架并不是唯一的,除了java,像Qt, NextStep, MacOS Cocoa, X Windows都是用单线程的

2.不缺乏对多线程gui框架的尝试,但基本都以失败告终了,为什么不能够用多线程?
原因有两个:
a.两类不同形式的运行动作,让多线程的gui天生就趋向于死锁:一类是自上向下的,如改变字体颜色,这个命令会从应用层一直往下走,一直到操作系统,然后执行渲染工作,一类是自下向上的,如鼠标的点击,从用户点击,到操作系统封装的事件,一直往上走,直到应用程序,根据死锁产生的原因之一:两种相反方向的加锁(可参考《java concurrency in practice》第十章),这两种gui所不可能避免的动作必然会导致死锁
b.mvc模式,我们知道,c可以改变m,然后反应到v上去,c也可以调用v的接口,让v去对m进行查询,这其实也是反向加锁的两种动作

3.简述了EDT的一些缺点(长任务)和限制(更新gui的操作应该都由EDT来执行)


其中,为什么不能够用多线程的两个原因中第一个原因,文章提供了一篇拓展阅读,但文中的地址有点问题,正确的地址应该是:Multithreaded toolkits: A failed dream?
懒得看英文的朋友可以下载附件(虽然翻译水平差了点,但应该意思是到位了)


文章其实也没说太多内容,主要还是针对于上述原因1做一些详细的论述,但也根据该文章了解到了这样一些内容
a.awt初衷是设计多线程的,但最后发现无法兑现承诺,之后做的swing是单线程的。
b.不能用多线程的根源性问题在于上述原因1,即使这些工具包的开发者很聪明,很仔细,也是无法避免的
c."failed dreams"的含义:一个梦想,虽然事实上是无法达到的(人们其实不会都这样认为),但是不断地有人,很多的人去为这个梦想去付出努力,让这样一个梦想无限地趋向于实现(当时查了之后有点热血沸腾的感觉)
d.多线程的gui不一定是不可实现的,但他需要开发者足够地有天份,了解工具包的细节,了解每一个并发的过程,但作为一个用于商业的组件,显然是不现实的。


看完了文章,也基本得出了接下来的学习的一个方向:深入awt和swing工具包(因为根据上述文章的描述,awt起初是设计为多线程的,同时至今它也是线程安全的,而swing是参考了awt的设计做的,用的是标准的单线程的事件队列)

于是,基于了解java gui框架的目的,在网上经过简单搜索后发现这么两篇文章:
java 2用户界面
SWT、Swing 或 AWT:哪个更适合您?

又学到了这样一些内容
1.awt提供的组件是各类操作系统的一个交集
2.swing提供的组件是各类操作系统是一个并集
swing和awt都可以实现平台的无关性(也就是java的本质),而swt据说可以,但实际上没做得swing跟awt好

由于目标是了解awt和swing的整体的结构,所以对一些细节问题没太留意,因此在这两篇文章中学到的也十分地有限

由于找不到太有用的资料,只能从java api的包的树结构开始去学习,发现swing中比较重要而基础的JComponent是继承自awt的Component的,所以决定从awt包看起

于是找了比较简单的类开始入手——Label
1.该类的确应该是设计为线程安全的,里面大部分方法都做了锁的相关操作
2.该类很简单
3.发现一个叫“peer”的概念(对等体)


于是大概翻了下Component的源码,发现处处有peer的痕迹,于是很自然地了解到搞懂peer的概念可能是读懂awt的关键

于是很幸运地找到了这篇文章
细说Java GUI:AWT,SWT,Swing
英文原文

里面有用的内容非常多,在这里稍微整理一下作为以后回忆的笔记:
1.原生组件和模拟组件
原生组件:也就是调用本地方法,使用操作系统提供的api,awt就是典型的代表
模拟组件:swing的做法,自己模拟画图
2.历史:java的swing是由IBM以前的对头公司的开发团队来开发的,swt是由ibm开发的
3.java本质:write once, run anywhere
swt在平台兼容性方面是比不上awt和swing的
“尽管Steve Northover,SWT之父,辩称SWT是平台无关的,你可以很容易地发现许多Windows API的痕迹。”
4.awt用的是原生组件,它支持的组件是java所有操作系统的交集,而swing跟swt是并集,具体实现上swing大部分(除了顶层容器)都用模拟组件(纯java实现),而swt是只有平台不支持的才用模拟
5.对等体(peer):awt中主要使用的是对等体实现平台无关性,它的主要行为也是由对等体来调用jni来实现的(主要的行为都发生在这里),这也解释了为什么源码中只有极少的代码
6.swt也使用了对等体,但实现方式跟awt有所不同,区别在于一个是纯jni实现,一个在jni的基础上封装了一层,以达到多个操作黏合在一起形成一个更有意义的操作
7.swing没有使用jni,“Swing将组件自己的数据结构存储在JVM的空间中。它完全由自己管理画图处理,事件分发和组件布局。”它的底层是java 2D的api[/b]
8.swt需要显性地释放资源,awt由系统管理
9."Swing组件在操作系统中没有相应的对等体。它只是一块顶层容器中的逻辑区域,实际上它从顶层容器的对等体中借用资源。"
"Swing组件与顶层AWT容器共享一个对等体。"
"当操作系统开始更新UI的时候,顶层容器和Swing组件总是先于AWT组件绘制。当它们完成绘制,AWT组件会覆盖Swing可能绘制过的地方。因此不提倡Swing和AWT组件的混用。"
10.look and feel观感(不太了解具体含义,是否是指类似renderer,editor这样的?):原生组件无法完成的东西,因为它依赖于操作系统的实现,但模拟组件却是可以模拟任何不存在的组件。
"Java 2D在Java中是强大的类库,它为高级图像处理,颜色管理,图形绘制和填充,坐标系变换和字体生成提供丰富的特性"
11.又谈到了多线程与单线程模型
12.谈到了想要仔细了解的一个重点:事件分发线程
"AWT读取操作系统中的基本事件并在java代码中处理它们。AWT事件分发线程被命名为?AWT-{OS}-Thread。这个线程由AWT系统隐式启动。当AWT应用程序启动时,这个线程在背后启动,在操作系统上抽取和移除事件。当诸如按钮动作这样的逻辑事件被触发时,它传递给注册在按钮上的操作监听器。开发者也能为AWT组件编写鼠标,键盘和绘制事件的事件监听器。这通常通过扩展AWT Canvas组件来完成。这一组件支持了所有已提供的事件,而且你可以通过重写事件处理方法,实现一个自定义的组件。"
"然而,在Swing中,这是一个不同的线程。Swing把AWT事件作为自身事件系统的一个输入。它获取AWT事件并做一些初始化处理,产生一个对应的Swing事件并把它放到自己的事件队列上。Swing也有自己的事件分发线程,通常命名为EventQueue-0。这个线程从事件队列中抽取Swing事件,就如同AWT从操作系统中抽取那样。然后它把事件分发给目标组件。通常事件首先被分发给组件的顶层容器,然后由顶层容器的dispatch方法处理,它可能被再分发或重定向到一个Swing组件,在那里继续由自己的监听器进行处理。"
"SWT更类似于AWT。唯一的区别是它要求开发者显式地书写事件循环代码。然而底层的实现细节是不同于AWT的。看看SWT的读取和分发事件代码,它会让你想起MFC的代码风格。"
"AWT,SWT和Swing都有相似的事件监听器模型。它们都使用观察者模式,组件和监听器的连接方式是把监听器添加到组件上,这组成了一个对象网络的模型。当事件被触发并分发给组件,组件调用它的监听器以处理事件。一个监听器也可以继续分发事件给后续的处理,或产生一个新的事件并把它广播到网络中的其他节点上。基本上有两种不同的广播事件方式。一种是同步调用监听器。另一种是异步地将事件发送回队列,它将在新一轮的事件分发中被分发出去。
    除了直接发送给队列的方式,Swing还有一些其他的分发异步事件的方法。其中之一是调用SwingUtilities 或EventQueue 的invokeLater,这一方法包装一个Runnable对象到事件中并把它发送给事件队列。这确保了Runnable的run方法能在事件分发线程上执行,保证了线程安全。实际上,Swing的Timer好SwingWorker基于这一机制实现。SWT也有相应的发送异步事件的方式。它的invokeLater的对应方法是display.asyncExec,它以一个Runnable对象作为参数。"

最后两段的内容其实《concurrency in practice》也提到了,而且讲得还详细些。
13.系统地描述了awt,swt,swing各自的优劣[/b]
基本也就是原生组件跟模拟组件各自的优劣
14.swing扩展组件的方法:
A.继承已有组件;
B.靠复合组件的方式扩展。
C.从零开始使用JComponent编写自定义组件;
D.使用渲染器和编辑器机制扩展复制的Swing组件,如JList,JComboBox,JTable,JTree等;
E.基于已有Look And Feel 或从零开始创建新的Look And Feel;
F.使用LayeredPane,GlassPane或拖放机制开发高级的组件,例如浮动的固定组件,自定义弹出窗口,自定义菜单等。
15.数据输送时间:数据输送时间是指用于将应用程序数据传递给UI组件所需要的时间。例如,一个学生管理系统要求从数据库中装载学生信息并在一个表格中显示出来。花费在从内存到表格组件的数据传递时间被称为数据输送时间.
在数据传送时间上,swing更有优势,
a.swing有许多设计良好的组件模型(model)
b.swing相比awt和swt少了一个从jvm到操作系统的数据转换过程(awt/swt需要从操作系统到jvm,然后再从jvm到操作系统,swing是直接画出来)

看完该文章后,再次定位到三个学习目标
1.事件分发线程和它的观察者模式是了解awt的关键(其他的就是原生组件引用peer的事了),同时swing也需要了解这一点,swing相当于是在awt的事件线程上做的第二次的分发
2.java 2D的实现:也就是渲染的过程(这一点需要权衡一下,如果需要占用太多时间,可只是基本了解或者放弃,个人认为与操作系统关联性会很高,而且以后用到的机会少)
3.据文章描述,swing的api是设计得非常良好的api,参考它的可扩展性设计可能会对以后的工作有很大的帮助


接着,自然是先找到这个事件线程了,然后看它事件是怎么收的,然后又怎么进行分发的,然后是每个组件收到事件后怎么做,或者说怎么进行二次分发?
怎么找事件线程?用了个很简单的办法,写了点测试代码,然后debug,直接就得到了调用栈,然后找到了这个事件调度线程:EventDispatchThread
再往深点看一下,因为关联了jdk的源码,直接断了个断点在EventDispatchThread的构造函数中,然后又得到了EventDispatchThread的创建的调用栈
从最底部的main看起,发现了一件有点意思的事情(Component中的)
show方法是被废弃掉的,但推荐使用的setVisible居然调用的是show()~,那废弃干嘛呢?
    public void setVisible(boolean b) {
        show(b);
    }

找了找JComponent的setVisible,发现除了调用super.setVisible(Component的)之外,还重画了组件
    public void setVisible(boolean aFlag) {
        if(aFlag != isVisible()) {
            super.setVisible(aFlag);//注意,掉用了super的,即会一直往上走,直到调用Component的该方法,即是也会用原生组件来重画该组件,这明显与文章中借用顶层的资源来画图的说法不一致
            Container parent = getParent();
            if(parent != null) {
                Rectangle r = getBounds();
                parent.repaint(r.x,r.y,r.width,r.height);
            }
	    // Some (all should) LayoutManagers do not consider components
	    // that are not visible. As such we need to revalidate when the
	    // visible bit changes.
	    revalidate();
        }
    }

这就恍然大悟了,估计是在做swing的时候为了统一接口将show给废弃的
但又发现了另外一个问题:据文章描述,swing中的组件是借用的是上层组件的对等体peer,如果让我们来做,想必是通过某种途径(主要应该由JComponent来覆盖掉Component的方法,找到顶层组件的peer),然后采用借用资源,然后进行绘图,那为什么他还是调用了super.setVisible?
不过这个问题可能还会关联到更多的问题,暂时先看下去,作为遗留问题处理吧


再往上走,于是开始看到的对等体peer相关的代码,然后开始研究EventQueue的源码
经过粗略的分析,总结出以下几点
awt的EventQueue提供了生产消费者模式,生产者是AWT-Windows线程(下面简称w),消费者是AWT-EventQueue-0(简称e),下面的图片可看出一些端倪
w线程主要由本地方法eventloop来循环拿到系统事件,然后是根据不同系统对应的Toolkit,然后执行EventQueue的postEvent方法将事件放入到队列中,之后调用了EventQueue的wakeup方法,唤醒e线程取数据,进行分发。
e线程负责对事件进行分发,分发完毕后则阻塞在getNextEvent方法,等待唤醒
在锁策略方面只用了固有锁(那时候并发包还没有~),因为只有一个队列,只需要在入队跟出队时上锁就可以了



简单画了个流程图


再看一下EventQueue在细节上的实现
1.虽然是个EventQueue,但其实封装了四个不同优先级的队列,同时只允许一个线程对Event进行操作,其中最高优先级的两种事件都是peer(对等体)产生的事件,paint或者update事件是最低的优先级,其他的为普通优先级(该举措能提高响应率)
2.存放了一个消费者的引用
3.虽然说awt只有一个队列(书中,文章中都这样说,图也这样画了),但其实是两个队列,一个是EventQueue,如上所说,包含4个优先级,同时提供消费者去取事件的方法,一个是PostEventQueue,它存放所有事件,相当于一个缓冲区,先经过该队列,才会放到真正的EventQueue中去。
4.接着是做gui的必须熟悉的方法了:invokeLater和invokeAndWait

    public static void invokeAndWait(Runnable runnable)
             throws InterruptedException, InvocationTargetException {
        if (EventQueue.isDispatchThread()) {
            throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
        }
	    class AWTInvocationLock {}
	    //每个进入的方法都创建一个
        Object lock = new AWTInvocationLock();
		//将锁传进去,到执行完毕后执行notifyAll
        InvocationEvent event = 
            new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
				true);

        synchronized (lock) {
            Toolkit.getEventQueue().postEvent(event);
            //等待,任务执行完毕后会执行notifyAll,然后继续往后执行
            lock.wait();
        }
        Throwable eventThrowable = event.getThrowable();
        if (eventThrowable != null) {
            throw new InvocationTargetException(eventThrowable);
        }
    }

5.awt提供了接口实现一种叫联合事件的东西(不过貌似不推荐使用),不过总是一个思路嘛,所以在这也记录一下:
/*                                                                               
     * Should avoid of calling this method by any means                            
     * as it's working time is dependant on EQ length.                             
     * In the wors case this method alone can slow down the entire application     
     * 10 times by stalling the Event processing.                                  
     * Only here by backward compatibility reasons.                                
     */

只需要覆盖Component的isCoalescingEnabled方法和coalesceEvents(AWTEvent existingEvent,AWTEvent newEvent)方法就可以了,前提是事件源相同,event id相等,event id可以参考下AWTEvent的id

至此,生产者和队列的代码和思路已基本清晰了,至于本地方法执行的内容,也基本可以猜出来了,下一步就是理解消费者的行为了



  • 大小: 12.5 KB
  • 大小: 125.7 KB
分享到:
评论
3 楼 cantellow 2011-05-12  
cantellow 写道
引用
show方法是被废弃掉的,但推荐使用的setVisible居然调用的是show()~,那废弃干嘛呢?

这个就是API对外承诺的问题了,show方法被废弃,那是因为sun不想让开发人员直接调用到show,本身的实现其实没有任何问题,多封装一层setVisible,估计是给以后的扩展留有余地,个人猜测。

抱歉,没有看完,哎
2 楼 cantellow 2011-05-12  
引用
show方法是被废弃掉的,但推荐使用的setVisible居然调用的是show()~,那废弃干嘛呢?

这个就是API对外承诺的问题了,show方法被废弃,那是因为sun不想让开发人员直接调用到show,本身的实现其实没有任何问题,多封装一层setVisible,估计是给以后的扩展留有余地,个人猜测。
1 楼 cantellow 2011-01-21  
占个位先,等想好了在发表。
顺便广告位招租。

相关推荐

    java GUI编程炫酷皮肤包

    Java GUI(图形用户界面)编程是开发桌面应用程序的重要部分,它可以提供丰富的用户交互体验。炫酷的皮肤包为GUI增加了视觉吸引力,使应用更具个性化。本皮肤包专注于为Java GUI应用提供美观、现代的设计元素,以...

    java GUI 事件监听

    本篇文章将围绕一个具体的示例——绘制一个棋盘并实现按钮与面板的事件监听,来深入探讨相关的Java GUI编程技巧。 #### 二、核心知识点 ##### 1. 导入必要的包 在编写Java GUI程序时,通常需要导入以下几个关键包...

    javaGUI开发的小程序

    本项目中提到的"javaGUI开发的小程序"显然使用了Swing库,因为压缩包文件名中包含“swing”。 Swing是Java Standard Edition (Java SE) 的一部分,它提供了一系列组件(JButton, JTextField, JFrame等)用于构建...

    生命游戏javaGUI实现界面十分美观

    本文将详细讨论如何使用Java图形用户界面(GUI)技术来实现一个美观的生命游戏界面。Java GUI为我们提供了丰富的组件和工具,可以创建出互动性强、视觉效果良好的应用程序。我们将主要关注以下几点: 1. **Java GUI...

    随机出题系统的java实现,使用java GUI和MySql数据库!

    内容概要:通过程序注释带着读者了解GUI界面构建过程,初步了解 java GUI的使用,理解GUI框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:登陆确认、随机出题、题目文本导入等内容实现。 适合人群:具备...

    Java GUI

    AWT 是 Java 早期的图形界面设计,但是它存在两个缺点:一是只提供基本的组件,二是不能跨平台。于是,Sun 公司研发了 Swing 组件,弥补了 AWT 的不足。Swing 组件是轻量级的、可跨平台的,提供了许多新的组件,可以...

    Java GUI程序设计.pdf

    Java GUI程序设计

    Java GUI 美化包

    美化包就是为了解决这一问题而出现的,它提供了丰富的皮肤、主题以及定制化的组件样式,使得Java GUI应用看起来更加专业且吸引人。 在Java GUI美化包中,开发者可以找到各种预设的UI主题,例如Windows、Mac OS X、...

    使用Java GUI实现用户登陆界面

    在本项目中,我们将探讨如何使用Java图形用户界面(GUI)技术来实现一个用户登录界面。这个任务适合大学二年级的学生作为编程作业,特别是在学习Java基础和GUI编程时。Java GUI允许开发者创建丰富的、交互式的应用...

    Java GUI实例下载

    Java图形用户界面(GUI,Graphical User Interface)是Java编程中的一个重要组成部分,它允许开发者创建交互式的桌面应用程序。本资源“Java GUI实例下载”提供了一系列关于Java GUI编程的实例,涵盖了事件处理、...

    java GUI生成二维码

    Java GUI生成二维码是一种在Java平台上使用图形用户界面(GUI)创建和显示二维码的技术。二维码,全称为二维条形码,是一种高效的信息编码方式,能够存储大量数据,如文本、URL、联系信息等,并且可以被智能手机等...

    java GUI界面的换算工具

    Java GUI界面的换算工具是一种基于Java编程语言开发的应用程序,它主要用于进行各种物理或非物理单位之间的转换。GUI(图形用户界面)是这个工具的核心部分,它为用户提供了一个直观、友好的操作环境,使用户无需...

    Java GUI GUI超市管理系统(mysql+java)

    Java GUI超市管理系统是一款基于MySQL数据库和Java编程语言的软件应用,专为课程设计或小型商业环境中的超市管理而开发。这个系统充分利用了Java的图形用户界面(GUI)库,如Swing或JavaFX,来创建直观易用的操作...

    java gui源代码

    Java GUI(图形用户界面)是Java编程中一个重要的部分,它允许开发者创建具有交互性的桌面应用程序。本资源包含了一系列的Java GUI源代码,非常适合初学者学习和实践。这些源代码涵盖了多种GUI组件和布局管理器的...

    Java GUI万能模板.zip

    Java图形用户界面(GUI,Graphical User Interface)是Java编程中的一个重要部分,它允许开发者创建交互式的桌面应用程序。"Java GUI万能模板"通常是指一套通用的、可复用的代码结构,帮助开发者快速构建GUI应用,...

    java GUI 生成器

    在提供的压缩包文件中,"javagui.CAB"可能是一个包含该GUI生成器软件组件的 CAB( Cabinet)文件,这是一种Microsoft开发的压缩文件格式,常用于Windows安装程序。"setup.exe"是安装程序文件,用于在用户的计算机上...

    JAVA-GUI学生管理系统

    【JAVA-GUI学生管理系统】是一种基于Java图形用户界面(GUI)技术开发的系统,主要用于管理学生信息。在Java编程环境中,GUI(图形用户界面)允许开发者创建交互式的应用程序,使得用户可以通过直观的图形元素如按钮...

    Java gui简单程序

    在Java编程领域,GUI(图形用户界面)是创建桌面应用程序不可或缺的一部分。本示例程序"Java GUI简单程序"旨在教你如何构建一个基本的GUI界面,同时整合数据库连接、查询功能,并利用JTable来展示查询结果。这是一个...

    Java 应用程序JavaGUI.zip

    JavaGUI,全称为Java图形用户界面(Graphical User Interface),是Java编程语言中用于创建交互式用户界面的技术。JavaGUI允许开发者构建具有按钮、文本框、菜单等元素的桌面应用程序,使得用户可以通过视觉方式进行...

    JAVA GUI编程实例集.rar

    Java GUI(图形用户界面)编程是Java开发中的一个重要领域,它允许开发者创建具有交互性的桌面应用程序。本资源“JAVA GUI编程实例集.rar”显然是为初学者设计的,旨在通过实例帮助学习者掌握Java GUI的基本概念和...

Global site tag (gtag.js) - Google Analytics