在android中,对view及其子类,都是画在surface上的。每个window对应一个surface,各surface对象通过surfaceflinger合成到framebuffer,每个surface都是双缓冲,它有一个back buffer和一个front buffer。back buffer就是画画的地方,front buffer是用来合成的。
surface创建Canvas对象(用来管理surface绘图操作),Canvas对应bitmap(存储surface内容)。当调用unlockCanvas()后,back buffer开始变为可用,就开始显示了。有一套机制实现back buffer和front buffer的互换,当要更新时,back buffer与front buffer互换,back buffer变成front buffer,
流程
- create a bitmap
- attach a canvas to it
- do the rendering into that canvas
- lockCanvas
- draw your bitmap into the backbuffer
- unlockAndPost
frameworks/base/core/java/android/view/Surface.java — Surface::Surface ()创建一个surface
public Surface(SurfaceSession s,
int pid, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
mCanvas = new Canvas();
init(s,pid,display,w,h,format,flags);
}
frameworks/base/core/jni/android_view_Surface.cpp — Surface_init ()。
在这个函数中SurfaceComposerClient 对象被创建。
frameworks/base/libs/ui/SurfaceComposerClient.cpp — SurfaceComposerClient::SurfaceComposerClient ()这个函数非常重要,在这里建立了client和server之间的桥梁。通过函数_get_surface_manager()获得了一个指向 server的IBinder 对象(具有ISurfaceComposer接口),之后通过这个IBinder就可以跨进程访问Server的功能。接着调用 ISurfaceComposer::createConnection()创建并返回了一个ISurfaceFlingerClient的 IBinder。
frameworks/base/libs/ui/SurfaceComposerClient.cpp — SurfaceComposerClient::createSurface().这个函数中,利用前面获得的ISurfaceFlingerClient的IBinder,调用其createSurface接
frameworks/base/libs/surfaceflinger/SurfaceFlinger.cpp — BClient::createSurface ()。Bclient由ISurfaceFlingerClient派生而来
frameworks/base/libs/surfaceflinger/SurfaceFlinger.cpp — SurfaceFlinger:: createSurface()。这个函数为Surface创建一个对应的Layer。
Android GUI系统:
涉及JAVA框架层的内容:
andriod.graphics 类 ,对应Skia底层库,提供绘图接口。
andriod.view.Surface 构建显示界面。
andriod.view.View 各种UI元素的基类。
Javax.microedition.khronos.opengles 标准的OpenGL接口。
Pixelflinger是一个底层的工具库。负责像素级别的基本处理。在system/core/include/pixelflinger/ 和/system/core/libpixelflinger/
libui是一个Andriod在本地层次的一个框架库,是GUI系统的中枢。这个库提供接口,其它的库通过类的继承方式来实现。包含颜色格式,Egl窗口,按键及事件处理,surface,overlay ,camera等多个方面的定义。
在framework/base/include/ui/ 和framework/base/libs/ui/中。
包含以下部分:
format(颜色格式)部分:需要用pixelflinger中的一些关于数据格式的定义。头文件为PixelFormat.h
Point.h Region.h Rect.h DispleyInfo.h
Native Windows (本地窗口)部分:主要是实现下个egl_native_window_t 的类。程序通过调用这个类来完成基本的显示功能。头文件为: EGLNativeSurface.h EGLDisplaySurface.h EGLNativeWindowSurface.h
Key/Event(按键和事件处理)部分:系统输入的基础,定义按键映射,通过Event事件设备来实现系统输入,头文件为:EventHub.h KeycodeLabels.h KeyCharacterMap.h
Surface(显示界面)部分:本部分定义显示界面较高层次的接口,包含显示界面的管理功能,头文件为带有Surface字符串的所有文件,这部分只是定义了Surface部分的框架,具体实现是SurfaceFlinger。
Overlay(显示部分的叠加层)部分:定义一个叠加的显示输出层接口,覆盖在主显示层之上,通常用于视频输出,主要在SurfaceFlinger中实现。头文件为:IOverlay.h Overlay.h
Camera部分:定义摄像头的框架和接口,主要在CameraService部分实现。头文件为带有Camera定符串的所有文件。
输入输出的接口:(主要和linux驱动打交道的)使用FrameBuffer的标准显示驱动和标准事件Input驱动,在libui中使用标准方式实现:
1。显示输出的硬件接口:
对于andriod的显示部分,需要实现的接口是egl_native_window_t,它是一个OpenGL结构,也是给libEGL使用的。
EGLNativeSurface.h定义了类EGLNativeSurface,这个类继承了egl_native_windows_t.
EGLDisplaySurface.h定义了类EGLDisplaySurface,继承了EGLNativeSurface,它是最终的实现类。
在EGLDisplaySurface.cpp所实现的构造函数中调用mapFrameBuffer()函数对驱动程序进行操作如:
status_t EGLDisplaySurface::mapFrameBuffer()
{
char const * const device_template[] = {
"/dev/graphics/fb%u",
"/dev/fb%u",
0 };
while ((fd==-1) && device_template[i]) {
snprintf(name, 64, device_template[i], 0);
fd = open(name, O_RDWR, 0);
i++;
}
struct fb_fix_screeninfo finfo;
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
return -errno;
struct fb_var_screeninfo info;
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
return -errno;
void* buffer = (uint16_t*) mmap(
0, finfo.smem_len,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd, 0);
if (buffer == MAP_FAILED)
return -errno;
// at least for now, always clear the fb
memset(buffer, 0, finfo.smem_len);
//省略了部分内容
}
2.输入的硬件接口:
对andriod的事件处理部分,主要是向上层提供统一的按键码(KeyCode),是一个整数,上层的Java程序中主要通过这个值来判断系统的实现。在libui中通过对标准的Input驱动的处理来将input值转换成andriod系统的按键码,按键码参考KeyCharacterMay.h头文件。
EventHub.cpp文件是输入部分的硬件抽象定义设备节点所在的路径。
Static const char *device_path = “/dev/input”; //输入设备目录
处理过程中将搜索路径下面所有input驱动设备节点,这在openPlatformInput()中通过调用scan_dir()来实现,scan_dir()将会从目录中查找设备,找到后调用open_device()将其打开。
bool EventHub::openPlatformInput(void)
{
//省略了部分内容
res = scan_dir(device_path);
if(res < 0) {
LOGE("scan dir failed for %s\n", device_path);
//open_device("/dev/input/event0");
}
return true;
}
主要事件处理有getEvent()中完成,处理过程是在一个无限循环内,调用阻塞的函数等待事件到来。
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
{
while(1) {
//省略了部分内容
for(i = 1; i < mFDCount; i++) {
if(mFDs[i].revents) {
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].fd, &iev, sizeof(iev));
}
//省略了部分内容
}
}
poll()函数会阻塞程序的运行,直到Input设备的相应事件发生,事件发生后poll()将返回,然后通过read()读取input设备发生的事件代码。
实现事件处理实际经过两个步骤:
1,将input设备的整数类型事件转换成表示按键的字条串。
2。将表示按键的字符串转换成android的按键码。
键盘布局文件(*.kl)将完成第一步转换,运行时的文本文件在system/usr/keylayout目录中。
源文件的development/emulator/keymaps/目录中有多个布局文件。
第二步是通过查找KEYCODES数组,将literal字符串转换成value的整数值。
(在keyCharacterMap.h文件中KEYCODES表示的数值和android.view.KeyEvent类中数值是一致的,这个java类的路径为frameworks/base/core/java/android/view/KeyEvent.java)
开发过程中对不同的硬件,只要定不同的键盘布局文件就OK了。Android中一般不需要增加新的按键码。
(在andriod的输入处理经过了两次映射,第一次将Event驱动中的整数按键码映射成字符串,第二次将字符串映射成Java的UI 程序中使用的整数值。如要增加按键在用户程序 中进行处理除了KeyCharacterMap.h和KeyEvent.java两个文件 ,还要改两个文件 tools/puppet_master/PuppetMaster.nav_keys.py和frameworks/base/core/res/res/values/attrs.xml
Surface系统:
包括本地代码和java代码部分,关系如下:
libui提供本地的Surface系统框架
surfaceflinger完成本地接口实现
java框架层次主要调用Surface向UI 提供接口
本地部分可以使用ISurface接口。
Surface系统本地接口
在libui中定义Surface的本地接口,路径frameworks/base/include/ui,主要有以下几个文件;
Surface.h
SurfaceComposerClient.h
ISurface.h
ISurfaceFlingerClient.h
IsurfaceComposer.h
Surface.h和 SurfaceComposerClient.h是为上层提供的调用接口通过surface系统的JNI提供给java层使用。ISurface.h IsurfaceFlingerClient.h IsurfaceComposer.h 是需要下层去继承和实现的接口,其中 Isurface.h 中的接口可以给本地程序来调用,进而实现图形数据输出功能。
在isurfaceComposeer.h接口中,定义了Surface系统的各种枚举值和接口,
class ISurfaceComposer : public IInterface
{
public:
DECLARE_META_INTERFACE(SurfaceComposer);
enum { // (keep in sync with Surface.java)
eHidden = 0x00000004,
//省略了部分内容
};
//省略了部分内容
}
调用createConnection()接口将构建一个ISurfaceFlingerClient,而eFXSurfaceNormal eFXSurfaceBlur eFXSurfaceDim 和 eFXSurfaceMask 表示不同类型的Surface层次,它们和java代码是对应的(对应surface.java文件)
SurfaceFlinger本地代码:
SurfaceFlinger是Surface的本地实现。实现Surface的建立。控制,管理等功能。其路径为:
frameworks/base/libs/surfaceflinger
在surfaceFlinger.h和SurfaceFlinger.cpp 文件中,SurfaceFlinger类继承IsurfaceComposer,是一个核心的实现。SurfaceFlinger::BClient类继承了ISurfaceFlingerClient。另一个重要的部分就是提供不同的层(layer),用于构建不同的显示界面。
在surfaceflinger内部有一个表示surface层次的类,就是LayerBase,它提供了与上层相关的通用接口,LayerBaseClient继承LayerBase,而LayerBaseClient的内部类Surface又继承Bnsurface。
这个LayBaseClient是各种层的一个基类,它被以下其它几个类继承:Layer,LayerBuffer,LayerDim,和LayerBlur。这几个类则表示了几种不同的“层”,
以上几个类中,LayerBuffer中的surfaceBuffer继承了本地的ISurface接口,也就是说,本地使用的ISurface接口在android的图形系统中只有LayerBuffer中的一个实现。
在上层的程序调用过程中,创建一个surface过程如下:
1。调用libui接口SurfaceComposerClient::createSurface();
2。调用ISurfaceFlingerClient::creatSurface();
3。由于继承关系,实际上调用 的是SurfaceFlinger中的接口,即BClient::creatSurface();
4。继续调用SurfaceFlinger::createSurface()函数,其处理过程如下:
sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
ISurfaceFlingerClient::surface_data_t* params,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
LayerBaseClient* layer = 0;
sp<LayerBaseClient::Surface> surfaceHandle;
Mutex::Autolock _l(mStateLock);
Client* const c = mClientsMap.valueFor(clientId);
if (UNLIKELY(!c)) {
LOGE("createSurface() failed, client not found (id=%d)", clientId);
return surfaceHandle;
}
//LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
int32_t id = c->generateId(pid);
if (uint32_t(id) >= NUM_LAYERS_MAX) {
LOGE("createSurface() failed, generateId = %d", id);
return surfaceHandle;
}
switch (flags & eFXSurfaceMask) {
case eFXSurfaceNormal:
if (UNLIKELY(flags & ePushBuffers)) {
layer = createPushBuffersSurfaceLocked(c, d, id, w, h, flags);
} else {
layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags);
}
break;
case eFXSurfaceBlur:
layer = createBlurSurfaceLocked(c, d, id, w, h, flags);
break;
case eFXSurfaceDim:
layer = createDimSurfaceLocked(c, d, id, w, h, flags);
break;
}
if (layer) {
setTransactionFlags(eTransactionNeeded);
surfaceHandle = layer->getSurface();
if (surfaceHandle != 0)
surfaceHandle->getSurfaceData(params);
}
return surfaceHandle;
}
创建surface时,调用 createSurface(),创建各个层后,分别调用不同层中的getSurface()接口来得到一个ISurface类型的实例,然后在SurfaceComposerClient::createSurface()中得到surface并将其返回。
实际是根据参数flags选择使用不同的层:eFXSurfaceNormal对应的层是Layer,LayerBuffer; eFXSurfaceBlur对应的层是LayerBlur; eFXSurfaceDim对应 的层是LayerDim。
如果将参数指定为普通,一般情况下会建立Layer类,当ePushBuffers为真时才会建立 LayerBuffer类。建立 Layer和LayerBuffer分别调用的是createNormalSurfaceLocked()函数和createPushBufferSurfacLocked()函数。
createNormalSurfaceLocked()先要建立一个Layer类,再向其中设置一个Buffer,然后增加层。
createPushBufferSurfacLocked()情况比较简单,只要建立 一个LayerBuffer的类将其加入层即可。
二者的区别是:LayBuffer是一个push类型的层,通常要使用队列的方式将显示的内容“推”入其中,Layer是一个普通 的层,建立时要将一个内存设置到其中 。
以设置大小 为例 ;
对一个Surface进行设置的过程如下:
1,调用libui的surface接口 setSize();
2, 实际调用 的是surfacecomposerClient::setSize(),在其中的参数surface的大小,并将其成员what设置为eSizeChanged。
3, 由于逻辑关系,由IsurfaceFlingerClient::setState()函数进行处理。
4,由于继承关系,实际调用 的是类SurfaceFlinger中的BClient::setState()。
5, 进一步调用surfaceFlinger::setClientState()函数。
在代码处理中,根据不同的状态变化命令来进行处理,最终调用的是各个“层”的setSize()函数,之后的内容由几个层的不同实现来进行处理。
Surfaceflinger和显示硬件的接口
显示设备由DisplayHardware目录中的DisplayHardware.cpp文件来实现的,其中创建了一个DisplayHardware来作为主要的显示界面。
mDisplaySurface = new EGLDisplaySurface();
EGLDisplaySurface类是在libui中实现的,它直接操作FrameBuffer的硬件驱动。在SurfaceFlinger.cpp中,将创建类DisplayHardware为实例,从而获取实际的显示设备,在上面进行显示输出。
此外,surfaceflinger可以使用可选的硬件模块copybit作为2D图形处理部分的加速器。这部分的接口在frameworks/base/include/core/java/android/hardware目录的copybit.h文件定义 的相关接口。作为硬件模块使用,这个模块在DisplayHardware初始化的过程中被打开。
Surface的java和JNI代码
Surface部分的JNI代码路径是:
frameworks/base/core/jni/android_view_Surface.cpp
它主要提供了android.view.SurfaceSession和android.view.Surface两个java类,分别调用SurfaceComposerClient和Surface两个本地类来完成实现。
Surface部分的Java代码的路径是:
frameworks/base/core/java/android/view/
由此对应的java类在android.view包中,除了上面提到的类surfaceSession和Surface之外,与其相关的还有接口SurfaceHolder和类SurfaceView。
Android.view.Surface 表示一个可以绘制图形的界面,它实际上是调用 底层的Surface接口来实现控制的硬件载体。
实际上,在java框架中,所有UI元素的基类都是android.view.View,这些UI元素本身也是基于Surface及2D绘图函数来实现的,如果要在java程序中使用一个可以进行自由绘制的界面,那么就需要使用类android.view.SurfaceView,这个类也继承了android.view.View,因此也是android中的一个UI元素,andriod.view.SurfaceHolder是android.view.SurfaceView 中包含的一个接口,用于处理Surface相关的事件。
(surface.java中定义的整数常量和本地的ISurfaceComposer.h是具有对应关系的)
Skia和2D图形系统:
skia是一个C++本地代码库,路径为external/skia/
包含3个库
Core Cg 核心图形库: libcorecg.so
GL (Skia 图形库):liblibsgl.so
skia-opengl glue library : libskiagl.so
核心库是libcorecg.so它是skia中最基础的库,其源代码主要在src/core/目录中,提供一系列基本功能,
Skia 图形库:liblibsgl.so是skia系统主要的库,它包含移植层,图形绘制,图像编解码,效果等方面内容。其源代码主要在src/effects , src/images/ , src/ports/, src/core/ , src/utils/ 目录 中。liblibsgl.so需要连接libcorecg.so,以及被其调用 的图像的编解码库,字体处理的库等。
libskiagl.so 是skia和OpenGL 相关联的库,其源代码在src/gl目录中。
skia对上层有众多的接口,接口头文件在include目录 中,skia 的API中最主要的SKCavans类,这个类提供了众多的绘制功能。事实上,整个android的GUI系统的底层绘制,就是这个类来完成的。其头文件 和源代码路径分别是:include/core/SkCavans.h 和src/core/SkCavans.cpp
SkCanvas 类有两个构造函数,其参数类型分别是SkBitmap和SkDevice,分别表示Skia进行绘制的目标。事实上,Skia的基本功能 就是一个绘制工具,这个绘制工具和绘制的目标是无关的。SkBitmap 可以视为一个表示位图区域的内存,除了一般的内存首指针和大小 之外,还包括宽,高和像素格式等信息,在这块内存上,可以进行skia图形系统的绘制工作。
SkCanvas的主要绘制功能有3种: 基本图形绘制 (如drawARGB , drawLine 函数)图像文件绘制(如drawBitmap函数)和文本绘制(如drawText函数).
Skia的图像编码部分:
skia的图像编码部分是一个相对独立 的部分,其接口分别在include/image目录下的SkImageDecoder.h 和skImageEncoder.h中定义:
SkImageDecoder既可以作为动态类使用,又可以使用静态函数,也支持同步和异步方式解码,它可以把图像文件或者流解码到skia的内部内存SkBitmap中。
SkImageEncoder和解码器类似,完成 的是编码工作,
skia的解码器和编码器都是接口,需要具体的类去实现,在src/images中的几个源文件通过继承SkImageDecoder和SkImageEncoder来实现解码器和编码器,如 SkImageDecoder_libjpeg.cpp 通过调用libjpeg 库实现了JPEG的解码。
如有其它 的解码器和编码器,也可以通过 SkImageEncoder和SkImageDecoder的类来实现。
Android图形系统的JNI接口
android图形系统和skia底层库联系比较紧密,android图形系统的JNI提供 了从skia底层到java层的支持,JNI代码路径为:frameworks/base/core/jni/android/grphic/
Canvas.cpp 是JNI中的核心接口,为java上层的android.graphics.Canvas类提供了支持,其中 ,initRaster()和initGL()两个函数将和Skia本地库联系起来。即通过建立skia本地库的Cavvas来建立给java层使用的Canvas,除此外,图形JNI还通过JNI接口,提供andriod.graphics.Region , android.graphics.Bitmap, android.graphics.Picture , android.graphics.Matrix 等多个java类的支持,这一般调用的是skia中同名文件中的函数。
Android的图形包:
android图形类的包是android.graphics它通过调用图形系统的JNI提供了对java框架中的图形系统的支持,代码路径为:frameworks/graphic/java/android/graphics/
Canvas.java定义了android图形系统中最为重要的一个类:android.graphics.Canvas. Canvas类处理“draw“的调用,当绘制(draw)内容时需要4个基本组件,一个保持像素的Bitmap,一个处理绘制调用 的canvas(写入Bitmap),绘制 的内容(如,Rect,Path,text,Bitmap)和一个paint(用开描述颜色和样式)。Bitmap.java文件实现了类android.graphics.Bitmap, 它表示内存中的一个位图。
Canvas是一个比较基础的图形类,android中的UI 元素也是通过调用Canvas类来构建的。在View.java文件 中,实现的类是android.view.View, 通过建立这个Canvas类来构建绘画的基础。
Android系统的OpenGL系统与3D图形系统
分本地代码和java框架代码两部分。
本地代码实现OpenGL接口的库,java框架层,javax.microedition.khronos.opengles是java标准的OpenGL包。
本地整体结构:
主要内容在frameworks/base/opengl/中,本地代码头文件 路径为:
frameworks/base/opengl/include/EGL/
frameworks/base/opengl/include/GLES/
源代码目录:
frameworks/base/opengl/libagl/
frameworks/base/opengl/libs/
编绎后生成3个库:
libGLESv1_CM.so: OpenGL Es库的封装,对应libs/GLES_CM目录 中的文件
libEGL.so:EGL库,OpenGL Es库的封装,对应libs/EGL目录 中的文件
libagl.so: OpenGL的软件实现库,对应libagl目录中的文件。
Android的OpenGL实现方式:
OpenGL本地库可使用软件库和硬件库这两种不同的 方式来实现。如果是软件实现,则用libagl.so库,如果是硬件实现,则使用libhgl.so库。
在libs/EGL/egl.cpp文件中,选择使用这两个库中的一个,主要操作在eglGetDisplay()函数中实现。
load_driver()函数实际上将通过dlopen()方式打开库,默认先去找OpenGL的软件实现libagl.so 当设置使用OpenGL的硬件实现(在init.rc中设置属性debug.egl.hw)时,则使用OpenGL的硬件实现libagl.so.然后用dlsysm方式打开其中支持API的符号,
gl_hooks_t 结构描述了OpenGL系统所支持的各种API。实际的符号将在gl_entries.in和egl_entries.in两个文件中定义。各种函数以GL_ENTRY和EGL_ENTRY的方式来进行描述。
Android的openGL的本地测试代码:
路径为:frameworks/base/opengl/tests 包含几个独立 的测试程序,
openGL的JNI代码:
openGL向上提供的JNI接口,主要由以下两个文件提供:
frameworks/base/core/jni/com_google_android_gles_jni_GLImpl.cpp
frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp
这是包com.google.android.gles_jni中的两个类,分别是EGLImpl和GLImpl,分别对应egl和gl的实现。
其中,EGLImpl中的各个接口负责一些管理功能,而GLImpl中的各个接口对应于OpenGL的GLES/gl.h头文件中定义的各个OpenGL功能函数。
OpenGL的java类
在andriod中使用openGL常要结合使用javax.microedition.khronos.opengles 和android.opengl包。
实现方法是使用一个类来继承OpenGL java的标准类,通过实现这个类来实现,在java层只要使用标准类。
OpenGL 的java类是javax中的一部分,这个包是javax.microedition.khronos.egl 和javax.microedition.khronos.opengls路径为:
opengl/java/javax/microedition/khronos/egl/
opengl/java/javax/microedition/khronos/opengles/
在egl类中主要的文件是GL10.java 和GL11.java ;在opengles中主要的文件 是EGL10.java;
android 使用继承的方法实现opengl这个继承的包为com.google.android.gles_jni 其路径为:
opengl/java/com/google/andriod/gles_jni/
事实上,EGLImpl和GLImpl的大部分函数是通过JNI调用本地的OpenGL程序来实现的。
在java应用层不会调com.google.android.gles_jni包中的类,只会调用标准包avax.microedition.khronos.opengles中的接口。
另个,android.opengl包提供了openGL的标准接口到android系统的媒介,路径为:
opengl/java/android/opengl/
其中主要的类调用com.google.android.gles_jni包中的类和android的基础GUI系统的类实现GLSurfaceView
GLSurfaceView继承了SurfaceView。 surfaceView又继承View,因此GLSurfaceView本身也是个UI元素,在android的java应用程序层使用OpenGL,具体的实现其实是继承GLSurfaceView类并调用OpenGL标准的接口。