1

「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

在截屏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 = 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;
    }

山庄的铁匠
15 声望11 粉丝