void framebuffer_service(int fd)
{
struct fb_var_screeninfo vinfo;
int fb, offset;
char x[256];
struct fbinfo fbinfo;
unsigned i, bytespp;
fb = open("/dev/graphics/fb0", O_RDONLY);
if(fb < 0) goto done;
if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done;
fcntl(fb, F_SETFD, FD_CLOEXEC);
bytespp = vinfo.bits_per_pixel / 8;
fbinfo.version = DDMS_RAWIMAGE_VERSION;
fbinfo.bpp = vinfo.bits_per_pixel;
fbinfo.size = vinfo.xres * vinfo.yres * bytespp;
fbinfo.width = vinfo.xres;
fbinfo.height = vinfo.yres;
fbinfo.red_offset = vinfo.red.offset;
fbinfo.red_length = vinfo.red.length;
fbinfo.green_offset = vinfo.green.offset;
fbinfo.green_length = vinfo.green.length;
fbinfo.blue_offset = vinfo.blue.offset;
fbinfo.blue_length = vinfo.blue.length;
fbinfo.alpha_offset = vinfo.transp.offset;
fbinfo.alpha_length = vinfo.transp.length;
/* HACK: for several of our 3d cores a specific alignment
* is required so the start of the fb may not be an integer number of lines
* from the base. As a result we are storing the additional offset in
* xoffset. This is not the correct usage for xoffset, it should be added
* to each line, not just once at the beginning */
offset = vinfo.xoffset * bytespp;
offset += vinfo.xres * vinfo.yoffset * bytespp;
printf("offset %d/n", offset);
if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;
lseek(fb, offset, SEEK_SET);
for(i = 0; i < fbinfo.size; i += 256) {
if(readx(fb, &x, 256)) goto done;
if(writex(fd, &x, 256)) goto done;
}
if(readx(fb, &x, fbinfo.size % 256)) goto done;
if(writex(fd, &x, fbinfo.size % 256)) goto done;
done:
if(fb >= 0) close(fb);
close(fd);
}
简单介绍:
截屏一般有三种方法:
1.
2.
3.
根据各种方法的特点,我们选择的是framebuffer的方式,View的方式也简单介绍一下。由于截屏需要一定的处理时间,而且需要在后台运行,用service来实现是比较好的选择。
View截屏的简单实现
使用view截屏的话首先要获取当前的界面的view
在Activity中获取view是非常容易的,但是仅限于当前Activity的view,这种方法截屏是有限制的,截取的图像没有状态栏。在Activity中截屏的代码如下:
取得bitmap:
View view = v.getRootView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
然后将bitmap保存为png格式:
framebuffer截屏的实现
framebuffer是linux内核对显示的最底层驱动。在一般的linux文件系统中,通过/dev/fb0设备文件来提供给应用程序对framebuffer进行读写的访问。这里,如果有多个显示设备,就将依次出现fb1,fb2,…等文件。而在我们所说的android系统中,这个设备文件被放在了/dev/graphics/fb0,而且往往只有这一个。
这种方法使用的最为普遍,linux系统中经常使用这种方法实现截屏,一般步骤是:
1.
2.
3.
读取framebuffer
在android上使用这种方法第一个难题是获取framebuffer,因为默认的配置中framebuffer的读取权限是“root”,而Apk的权限最高只能提升到“system”,framework工作的权限也是“system”,也因此网上提供的截屏软件都只能在root过的手机上使用。对拥有源码的人来说,最简单的方法是直接改变framebuffer的权限,普通用户也有权限读取framebuffer。
然后就可以通过读文件的方式直接读取framebuffer,利用如下代码打开一个指向framebuffer的输入流就可以读取了。
new
}//get the InputStream from framebuffer
framebuffer到bitmap的转换
framebuffer的数据是直接送入显示设备的,这些数据没有文件头,而且由于framebuffer读到数据跟显示方式关系很大,在不同设备上framebuffer的大小和数据格式不一样,读取前确定framebuffer的数据格式。
pixelformat代表的意义是在PixelFormat.java定义的:
pixelformat是下面进行判断处理的依据,根据pixelformat计算出实际的深度。
一般
之所以“*2”,是因为Framebuffer包含两帧画面,我们使用任何一帧都可以。
piex =
InputStream stream =
new
piex
bitmap保存为png格式:
这是跟上面一样的
这样截取的一幅图片就被保存到了固定位置。
快捷键的添加
public int interceptKeyBeforeQueuei
long whenNanos, int action, int flags,
int keyCode, int scanCode, int policyFlags,
boolean isScreenOn)
这个函数是在按键事件进入消息队列之前调用的,所以先与所有app相应事件。但是在这个函数对于每个按键事件是单独处理的,组合键需要我们自己来识别。
下图分别是power键和volumedown键的按键处理,黄色区域为新增加代码。由于现在power键的长按和短按都是有事件响应的,所以设计的关键在于必须在长按和短按的间隙处理截屏事件,而且截屏后保证power的长按不在响应。
service设计:
这个服务的功能比较单一,只在启动时响应请求就可以,不需要驻留在后台,也不需要调用接口,所以不需要AIDL,也不需要远程连接。
客户端,使用intent启动服务
Intent intent = new Intent();
intent.setAction("com.android.ScreenServer.SHOT");
mContext.startService(intent);
在应用程序端
<uses-permission
</application>
程序代码:
}
这样截屏结束后,Service的生命周期就结束了,系统会自动销毁Service。