「Android」后台截屏的初步实现

「Android」后台截屏的初步实现 需要明确,在Android中,如果只是想得到正在运行的应用程序自身的界面,是非常简单的,只需要基于View对象即可完成截屏特性,具体此处不再赘述。但如果是想从后台服务中实时的截屏,获取除了应用程序自身以外的其他界面,则需要依赖于MediaProjectionManager类。
以下为后台截屏的简略步骤和对应代码:

  • 新建透明Activity
  • 启动截屏Service
新建透明Activity 首先,新建一个透明Activity(新建透明Activity的主要原因是,startActivityForResult()方法为Activity类的对象方法,必须得有一个Activity作为“母体”),并在在这个Activity中调用requestCaptureScreen()方法,在该方法中初始化了若干截屏相关数值并通过createScreenCaptureIntent())方法向用户发起截屏请求:
private void requestCaptureScreen() { Log.d(TAG, "requestCaptureScreen: "); Display display = this.getWindowManager().getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); mWidth = metrics.widthPixels; mHeight = metrics.heightPixels; mDpi = metrics.densityDpi; mMediaProjectionManager = (MediaProjectionManager) this.getSystemService(Context.MEDIA_PROJECTION_SERVICE); if (mMediaProjectionManager != null) { // 关键代码 this.startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE); } }

在调用startActivityForResult()方法后,需要重写onActivityResult()方法,在该方法中当确认获取到用户授权后,启动截屏Service:
@SuppressLint("WrongConstant") @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { Log.d(TAG, "onActivityResult: InvisibleActivity"); if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) { Intent service = new Intent(this, ScreenShotService.class); service.putExtra("resultCode", resultCode); service.putExtra("data", data); service.putExtra("mWidth", mWidth); service.putExtra("mHeight", mHeight); service.putExtra("mDpi", mDpi); startForegroundService(service); } }

启动截屏Service 【「Android」后台截屏的初步实现】在截屏Service启动后,分别获取到MediaProjectionManager对象和MediaProjection对象,并调用captureScreen()方法开始截屏并获取截屏文件的路径:
@Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand: ScreenShotService"); createNotificationChannel(); int resultCode = intent.getIntExtra("resultCode", 0); Intent data = https://www.it610.com/article/intent.getParcelableExtra("data"); int width = intent.getIntExtra("mWidth", 0); int height = intent.getIntExtra("mHeight", 0); int dpi = intent.getIntExtra("mDpi", 0); MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); MediaProjection projection = mediaProjectionManager.getMediaProjection(resultCode, data); // 获取截屏文件的路径 String filePath = captureScreen(projection, width, height, dpi, this); return super.onStartCommand(intent, flags, startId); }

/** * 获取截屏 * * @param projection projection * @param width width * @param height height * @param dpi dpi * @param context context * @return 返回截屏文件路径 */ public static String captureScreen(MediaProjection projection, int width, int height, int dpi, Context context) { Log.d(TAG, "screenShot: "); Bitmap bitmap = null; // 获取ImageReader实例 @SuppressLint("WrongConstant") ImageReader imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2); // 获取虚拟显示器VirtualDisplay的实例 VirtualDisplay display = projection.createVirtualDisplay("ScreenShot", width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, imageReader.getSurface(), null, null); try { SystemClock.sleep(STOP_TIME); Image image = imageReader.acquireLatestImage(); 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.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(buffer); image.close(); } catch (Exception e) { e.printStackTrace(); } finally { if (imageReader != null) { imageReader.close(); } if (projection != null) { projection.stop(); } if (display != null) { display.release(); } } String outPath = ""; try { outPath = saveImage(bitmap, context); } catch (IOException e) { e.printStackTrace(); } return outPath; }

    推荐阅读