对截屏的速度要求比较高,所以放弃了传统的截图到SD卡再访问的策略。那使用什么方式去读取呢,网上各种搜索发现可以读取android的图像缓冲区(/dev/graphics/fb0),但是这个文件是root权限的,只能在root权限的机器上访问了,这也是该方法的缺陷之一。
1.
首先让我们来说说Android的屏幕是怎么显示出来的。 众所周知,Android也是linux派生出来的,因此Android的显示机制用的是Linux一样的机制:Framebuffer
FrameBuffer是一种机制。他提共接口将显示设备抽象为帧缓冲区。用户可以将它看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直 接进行读写操作,这种机制是把屏幕上的每个点映射成一段线性内存空间,程序可以简单的改变这段内存的值来改变屏幕上某一点的颜色。例如如果你想把一张 bitmap图片显示到屏幕上去,你只要解析bitmap之后把数据bit copy进framebuffer,屏幕就会立刻显示出来。一般Linux的framebuffer 对应/dev/fb0这个字符设备文件。 Android稍微改了改,放在/dev/graphics/fb0下。
2.
知道了Android的显示机制,和android framebuffer的文件位置,那么截屏就可以变得非常高效率了:将framebuffer里的显示区读出来就行了。但是读出来还是有很多细节的, 你知道这里面存了多少帧? 你知道图像以什么格式存在里面的?
3.
关于这个问题,Android不同机型还真不同,我在网上找到过一篇文章说的是G1机型的,G1的framebuffer一次存2帧, 帧的格式是RGB565,也就是说一个点的颜色由Red Blue Green 一共16个bit, 2 bytes形成。而对于N1, 经过摸索, 实际上framebuffer里存了3帧, 颜色格式是BGR32, 一共4bytes, RGBA各8bit顺序排列,注意不是ARGB, A:透明度。
关于如何访问fb0创建可显示的bitmap可以参考这篇博文 ,但是我发现作者的代码在我的机器上(htc g7,miui 2.3.7系统,屏幕宽度480,高度800,位深32bit,4字节)运行截屏出来的图像颜色显示不对,红的显示成蓝的,蓝的显示成红的。根据之前描述,fb0读出来的数据(以4字节为单位)是按照RGBA各8位的顺序排列的,而原作者是按照BGRA的顺序处理的,修改后的转换代码如下:
// 将rgb转为色值
for (int m = 0; m < colors.length; m++) {
int r = (piex[m * 4] & 0xFF);
int g = (piex[m * 4 + 1] & 0xFF);
int b = (piex[m * 4 + 2] & 0xFF);
int a = (piex[m * 4 + 3] & 0xFF);
colors[m] = (a << 24) + (r << 16) + (g << 8) + b;
}
// 将rgb转为色值
for (int m = 0; m < colors.length; m++) {
int r = (piex[m * 4] & 0xFF);
int g = (piex[m * 4 + 1] & 0xFF);
int b = (piex[m * 4 + 2] & 0xFF);
int a = (piex[m * 4 + 3] & 0xFF);
colors[m] = (a << 24) + (r << 16) + (g << 8) + b;
}
这回显示就正常了。