屏幕捕捉
Android5.0之后开放了屏幕捕捉的API,因此开发者便可以直接通过代码进行截图与录屏,而无需操作系统底层了。屏幕捕捉的功能由MediaProjectionManager媒体投影管理器实现,该管理器的对象从系统服务MEDIA_PROJECTION_SERVICE中获得。注意MediaProjectionManager是Android5.0之后新增的工具,故代码中要补充判断系统版本,如果是4.*及以下版本,则不可处理屏幕捕捉操作。具体的屏幕捕捉,还要调用媒体投影管理器对象的getMediaProjection方法,获取MediaProjection媒体投影对象。MediaProjection主要有两个方法,说明如下:
createVirtualDisplay : 创建虚拟显示层。可分别指定显示层的名称、宽度、高度、密度、标志、渲染表面等等。其中标志通常取值DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,渲染表面则按照截图和录屏两种方式分别取值。
stop : 停止投影。
屏幕捕捉的用途主要是截图和录屏,这有点像摄像头的功能,截图对应拍照,而录屏对应录像。对于拍照和录像,我们知道需要创建一个SurfaceView表面视图做为画面预览层,那么就屏幕捕捉而言,也需要创建一个虚拟显示对象做为投影预览层。这个投影预览层即前面createVirtualDisplay方法返回的VirtualDisplay对象,具体的表面对象则为createVirtualDisplay方法中的渲染表面参数,也就是一个Surface对象。如果当前为截图操作,那么调用ImageReader对象的getSurface方法获得渲染表面;如果当前为录屏操作,那么调用MediaCodec对象的createInputSurface方法获得渲染表面。
截图
给屏幕截图用到了ImageReader,它的常用方法说明如下:newInstance : 静态函数,构造一个图像读取器,可指定图像的宽度、高度、色彩模式,以及图像数量。
getSurface : 获取图像的渲染表面。在实现截图功能时,这里的表面对象要作为createVirtualDisplay方法的输入参数。
acquireLatestImage : 获得最近的一幅图像数据。该方法返回Image对象,需转换为Bitmap格式。
下面是把Image对象转换为Bitmap格式的示例代码:
public static Bitmap getBitmap(Image image) {
int width = image.getWidth();
int height = image.getHeight();
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride,
height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
image.close();
return bitmap;
}
截图服务的主要逻辑代码如下所示:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class CaptureService extends Service implements FloatClickListener {
private static final String TAG = "CaptureService";
private MediaProjectionManager mMpMgr;
private MediaProjection mMP;
private ImageReader mImageReader;
private String mImagePath, mImageName;
private int mScreenWidth, mScreenHeight, mScreenDensity;
private VirtualDisplay mVirtualDisplay;
private FloatView mFloatView;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mImagePath = Environment.getExternalStor