摘自:柯元旦《Android内核剖析》
Android中的GUI系统是客户端和服务端配合的窗口系统,即后台运行了一个绘制服务,每个应用程序都是该服务端的一个客户端,当客户端需要绘制时,首先请求服务端创建一个窗口,然后在窗口中进行具体的视图内容绘制;对于每个客户端而言,他们都感觉自己独占了屏幕,而对于服务端而言,它会给每一个客户端窗口分配不同的层值,并根据用户的交互情况动态改变窗口的层值,这就给用户造成了所谓的前台窗口和后台窗口的概念。
![](https://img-blog.csdn.net/20140910111208181)
SurfaceFlinger进程:简称sf,该进程在系统开机时自动启动,在init.rc中定义,他的作用就是给每个客户端分配窗口,在程序中这个窗口用Surface类来表示,即每个窗口都是一个平面,而每个平面在程序中都会对应一段内存,即一块屏幕缓冲区,大小一般为宽度*高度;
sf服务进程的启动命令源码位于./frameworks/native/cmds/surfaceflinger/;
sf服务的具体源码位于./frameworks/native/services/surfaceflinger/;
当一个apk程序要创建窗口时,步骤是这样子的:
在本地创建一个Surface对象--->>然后调用WindowManager类向WmS服务发出一个请求,并把Surface对象作为参数传递过去-->>WmS收到请求后,通过Surface类的JNI调用SurfaceFlinger_client驱动--->>client驱动请求sf进程创建指定的窗口--->> sf会创建一段屏幕缓冲区,并把这个窗口缓冲区地址传递给Wms--->>Wms用这个地址去初始化apk程序传入的Surface对象-->>回到APK程序。
Surface对象仅仅是一个空壳,它表示一个窗口,但必须经过初始化后才能真正在屏幕上显示为一个窗口,这个初始化就是给Surface对象分配一段屏幕缓冲区内存。
APK程序有了这个Surface后,就可以在这个平面上绘制想要的内容了。Surface类仅仅表示一个平面,而绘制不同图案是一种操作,Android中使用一个叫做Skia的绘图驱动库,来进行各种平面绘制。在程序中用Canvas来表示这个功能对象,Surface类包含一个函数lockCanvas(),它返回一个Canvas对象,调用该对象的各种绘图函数就可以完成对平面的绘制。
1. Surface类:表示一个平面,内部仅包含平面的大小、位置,及一段屏幕缓冲内存区。不过在Java端,不能直接访问这段内存,也不能通过该类直接设置该平面的大小和位置,只能通过SurfaceHolder类。
2. Canvas类:一个包含各种绘制函数的功能类。必须为Canvas指定一段内存地址,因为绘制的结果实际上就是给这段内存地址中填充不同的像素值。
这段内存有两种类型:一种是普通内存,不会在屏幕上看到,要将这段内存复制到拥有屏幕缓冲内存的Canvas中,游戏开发中常用的方式;另一种是屏幕缓冲区内存,就是绘制函数执行后直接可以在屏幕上看到。
3. Drawable类:一个抽象类,也是一个功能类。它和Canvas的相同之处是两者都可给内存缓冲区中绘制图案。
不同之处是:
1> Drawable内部没有一段内存缓冲区,当应用程序要绘制图案时,要将一个包含内存缓冲区的Canvas对象传递给Drawable,然后Drawable才能在Canvas上绘制图案。
2> 每个具体的Drawable对象仅绘制某个特定的图案,比如SDK中包括了BitmapDrawable、ColorDrawable、NinePatchDrawable等.打个比方,Drawable是来料加工,而Canvas是综合加工厂。
4. Paint:画笔,用来保存图案绘制时所用的颜色、样式(是否有阴影、粗细、圆角、字体、对齐方式等),一般在canvas.drawXXX中作为参数进行使用;
下面介绍一些Canvas的常用方法:
Android中的GUI系统是客户端和服务端配合的窗口系统,即后台运行了一个绘制服务,每个应用程序都是该服务端的一个客户端,当客户端需要绘制时,首先请求服务端创建一个窗口,然后在窗口中进行具体的视图内容绘制;对于每个客户端而言,他们都感觉自己独占了屏幕,而对于服务端而言,它会给每一个客户端窗口分配不同的层值,并根据用户的交互情况动态改变窗口的层值,这就给用户造成了所谓的前台窗口和后台窗口的概念。
绘制屏幕的软件架构
Android的屏幕绘制架构如下图:SurfaceFlinger进程:简称sf,该进程在系统开机时自动启动,在init.rc中定义,他的作用就是给每个客户端分配窗口,在程序中这个窗口用Surface类来表示,即每个窗口都是一个平面,而每个平面在程序中都会对应一段内存,即一块屏幕缓冲区,大小一般为宽度*高度;
sf服务进程的启动命令源码位于./frameworks/native/cmds/surfaceflinger/;
sf服务的具体源码位于./frameworks/native/services/surfaceflinger/;
当一个apk程序要创建窗口时,步骤是这样子的:
在本地创建一个Surface对象--->>然后调用WindowManager类向WmS服务发出一个请求,并把Surface对象作为参数传递过去-->>WmS收到请求后,通过Surface类的JNI调用SurfaceFlinger_client驱动--->>client驱动请求sf进程创建指定的窗口--->> sf会创建一段屏幕缓冲区,并把这个窗口缓冲区地址传递给Wms--->>Wms用这个地址去初始化apk程序传入的Surface对象-->>回到APK程序。
Surface对象仅仅是一个空壳,它表示一个窗口,但必须经过初始化后才能真正在屏幕上显示为一个窗口,这个初始化就是给Surface对象分配一段屏幕缓冲区内存。
APK程序有了这个Surface后,就可以在这个平面上绘制想要的内容了。Surface类仅仅表示一个平面,而绘制不同图案是一种操作,Android中使用一个叫做Skia的绘图驱动库,来进行各种平面绘制。在程序中用Canvas来表示这个功能对象,Surface类包含一个函数lockCanvas(),它返回一个Canvas对象,调用该对象的各种绘图函数就可以完成对平面的绘制。
Java客户端绘制相关类的关系
Java客户端中,和绘制相关的类包括以下几种:1. Surface类:表示一个平面,内部仅包含平面的大小、位置,及一段屏幕缓冲内存区。不过在Java端,不能直接访问这段内存,也不能通过该类直接设置该平面的大小和位置,只能通过SurfaceHolder类。
2. Canvas类:一个包含各种绘制函数的功能类。必须为Canvas指定一段内存地址,因为绘制的结果实际上就是给这段内存地址中填充不同的像素值。
这段内存有两种类型:一种是普通内存,不会在屏幕上看到,要将这段内存复制到拥有屏幕缓冲内存的Canvas中,游戏开发中常用的方式;另一种是屏幕缓冲区内存,就是绘制函数执行后直接可以在屏幕上看到。
3. Drawable类:一个抽象类,也是一个功能类。它和Canvas的相同之处是两者都可给内存缓冲区中绘制图案。
不同之处是:
1> Drawable内部没有一段内存缓冲区,当应用程序要绘制图案时,要将一个包含内存缓冲区的Canvas对象传递给Drawable,然后Drawable才能在Canvas上绘制图案。
2> 每个具体的Drawable对象仅绘制某个特定的图案,比如SDK中包括了BitmapDrawable、ColorDrawable、NinePatchDrawable等.打个比方,Drawable是来料加工,而Canvas是综合加工厂。
4. Paint:画笔,用来保存图案绘制时所用的颜色、样式(是否有阴影、粗细、圆角、字体、对齐方式等),一般在canvas.drawXXX中作为参数进行使用;
下面介绍一些Canvas的常用方法:
final static int GRAY=0x0ffa0a0a0;
final static int DARK=0x0ff000D00;
private void onDraw2(Canvas canvas){
canvas.drawColor(0x0ffffffff);
int h=canvas.getHeight();
int w=canvas.getWidth();
if(mPaint==null){
mPaint=new Paint();
}
int textSize=(int)mPaint.getTextSize();
Rect rt=canvas.getClipBounds();
String str1=rt.toShortString();
canvas.drawText(str1,0,0+textSize,mPaint);/*坐标原点相对于canvas经父窗口裁剪后的左上角*/
canvas.drawText("h="+h+",w="+w,0,100,mPaint);
canvas.translate(100,50);/*调整坐标原点到指定位置*/
canvas.drawCircle(0,0,2,mPaint);
int saveCount=canvas.save();
canvas.clipRect(20,0,40,20);
canvas.drawColor(GRAY);
canvas.restoreToCount(saveCount);
Matrix m=new Matrix();/*Matrix作用是改变绘制图案的形状参数,包括旋转、平移、缩放、阿尔法*/
m.setScale(2,4);
canvas.concat(m);
canvas.clipRect(20,0,40,20);
canvas.drawColor(DARK);
}
在Java端操作Surface,可以想看这篇文章:SurfaceView的使用方法