在Canvas中调用repaint后,系统会调用paint(Graphics g),而能给与程序员发挥的空间就在paint里,那么,paint的Graphics从那里来的呢?换句话说,我们paint操作的东西到底是什么呢?他又是怎么反应到手机硬件屏幕上了呢?
1。我们先来看看repaint是怎么实现的。
public final void repaint(int x, int y, int width, int height) {
synchronized (Display.LCDUILock) {
callRepaint(x + viewport[X], y + viewport[Y], width, height, null);
}
}
2。那么callReapint是怎么实现的呢?
在Displable里有一个方法(以final标识),此方法禁止任何子类重写,就是为了保证只有他来控制repaint事件的传递:
final void callRepaint(int x, int y, int width, int height, Object target) {
if (currentDisplay != null) {
// Note: Display will not let anyone but the current
// Displayable schedule repaints
currentDisplay.repaintImpl(paintDelegate, x, y, width, height, target);//target是NULL
}
}
3,在Diaplay里,此方法的实现如下:
void repaintImpl(Displayable d, int x, int y, int w, int h,
Object target) {
synchronized (LCDUILock) {
if (paintSuspended || !hasForeground || d != current) {
return;
}
}
eventHandler.scheduleRepaint(x, y, w, h, target);//把Repaint的请求部署下去
}
4. /**
* Called to schedule a repaint of the current Displayable
* as soon as possible
*
* @param x The x coordinate of the origin of the repaint rectangle
* @param y The y coordinate of the origin of the repaint rectangle
* @param w The width of the repaint rectangle
* @param h The height of the repaint rectangle
* @param target An optional target Object, which may have been the
* original requestor for the repaint
*/
public void scheduleRepaint(int x, int y, int w, int h, Object target) {
eventQueue.push(x, y, w, h, target);//安排后的处理就是往事件队列里push一个消息
}
5.
/**
* Push a repaint
*
* @param x The x origin coordinate
* @param y The y origin coordinate
* @param w The width
* @param h The height
* @param target The optional paint target
*/
public void push(int x, int y, int w, int h, Object target) {
try {
w += x; // convert from width, height to absolute
h += y; // x2, y2
if (x < 0) x = 0;
if (y < 0) y = 0;
synchronized (qLock) {
if (paintX1 == -1) {
// If we have no pending repaint
// just store the region
paintX1 = x;
paintY1 = y;
paintX2 = w;
paintY2 = h;
paintTarget = target;
} else {
// If there is a pending repaint
// union the dirty regions
if (paintX1 > x) {
paintX1 = x;
}
if (paintY1 > y) {
paintY1 = y;
}
if (paintX2 < w) {
paintX2 = w;
}
if (paintY2 < h) {
paintY2 = h;
}
paintTarget = null;
}
} // synchronized
} catch (Throwable t) {
t.printStackTrace();
}
queuedEventHandler.process(); //push消息后,就要立即进行处理,
}
6. 其实,一个事件队列就是一个无限循环的线程,不停的检查队列,看是否有需要处理的消息,有则处理,无则等待。现在既然已经push进去一个消息,就要立即唤醒他去处理这个消息。所以自然就notify了。
/**
* Signal this handler there is a need to process
* a pending event.
*/
public synchronized void process() {
try {
notify();
} catch (Throwable t) {
// TO DO: Do something more useful with this
t.printStackTrace();
}
}
7. 我们来看看事件队列EventQuene的线程是如何定义的,其中repaintScreenEvent是来对repaint事件进行处理的。
/**
* Process events from the EventQueue
*/
public void run() {
int x1, y1, x2, y2, type = 0;
Display parentOfNextScreen = null;
Displayable nextScreen = null;
boolean call = false;
........
if (x1 != -1) {
repaintScreenEvent(x1, y1, x2, y2, target);
x1 = y1 = x2 = y2 = -1;
target = null;
}
8. 那么,我们再来看看是如何实现repaintScreenEvent的,
/**
* Process a repaint event
*
* @param x1 The x origin coordinate
* @param y1 The y origin coordinate
* @param x2 The lower right x coordinate
* @param y2 The lower right y coordinate
* @param target The optional paint target
*/
void repaintScreenEvent(int x1, int y1, int x2, int y2, Object target) {
try {
synchronized (eventLock) {
displayManager.repaint(x1, y1, x2, y2, target);//调用了屏幕管理器的reapint,请注意,这是5个参数的repaint
}
} catch (Throwable t) {
handleThrowable(t);
}
}
9. 这个5个参数的实现方法如下:
/*
* SYNC NOTE: this method performs its own locking of
* LCDUILock and calloutLock. Therefore, callers
* must not hold any locks when they call this method.
*/
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);
}
10. 可见,screenGraphcis当作了参数传递给了callPaint,在需要知道screenGraphics如何来的之前,我们先看看callPaint方法。
/**
* Paint this Canvas
*
* @param g the Graphics to paint to
* @param target the target Object of this repaint
*/
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); //这就是调用后repaint方法后,系统会调用到paint(g),这个g就是调用callPaint方法传递过来的screenGraphcis,请看下面分析
} catch (Throwable t) {
Display.handleThrowable(t);
}
g.translate(-viewport[X], -viewport[Y]);
}
}
11,现在我们来看看screenGraphics是从那里来的,在Display类里,有一段静态数据初始化的代码
./*
* ************* Static initializer, constructor
*/
static {
/* done this way because native access to static fields is hard */
DeviceCaps c = new DeviceCaps();
WIDTH = c.width;
HEIGHT = c.height;
ADORNEDHEIGHT = c.adornedHeight;
………
c = null; // let the DeviceCaps instance be garbage collected
/* Let com.sun.midp classes call in to this class. */
displayManagerImpl = new DisplayManagerImpl();//初始化屏幕管理器
DisplayManagerFactory.SetDisplayManagerImpl(displayManagerImpl); //得到屏幕管理器,这个一个工厂方法,为以后使用做准备,此处不赘叙
deviceAccess = new DisplayDeviceAccess(); //猜测可能是KVM和DEVICE的桥接器
eventHandler = getEventHandler(); //事件处理器
screenGraphics = Graphics.getGraphics(null);//哈哈,screenGraphics找到了
}
12. 在Graphics类里有个方法,getGraphics,但是,这个方法是默认的package级别的访问权限。所以我们不会看到。
static Graphics getGraphics(Image img) {
if (img == null) {
return new Graphics(Display.WIDTH, Display.HEIGHT);
} else {
return new ImageGraphics(img); //猜猜它是为谁服务的?
}
}
13. Graphics的构造方法如下:
Graphics(int w, int h) {
destination = null;
maxWidth = (short) (w & 0x7fff);
maxHeight = (short) (h & 0x7fff);
init();
reset();
}
14. 天哪,终于找到了,native的一个接口,init,是它来初始化了Graphics
/**
* Intialize the native peer of this Graphics context
*/
private native void init();
15.其实,找到这里,还不能说挖掘到了最根本的实现,但是,我们暂且就挖到这里,所以,总结一下,paint中的Graphics,其实是Display来提供的,只是,这个变量不会让我们看到。那么KVM的底层实现,也就是native的init(),会把这个Graphics给对接到某个具体的平台,比如win32的GDI,Symbian的Graphics GDI,还有LINUX常见的QT平台的GDI等等。
最后还望各位看官给多多指点错误。我去喝咖啡去了。。。