- 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。