Android three ways to capture any interface screen

1. Use MediaProjectionManager

After Android 5.0, the API for capturing the screen is opened, that is, using MediaProjectionManager to create VirtualDisplay, passing in the Surface associated with ImageReader, so that you can get Image from ImageReader, and then copy the pixel array of Image to Bitmap, if you want to save it as For pictures, use the obtained Bitmap to compress pictures in JPEG format.

The first is to use startActivityForResult to initiate a screen recording request:

    private void startScreenShot(){
        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        if (windowManager != null) {
            DisplayMetrics displayMetrics = new DisplayMetrics();
            windowManager.getDefaultDisplay().getMetrics(displayMetrics);
            width = displayMetrics.widthPixels;
            height = displayMetrics.heightPixels;
            dpi = displayMetrics.densityDpi;
        }

        mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        if (mediaProjectionManager != null) {
            startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 123);
        }
    }

After getting the user's authorization to record the screen, get MediaProjection-->VirtualDisplay-->ImagReader-->Image-->Bitmap in onActivityResult:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        MediaProjection mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
        if (mediaProjection != null){
            getBitmap(mediaProjection);
        }
    }
private void getBitmap(MediaProjection mediaProjection){
        ImageReader imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 3);
        mediaProjection.createVirtualDisplay("screen_shot",
                width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                imageReader.getSurface(), null, null);
        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireNextImage();
                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);
                String filePath = Environment.getExternalStorageDirectory().getPath() + "/hello.jpg";
                //bitmap保存为图片
                saveBitmap(bitmap, filePath);
                image.close();
            }
        }, null);

    }
private void saveBitmap(Bitmap bitmap, String filePath){
        try {
            FileOutputStream outputStream = new FileOutputStream(filePath);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Advantages: no system signature, no dependence on the underlying API of the system;

Disadvantages: A confirmation box pops up, requiring user authorization to record the screen;

 

2. Use SurfaceControl

From the above analysis, it can be seen that MediaProjectionManager relies on Surface for screen recording. After analyzing the Surface source code, it is found that Surface actually calls SurfaceControl, which means that SurfaceControl can be used to take shortcuts to take screenshots. In fact, the screenshot of the system framework layer is also called SurfaceControl. However, since SurfaceControl belongs to the system API and is not open to users, we cannot call it directly. At this point, everyone should think of using the reflection mechanism to call.

The correct way to call is like this:

Bitmap bitmap = SurfaceControl.screenshot(width, height);

Through the system source code, you can know that it is under the path of Android.view.SurfaceControl. Now that the paths, classes, methods, and parameters are known, reflection is fine:

	//使用反射调用截屏
	private void screenShotByReflect(){
		DisplayMetrics mDisplayMetrics = new DisplayMetrics();
		float[] dims = { mDisplayMetrics.widthPixels,
				mDisplayMetrics.heightPixels };
		try {
			Class<?> demo = Class.forName("android.view.SurfaceControl");
			Method method = demo.getDeclaredMethod("screenshot", int.class,int.class);
			mScreenBitmap = (Bitmap) method.invoke(null,(int) dims[0],(int) dims[1]);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

Before calling the screenshot, determine whether the screen is rotated:

	private Bitmap takeScreenshot() {
		 mDisplay.getRealMetrics(mDisplayMetrics);
		 float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
		float degrees = getDegreesForRotation(mDisplay.getRotation());
		boolean requiresRotation = (degrees > 0);
		//如果屏幕发生旋转,通过matrix旋转回来
		if (requiresRotation) {
			mDisplayMatrix.reset();
			mDisplayMatrix.preRotate(-degrees);
			mDisplayMatrix.mapPoints(dims);
			dims[0] = Math.abs(dims[0]);
			dims[1] = Math.abs(dims[1]);
		}
		//调用截屏
		screenShotByReflect();
		return mScreenBitmap;
	}

Advantages: No pop-up authorization, no system signature;

Disadvantages: Using the reflection mechanism, if the system API or method changes, it cannot be called;

 

3. Use the adb command of screencap

The command line is like this: adb shell screencap -p file_path

To execute in the code, without adb shell, directly screencap -p file_path, call the Runtime process to execute:

	public static void screenShotByShell(String filePath){
		String shotCmd = "screencap -p " + filePath + " \n";
		try {
			Runtime.getRuntime().exec(shotCmd);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

Advantages: The code is simple, and the pictures can be obtained directly;

Disadvantages: system signature required;

 

The above three screenshot methods can be used according to the application scenario. If it is a user app and needs continuous screen recording, it is recommended to use MediaProjectionManager; if you want to get a single Bitmap, you can use SurfaceControl; if it is a system app, and you want to get pictures, the adb command line of screencap is preferred.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324515284&siteId=291194637