SWT源码分析 (一)

来源:互联网 发布:深圳java自行车 编辑:程序博客网 时间:2024/06/04 17:40

http://edgar108.iteye.com/blog/monthblog/2011-02


阅读本文需要知道搭建swt的开发环境,对SWT有基本的了解,最好对Windows api也有所了解,还要稍微了解一下JNI。

 

 

HelloSWT是一个基本的SWT程序,当点击输入框的时候,会弹出一个MessageBox。

 

Java代码  收藏代码
  1. import org.eclipse.swt.SWT;  
  2. import org.eclipse.swt.events.MouseEvent;  
  3. import org.eclipse.swt.events.MouseListener;  
  4. import org.eclipse.swt.graphics.Color;  
  5. import org.eclipse.swt.widgets.Display;  
  6. import org.eclipse.swt.widgets.MessageBox;  
  7. import org.eclipse.swt.widgets.Shell;  
  8. import org.eclipse.swt.widgets.Text;  
  9.   
  10. class HelloSWT {  
  11. public static void main(String[] args) {  
  12. Display display = new Display();// 创建一个display对象。  
  13. final Shell shell = new Shell(display);// shell是程序的主窗体  
  14. shell.setLayout(null); // 设置shell的布局方式  
  15. Text hello = new Text(shell, SWT.MULTI); // 声明一个可以显示多行信息的文本框  
  16. shell.setText("Java应用程序"); // 设置主窗体的标题  
  17. shell.setSize(200100); // 设置主窗体的大小  
  18. Color color = new Color(Display.getCurrent(), 255255255);// 声明颜色对象  
  19. shell.setBackground(color); // 设置窗体的背景颜色  
  20. hello.setText("Hello, SWT World!\n\n你好,SWT世界!");// 设置文本框信息  
  21. hello.pack(); // 自动调整文本框的大小  
  22. shell.pack(); //自动调整主窗体的大小  
  23. shell.open(); // 打开主窗体  
  24.   
  25. hello.addMouseListener(new MouseListener() {  
  26.   
  27. @Override  
  28. public void mouseUp(MouseEvent e) {  
  29. // TODO Auto-generated method stub  
  30. MessageBox box = new MessageBox(shell);  
  31. box.setMessage("内容");  
  32. box.setText("标题");  
  33. box.open();  
  34. }  
  35.   
  36. @Override  
  37. public void mouseDown(MouseEvent e) {  
  38. // TODO Auto-generated method stub  
  39.   
  40. }  
  41.   
  42. @Override  
  43. public void mouseDoubleClick(MouseEvent e) {  
  44. // TODO Auto-generated method stub  
  45.   
  46. }  
  47. });  
  48. while (!shell.isDisposed()) { // 如果主窗体没有关闭则一直循环  
  49. if (!display.readAndDispatch()) { // 如果display不忙  
  50. display.sleep(); // 休眠  
  51. }  
  52. }  
  53. display.dispose(); // 销毁display  
  54. }  
  55. }  

 

运行效果:

 


这个程序也是我们分析SWT运行的一个介入点。程序中为hello这个Text增加了鼠标事件的监听器,在 MessageBox box = new MessageBox(shell);这一行加上一个断点,因为可以肯定,在点击鼠标时,会执行这段代码,可以通过断点观察出前后程序执行的顺序。当在Text上点击鼠标时,会在断点处暂停,此时运行的上下文为:

 

 

从这里就可以看出程序的执行过程了,先从main方法开始,HelloSWT的51行为 if (!display.readAndDispatch())

 

Java代码  收藏代码
  1. while (!shell.isDisposed()) { // 如果主窗体没有关闭则一直循环  
  2.     if (!display.readAndDispatch()) { // 如果display不忙  
  3.         display.sleep(); // 休眠  
  4.     }  
  5. }  
  6. display.dispose(); // 销毁display  

 

 readAndDispatch方法在API中的说明为:

 

 

Reads an event from the operating system's event queue, dispatches it appropriately, and returns trueif there is potentially more work to do, or false if the caller can sleep until another event is placed on the event queue.

In addition to checking the system event queue, this method also checks if any inter-thread messages (created by syncExec() or asyncExec()) are waiting to be processed, and if so handles them before returning. 

 

从操作系统的事件队列里面读取一个事件,并做适当的分发,返回true时表示有工作要做,返回false表示没有接受到事件。...

当点击鼠标时,程序接收到相应的事件,readAndDispatch()就会返回true。

readAndDispatch的实现为:

 

Java代码  收藏代码
  1. public boolean readAndDispatch () {  
  2.     checkDevice ();  
  3.     lpStartupInfo = null;  
  4.     drawMenuBars ();  
  5.     runSkin ();  
  6.     runDeferredLayouts ();  
  7.     runPopups ();  
  8.     if (OS.PeekMessage (msg, 000, OS.PM_REMOVE)) {  
  9.         if (!filterMessage (msg)) {  
  10.             OS.TranslateMessage (msg);  
  11.             OS.DispatchMessage (msg);  
  12.         }  
  13.         runDeferredEvents ();  
  14.         return true;  
  15.     }  
  16.     return isDisposed () || (runMessages && runAsyncMessages (false));  
  17. }  

 

 

方法开始调用了一些其他函数,从名称上来看是做了一些检查和界面显示、菜单的相关操作,我们现在不关心这些,重点看这里:

 

