这几天都在看Kuix的源代码,了解它的消息处理机制,事件处理,窗口刷新,内部线程处理等等,其实Kuix的核心代码作者已经标志的很清楚了,org.kalmeo.kuix.core,widget包可以修改控件的一些表现,要理解框架的话还是应该看core包,尤其是KuixCanvas.我把Kuix分为三个部分,首先是框架的内部处理机制,主要是几个内部处理线程,其次是控件的布局管理,再次是xml和css的文件解析和控件包,一般人都会习惯从后面开始入手,我也是这样开始的.
要说明Kuix的窗口显示机制,还是先从一个案例入手,这个问题,我的程序中从服务器读取数据后,弹出新的窗口(或者更新当前窗口),服务器连接用线程管理,接收后更新界面,保证主线程不会足赛,这会出现一个很奇怪的现象,有时候刷新的界面会弹不出来,按动任何一个键盘界面才会马上显示,而且这个现象是随机出现的,在实机上出现的频率较高,根据你接收数据的处理效率,处理任务更重出现的效率越高,比如我采用了ssl通道加密后,出现界面"停滞"的几率大大增高.
首先怀疑是接收处理数据的代码有问题,或者数据有异常,接着做的一个测试排除了代码或者数据问题,我打开一个连接后,不读取服务器的数据,直接打开本地的ui,这一现象仍然出现,所以判断为线程同步问题,可是对screen,frame加同步锁后,现象更加明显.实际上这个问题出现了一个多月,一直没有彻底解决,知道最近才有时间解读Kuix的窗口弹出机制.
先从Desktop.setCurrentScreen入手,Desktop可以认为是Kuix所有容器的顶层容器,setCurrentScreen只是替换了当前screen变量,隐藏原来的窗口和菜单,注意,菜单不是screen的一部分,所以screen.remove只是清空窗口,菜单仍然保留
/**
* @param screen the screen to set
*/
public void setCurrentScreen(Screen screen) {
if (this.screen == screen) {
return;
}
if (this.screen != null) {
this.screen.remove();
}
// Hide menuPopups
Menu.hideAllMenuPopups();
// Check transition
if (screen != null) {
Transition transition = screen.getTransition();
if (transition != null && this.screen != null) {
Kuix.getCanvas().setTransition(transition);
}
}
this.screen = screen;
if (screen != null) {
super.add(screen);
}
}
继续追到Widget.add函数,主要是修改控件链表,触发添加事件等,主要还是invalidate(),触发界面更新
/**
* Invalidate the widget's size and position and propagate the information
* to its parent. Calling this method will generate a call to the
* <code>doLayout()</code> and </code>paint()</code> method on all
* invalidated widgets.
*
* @param fromWidget the Widget responsible of the invalidation.
*/
protected void invalidate(Widget fromWidget) {
invalidated = true;
if (parent != null && !parent.invalidated) {
parent.invalidate(fromWidget);
}
}
这个函数似乎看不出什么有用的东西,关键其实在parent.invalidate ,不断的向上递归调用,直到最顶层的Desktop.invalidate,这里有所不同了.调用Kuixcanvas.revalidateNextFrame,所以前面说过Kuixcanvas是相当核心的类,几乎所有的内部线程都在这个类里面.
/* (non-Javadoc)
* @see org.kalmeo.kuix.widget.Widget#invalidate(org.kalmeo.kuix.widget.Widget)
*/
public void invalidate(Widget fromWidget) {
super.invalidate(fromWidget);
Kuix.getCanvas().revalidateNextFrame();
}
追到下面的函数似乎线索中断了,一直没发现在哪里刷新界面了
/**
* Repaint through the Worker task
*
* @param x x coordinate of the repaint region
* @param y y coordinate of the repaint region
* @param width width of the repaint region
* @param height height of the repaint region
*/
public void repaintNextFrame(int x, int y, int width, int height) {
needToRepaint = true;
repaintRegion.add(x, y, width, height);
}
实际上关键就在needToRepaint = true;Kuixcanvas有一个内部线程,里面有这个代码,forceRepaint实际上就是刷新界面的函数了.
// Repaint
if (needToRepaint) {
forceRepaint();
}
所以实际上Kuix所有的事件都是通过线程操作的,而不是实时实现.这会有一些延迟,这个延迟就会导致我们用多线程的时候,界面会被随机"冻结",而操作一个任一个按键,都会触发KuixCanvas的消息处理线程,实际上最终触发重绘界面的函数,导致"迟到"的界面重新出现.