SUN在它的API里,说过。
Forces any pending repaint requests to be serviced immediately. This method blocks until the pending requests have been serviced. If there are no pending repaints, or if this canvas is not visible on the display, this call does nothing and returns immediately
强制让任何有repaint的请求立即得到服务。这个方法将阻塞,一直到此请求被“服务”掉 。如果没有repaint请求,或者当前canvas不可视,这个调用将立即返回,不做任何事。
比如:
当我们在一个线程里,设置了某些要画的object的坐标值。调用了repaint而没有调用serviceRepaint的画,那么,系统会从隶属于repaint的队列里进行区域运算,得到一个多次请求复合后的RECT,然后进行处理。那么是吗时候能得到处理呢?不知道。那要看系统情况,这个问题,我稍后再说。
那么调用了serviceRepaints呢,最后的结果是,系统会理解调用Paint。进行刷屏操作。
我们来看一下源码:
- 在Canvas中的代码是:
public final void serviceRepaints() {
Display d = currentDisplay;
if (d != null) {
d.serviceRepaints(this);
}
}
可见,canvas调用的是Display的serviceRepaints
- 在Display的代码是:
void serviceRepaints(Displayable d) {
synchronized (LCDUILock) {
if (paintSuspended || !hasForeground || d != current) {
return;
}
}
eventHandler.serviceRepaints();
}
不可视,无repaint请求,currentDisplay不一样,都将立即返回(不理解的将在以后加以说明)
- 那么,现在serviceRepaints的请求,将要被eventHandler处理。
那么,我们来看看eventHandler。
eventHandler是由Display来通过class.forName来加载的
-------------------------
private static EventHandler getEventHandler() {
String n = Configuration.getProperty(
"com.sun.midp.lcdui.eventHandler");
try {
return (EventHandler) (Class.forName(n)).newInstance();
} catch (Exception e) { }
if (Configuration.getProperty("microedition.configuration") != null) {
try {
return (EventHandler) (Class.forName(
"com.sun.midp.lcdui.AutomatedEventHandler")).newInstance();
} catch (Exception e) { }
try {
return (EventHandler) (Class.forName(
"com.sun.midp.lcdui.DefaultEventHandler")).newInstance();
} catch (Exception e) { }
throw new Error("Unable to establish EventHandler");
}
try {
return (EventHandler)
(Class.forName(
"com.sun.midp.lcdui.AWTEventHandler")).newInstance();
} catch (Exception e) { }
throw new Error("Unable to establish EventHandler");
}
-------------------------------
- 现在焦点又要到EventHandler了,看他是如何处理serviceRepaints的吧。
public void serviceRepaints() {
try {
eventQueue.serviceRepaints();
} catch (Throwable t) {
t.printStackTrace();
}
}
- oh,MyGod,现在又要转换焦点到eventQuene,那么再让我们来看看这个eventQuene是怎么处理serviceRepaints的。
class EventQueue {
/** The latest vm event */
int vmEvent;
/** The parent Display of the next Displayable. */
Display parentOfNextScreen;
/** The next displayable to show */
Displayable nextScreen;
/** A flag to process all call serially's */
boolean callSeriallyPending;
/** A flag to perform an invalidation of a Form */
boolean invalidatePending;
/** An Item can be the cause of an invalidation */
Item invalidItem;
/** An Item whose state has changed */
Item changedItem;
/** The dirty region for any pending repaint */
int paintX1, paintY1, paintX2, paintY2;
/** The optional target for the repaint */
Object paintTarget;
/** The lock for manipulating queue data */
Object qLock;
/**
* Create a new default EventQueue
*/
public EventQueue() {
qLock = new Object();
paintX1 = paintY1 = paintX2 = paintY2 = -1;
}
/**
* Service any pending repaints. If there is a pending
* repaint, process it immediately, otherwise return.
*/
public void serviceRepaints() {
int x1, y1, x2, y2;
Object target;
synchronized (qLock) {
if (paintX1 == -1) {
return;
}
x1 = paintX1;
y1 = paintY1;
x2 = paintX2;
y2 = paintY2;
target = paintTarget;
paintX1 = paintY1 = paintX2 = paintY2 = -1;
paintTarget = null;
}
repaintScreenEvent(x1, y1, x2, y2, target);
}
void repaintScreenEvent(int x1, int y1, int x2, int y2, Object target) {
try {
synchronized (eventLock) {
displayManager.repaint(x1, y1, x2, y2, target);
}
} catch (Throwable t) {
handleThrowable(t);
}
}
- 现在又要转移到DisplayManager上,这个类,我们再MIDP2。0里从来没有碰到过,所以,可以断定那个一定是一个厂商扩展包。现在开始找,我找,我找。。。
在com/sun/midp/lcdui/DisplayEvents.java里有一个接口类。他定义了一个接口。
void repaint(int x1, int y1, int x2, int y2, Object target);(5个参数)
肯定有一个扩展的类来实现了这个接口,我再找,我找,我找。。
public interface DisplayAccess extends DisplayEvents,我再找DisplayAccess,
- Display中有一个内部类:DisplayAccessor,这个类实现了DisplayAccess接口。
- 这个内部类里是如何实现repaint的呢?
public void repaint(int x1, int y1, int x2, int y2, Object target) {
Display.this.repaint(x1, y1, x2, y2, target);
}
可见,最后还是调用的Display的repaint方法,最后绕啊,绕啊,最后还是绕到Display这里了。
- OK,那么最后让我们来看看Display的repaint方法。
void repaint(int x1, int y1, int x2, int y2, Object target) {
Displayable currentCopy = null;
synchronized (LCDUILock) {
if (paintSuspended || !hasForeground) {
return;
}
currentCopy = current;
}
if (currentCopy == null) {
return;
}
screenGraphics.reset(x1, y1, x2, y2);
current.callPaint(screenGraphics, target);
refresh(x1, y1, x2, y2);
}
- current.callPaint是在Displayabe里实现的。
- Canvas重写了callPaint
void callPaint(Graphics g, Object target) {
super.callPaint(g, target);
if (g.getClipY() + g.getClipHeight() <= viewport[Y]) {
return;
}
// We prevent the Canvas from drawing outside of the
// allowable viewport - such as over the command labels
g.clipRect(viewport[X], viewport[Y],
viewport[WIDTH],
viewport[HEIGHT]);
synchronized (Display.calloutLock) {
g.translate(viewport[X], viewport[Y]);
try {
paint(g); //这个就是最后调用srviceRePaint后的效果。
} catch (Throwable t) {
Display.handleThrowable(t);
}
g.translate(-viewport[X], -viewport[Y]);
}
}
- 找到这里,似乎好象找到了答案,其实还没有,在Display中,有两个同步锁:
/** Static lock object for LCDUI package */
static final Object LCDUILock = new Object();
/** Static lock object for making calls into application code */
static final Object calloutLock = new Object();
这两个同步锁,一个是用来同步paint的操作。一个是用来同步诸多调用的,比如,系统通知KVM的
void callKeyPressed(int keyCode) {
if (allowKey(keyCode)) {
synchronized (Display.calloutLock) {/保证系统底层发来的消息不会紊乱。
try {
this.keyPressed(keyCode);
} catch (Throwable t) {
Display.handleThrowable(t);
}
}
}
}
- 因为这个问题,很多人都在争论,我想我就把sun的实现,理理,整理在这里,不过,sun为了实现扩展,模块化,我靠,真是绕了很多的弯,不过我说这个不是说sun的不好,而是让我费了很大的劲才找到。以上说的仅供你参考。
- 在Display里有几个类和方法是很需要注意的。
- DisplayAccessor
- DisplayManagerImpl
- 同步锁。
有了这些跟硬件结合机密的类,再加上native的API,一个完整的能给我们来操纵的世界就展现出来了,
- 最后,欢迎各位给我加评论。指出我的错误。
- 送大家一个问题:如果在KEYPRESS里调用serviceRepaint会怎么样?
我贴一段SUN的comment,估计会你有所收获的。嘿嘿
--------------------------------
Warning: This method blocks until the call to the application's
paint()
method returns. The application has no control over which thread calls
paint()
; it may vary from implementation to implementation. If the caller of
serviceRepaints()
holds a lock that the
paint()
method acquires, this may result in deadlock. Therefore, callers of
serviceRepaints()
must not hold any locks that might be acquired within the
paint()
method. The
Display.callSerially()
method provides a facility where an application can be called back after painting has completed, avoiding the danger of deadlock.