Java代码  收藏代码
  1. if (OS.PeekMessage (msg, 000, OS.PM_REMOVE)) {  
  2.     if (!filterMessage (msg)) {  
  3.         OS.TranslateMessage (msg);  
  4.         OS.DispatchMessage (msg);  
  5.     }  
  6.     runDeferredEvents ();  
  7.     return true;  
  8. }  

 

 这里出现一个OS类,OS类是SWT内部的一个类,里面定义了很多native方法,我们知道SWT底层通过JNI调用了很多本地的windows api,所以程序的界面和本地的windows程序几乎是一样的,而Swing做出来的界面和具体的平台没关系,和一般的windows程序风格不一样,总是让人感觉不习惯。

OS类如下:

 

Java代码  收藏代码
  1. public class OS extends C {  
  2.     static {  
  3.         Library.loadLibrary ("swt"); //$NON-NLS-1$  
  4.     }  
  5.     ......  

 

 可见OS类中通过Library.loadLibrary ("swt")加载本地的dll,可以继续进入loafLibrary方法内部,最后得知

加载的是swt-win32-xxxx.dll 。xxxx是eclipse的版本号。在我的机器上是swt-win32-3650.dll。这个dll在eclipse安装目录下的plugins目录中的org.eclipse.swt.win32.win32.x86_3.6.0.v3650b.jar中。还有一个org.eclipse.swt.win32.win32.x86.source_3.6.0.v3650b.jar,里面是swt的源码。解压这个源码的jar之后,可以看到一个os.c文件,我猜测,OS类的native方法的实现就是在os.c中,并没有去验证。

os.c 的部分代码:

 

C代码  收藏代码
  1. #include "swt.h"  
  2. #include "os_structs.h"  
  3. #include "os_stats.h"  
  4.   
  5. #define OS_NATIVE(func) Java_org_eclipse_swt_internal_win32_OS_##func  
  6.   
  7. ......  
  8.   
  9. #ifndef NO_PeekMessageA  
  10. JNIEXPORT jboolean JNICALL OS_NATIVE(PeekMessageA)  
  11.     (JNIEnv *env, jclass that, jobject arg0, jintLong arg1, jint arg2, jint arg3, jint arg4)  
  12. {  
  13.     MSG _arg0, *lparg0=NULL;  
  14.     jboolean rc = 0;  
  15.     OS_NATIVE_ENTER(env, that, PeekMessageA_FUNC);  
  16.     if (arg0) if ((lparg0 = getMSGFields(env, arg0, &_arg0)) == NULL) goto fail;  
  17.     rc = (jboolean)PeekMessageA(lparg0, (HWND)arg1, arg2, arg3, arg4);  
  18. fail:  
  19.     if (arg0 && lparg0) setMSGFields(env, arg0, lparg0);  
  20.     OS_NATIVE_EXIT(env, that, PeekMessageA_FUNC);  
  21.     return rc;  
  22. }  
  23. #endif  

 

不同函数的定义都是类似:

 JNIEXPORT jboolean JNICALL OS_NATIVE(PeekMessageA)

(JNIEnv *env, jclass that, jobject arg0, jintLong arg1, jint arg2, jint arg3, jint arg4)

 

其中OS_NATIVE是一个宏,以上函数实际的定义为:

 

JNIEXPORT jboolean JNICALL Java_org_eclipse_swt_internal_win32_OS_PeekMessageA

(JNIEnv *env, jclass that, jobject arg0, jintLong arg1, jint arg2, jint arg3, jint arg4)

 

 

函数名为 :java中的包名_类名_方法名。这种格式是JNI要求的。

 

现在知道了,SWT调用OS类的static native方法就相当于直接调用了Windows SDK Api。

回到Display类中,OS.PeekMessage的作用是从系统的消息队列取消息,如果取到了消息就返回true。

 

OS.TranslateMessage (msg);将消息进行一些转化。

OS.DispatchMessage (msg);将消息发送给相应的窗口。

 

这几个都是windows api 函数,不清楚也不要紧,这些是每个windows程序都有的,后面我会贴出一个最简单的

windows c程序。

 

随后是runDeferredEvents ()函数,函数如下:

 

Java代码  收藏代码
  1. boolean runDeferredEvents () {  
  2.     boolean run = false;  
  3.     /* 
  4.     * Run deferred events.  This code is always 
  5.     * called in the Display's thread so it must 
  6.     * be re-enterant but need not be synchronized. 
  7.     */  
  8.     while (eventQueue != null) {  
  9.           
  10.         /* Take an event off the queue */  
  11.         Event event = eventQueue [0];  
  12.         if (event == nullbreak;  
  13.         int length = eventQueue.length;  
  14.         System.arraycopy (eventQueue, 1, eventQueue, 0, --length);  
  15.         eventQueue [length] = null;  
  16.   
  17.         /* Run the event */  
  18.         Widget widget = event.widget;  
  19.         if (widget != null && !widget.isDisposed ()) {  
  20.             Widget item = event.item;  
  21.             if (item == null || !item.isDisposed ()) {  
  22.                 run = true;  
  23.                 widget.sendEvent (event);  
  24.             }  
  25.         }  
  26.   
  27.         /* 
  28.         * At this point, the event queue could 
  29.         * be null due to a recursive invocation 
  30.         * when running the event. 
  31.         */  
  32.     }  
  33.   
  34.     /* Clear the queue */  
  35.     eventQueue = null;  
  36.     return run;  
  37. }  

 作用是从SWT的 eventQuene中取出一个事件(事件是怎么放入到eventQueue中的呢?后面说),找到事件对应的控件,让这个控件去处理这个事件。

 

到现在分析到前面断点那个图的第三步,后面的过程会继续分析。

0 0