Giter VIP home page Giter VIP logo

screencapture's Introduction

###在Android 5.0,API 21 之前想要截图系统屏幕必须Root才能完成,5.0之后开放了接口,下面看我们是怎么实现的。

1. 涉及到的相关类

1. MediaProjectionManager

官方原话: Manages the retrieval of certain types of {@link MediaProjection} tokens. 这个类通过 Context#getSystemServiceMEDIA_PROJECTION_SERVICE 获取,他的功能就是获取MediaProjection

2. MediaProjection

官方原话:A token granting applications the ability to capture screen contents and/or record system audio. The exact capabilities granted depend on the type of MediaProjection.在这个类中我们能获取到屏幕的内容

3. ImageReader

官方原话:The ImageReader class allows direct application access to image data rendered into a {@link android.view.Surface} 通过这个类我们可以把Surface转换成图片

2. 上面三个类就可以完成我们截取屏幕图片的操作,那么下面我们将解释他们是怎么合作完成的

1. 首先获取用户授权,截图屏幕需要用户手动授权后才能操作

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public void requestCapturePermission() {

  if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
	 //5.0 之后才允许使用屏幕截图

 	return;
  }

   MediaProjectionManager mediaProjectionManager = (MediaProjectionManager)
  	getSystemService(Context.MEDIA_PROJECTION_SERVICE);
  startActivityForResult(
   	mediaProjectionManager.createScreenCaptureIntent(),
   	REQUEST_MEDIA_PROJECTION);
  }

这里必须使用startActivityForResult 因为在createScreenCaptureIntent() 方法中会返回用户授权截取屏幕的结果,用户根据下面弹窗允许或者拒绝

授权

用户选择后在Activity 的onActivityResult 中操作返回的结果data

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  	super.onActivityResult(requestCode, resultCode, data);

  	switch (requestCode) {
 			case REQUEST_MEDIA_PROJECTION:

   		if (resultCode == RESULT_OK && data != null) {
     			FloatWindowsService.setResultData(data);
     			startService(new Intent(getApplicationContext(), FloatWindowsService.class));
   		} 

   	break;
   	}
  }

这里我是用FloatWindowsService在桌面上显示一个悬浮按钮,点击截屏,下面我们看在FloatWindowsService 是如何实现截图

2. 截取屏幕内容生成Bitmap

首先创建ImageReader实例

	private void createImageReader() {

  	 mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight, PixelFormat.RGBA_8888, 2); 

  }

然后点击事件中触发startScreenShot()

  private void startScreenShot() {

  	mFloatView.setVisibility(View.GONE);

  	Handler handler = new Handler();
  	handler.postDelayed(new Runnable() {
 		public void run() {
   		//获取当前屏幕内容
   		startVirtual();
 		}
  	}, 5);

  	handler.postDelayed(new Runnable() {
 		public void run() {
   		//生成图片保存到本地
   		startCapture();

 		}
  	}, 30);
  }

startVirtual() 方法中我们做一件事,就是获取当前屏幕内容

  public void startVirtual() {
  	if (mMediaProjection != null) {
		 virtualDisplay();
  	} else {
		 setUpMediaProjection();
 	     virtualDisplay();
  	}
  }

与此同时需要获取MediaProjection 实例,而mResultData 是授权后返回的结果

  public void setUpMediaProjection() {
   if (mResultData == null) {
	 	Intent intent = new Intent(Intent.ACTION_MAIN);
	 	intent.addCategory(Intent.CATEGORY_LAUNCHER);
	 	startActivity(intent);
   } else {
        //mResultData是在Activity中用户授权后返回的结果
		 mMediaProjection = getMediaProjectionManager().getMediaProjection(Activity.RESULT_OK, mResultData);
  }
}

最终得到当前屏幕的内容,注意这里mImageReader.getSurface()被传入,屏幕的数据也将会在ImageReader中的Surface中

private void virtualDisplay() {
 	mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror",
    mScreenWidth, mScreenHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
    mImageReader.getSurface(), null, null);
 }

最后把mImageReader得到的屏幕内容数据转换成图片,在AsyncTask中处理,

Image.Plane中的 buffer 数据并不是完全是Bitmap所需要的,需要注意下面3点
1. Image 设置的图片格式与Bitmap设置的必须一致
2. 缓冲数据存在行间距,所以我们必须去除这些间距
3. Image 使用后必须调用image.close();关闭,否则再次使用会报错
@Override
protected Bitmap doInBackground(Image... params) {

	 if (params == null || params.length < 1 || params[0] == null) {

  	 return null;
 	}

 	Image image = params[0];

	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();
最后把生成的bitmap保存起来,就ok了

###源码

###APK

screencapture's People

Contributors

goodbranch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

screencapture's Issues

NullPointer

lz,目前发现会有一定频率的空指针异常,暂时我还没找出问题,log如下:
Process: com.branch.www.screencapture, PID: 15106
java.lang.NullPointerException: Attempt to invoke virtual method 'android.hardware.display.VirtualDisplay android.media.projection.MediaProjection.createVirtualDisplay(java.lang.String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay$Callback, android.os.Handler)' on a null object reference
at com.branch.www.screencapture.FloatWindowsService.virtualDisplay(FloatWindowsService.java:235)
at com.branch.www.screencapture.FloatWindowsService.startVirtual(FloatWindowsService.java:215)
at com.branch.www.screencapture.FloatWindowsService$2.run(FloatWindowsService.java:190)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6095)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

java.lang.RuntimeException:

java.lang.RuntimeException: Unable to create service com.branch.www.screencapture.FloatWindowsService: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@92f423f -- permission denied for window type 2003
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3544)
at android.app.ActivityThread.access$1300(ActivityThread.java:199)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1666)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@92f423f -- permission denied for window type 2003
at android.view.ViewRootImpl.setView(ViewRootImpl.java:822)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at com.branch.www.screencapture.FloatWindowsService.createFloatView(FloatWindowsService.java:122)
at com.branch.www.screencapture.FloatWindowsService.onCreate(FloatWindowsService.java:79)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3532)
at android.app.ActivityThread.access$1300(ActivityThread.java:199) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1666) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:193) 
at android.app.ActivityThread.main(ActivityThread.java:6669) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.