Android5.0之前如果希望截图屏幕需要系统权限root,在5.0之后开发了接口android.media.projection,使用该接口,第三方程序无需root权限也可以直接进行屏幕截图操作了。
由于使用了媒体的映射技术手段,故截取的屏幕并不是真正的设备屏幕,而是截取的通过映射出来的“虚拟屏幕”。不过,因为截图我们希望得到的只是一张图片而已,而“映射”出来的图片与系统屏幕完全一致,所以,对于普通屏幕截屏操作,该方法是可行的。
MediaProjection使用步骤:
首先获取MediaProjectionManager,和其他的Manager一样通过 Context.getSystemService() 传入参数MEDIA_PROJECTION_SERVICE获得
接着调用MediaProjectionManager.createScreenCaptureIntent(),调用后会弹出一个dialog询问用户是否授权应用捕捉屏幕
然后在onActivityResult()中获取授权结果
如果授权成功,通过MediaProjectionManager.getMediaProjection(int resultCode, Intent resultData)获取MediaProjection实例,通过MediaProjection.createVirtualDisplay(String name, int width, int height, int dpi, int flags, Surface surface, VirtualDisplay.Callback callback, Handler handler)创建VirtualDisplay实例。实际上在上述方法中传入的surface参数是真正用来截屏或者录屏的。
方法代码
private static MediaProjectionManager mediaProjectionManager;
private static MediaProjection mediaProjection;
private static ImageReader imageReader;
private static VirtualDisplay mVirtualDisplay = null;
public static void startScreenCap(Activity context,int screenWidth,int screenHeight) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return;
}
if (mediaProjectionManager == null)
mediaProjectionManager = (MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if(imageReader==null)
imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 1);
context.startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), ConstantUtils.SCREEN_CAPTURE);
}
//获取MediaProjection实例
public static void setUpMediaProjection(Activity context, Intent data, int screenWidth, int screenHeight,int screenDensity) {
if(mediaProjection == null)
mediaProjection = mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, data);
}
//获取当前屏幕的内容,mImageReader.getSurface()被传入,屏幕的数据也将会在ImageReader中的Surface中
public static void virtualDisplay(int screenWidth, int screenHeight, int mScreenDensity) {
try {
mVirtualDisplay = mediaProjection.createVirtualDisplay("screen-mirror", screenWidth, screenHeight, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader.getSurface(), null, null);
} catch (Exception e) {
e.printStackTrace();
}
}
//生成图像
public static Bitmap createPicture(int screenWidth,int screenHeight) {
final Image image = imageReader.acquireLatestImage();
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
final 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;
}
public static void stopVirtual() {
if (mVirtualDisplay == null) {
return;
}
mVirtualDisplay.release();
mVirtualDisplay = null;
}
通过调用方法
startScreenCap(activity,ScreenUtils.getScreenWidth(activity),ScreenUtils.getScreenHeight(activity));初始化并授权
在Activity的onResult()方法中调用
public static void capOnActivityResult(final Activity context, int requestCode, int resultCode, final Intent data, final int screenWidth, final int screenHeight, final int screenDensity){ if(requestCode == ConstantUtils.SCREEN_CAPTURE && resultCode==Activity.RESULT_OK && data != null){ singleExecutor.execute(new Runnable() { @Override public void run() { ScreenCaptureUtils.setUpMediaProjection(context,data,screenWidth,screenHeight,screenDensity); ScreenCaptureUtils.virtualDisplay(screenWidth,screenHeight,screenDensity); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } BitmapUtils.saveImageToGallery(context, ScreenCaptureUtils.createPicture(screenWidth,screenHeight)); ScreenCaptureUtils.stopVirtual(); } }); } }
原文地址:
关于Android5.0以上屏幕截图探索总结:
https://blog.csdn.net/gulinxieying/article/details/50164469
Android截屏的几种方式:
https://www.2cto.com/kf/201602/488933.html
5.0之前屏幕截屏:
https://www.cnblogs.com/baiqiantao/p/5b9d046a774000e292d1ab22a07a20b1.html
Android5.0屏幕截屏与录制:
https://blog.csdn.net/zhang_jun_ling/article/details/52558243
Android5.0之前屏幕截图的方法:
https://www.cnblogs.com/baiqiantao/p/5b9d046a774000e292d1ab22a07a20b1.html
Android5.0及以上实现屏幕截屏:
https://www.jianshu.com/p/6c2e896b71e0
2、遇到的问题:
Process: com.example.applications, PID: 12529
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.media.Image.getWidth()' on a null object reference
可能的原因及解决办法:
①mediaProjection.createVirtualDisplay与imageReader.acquireLatestImage();之间需要间隔最少1s的时间
②根据源码可知resultCode必须为Activity.RESULT_OK ,intentData不能为null
public MediaProjection getMediaProjection(int resultCode, @NonNull Intent resultData) {
if (resultCode != Activity.RESULT_OK || resultData == null) {
return null;
}
IBinder projection = resultData.getIBinderExtra(EXTRA_MEDIA_PROJECTION);
if (projection == null) {
return null;
}
return new MediaProjection(mContext, IMediaProjection.Stub.asInterface(projection));
}
③
imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 1);imagereader必须在方法createVirtualDisplay方法之前调用
mediaProjection.createVirtualDisplay("screen-mirror", screenWidth, screenHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, imageReader.getSurface(), null, null);
④所需要的参数screenWidth、screenHeight、screenDensity的值不正确