在使用 MediaProject 的过程种,遇到了N种错误,自己一直也没有解决
下面来分享一个我找到的资料,我对作者的代码进行了小部分的修改来完成我的功能。在这里非常感谢作者,
我在各种平台都搜集了很久,没有找到一个可用的 MeidaPorject 截屏代码,而网上的教程各式各样,但是实操都会遇到各种各样的问题。由于作者好像在除了 github 平台发布了代码之外,在其他平台并没有找到。
特此,使用了作者代码截屏的部分,并修改了部分代码,作者的github仓库
sdk 28
MainActivity
public class MainActivity extends BaseActivity implements View.OnClickListener {
private static final int REQUEST_CODE = 100;
private static MediaProjection sMediaProjection;
Button mBtnCapture;
private MediaProjectionManager mProjectionManager;
private ScreenCapturer mSC;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnCapture = findViewById(R.id.btn_capture);
mBtnCapture.setOnClickListener(this);
mProjectionManager = (MediaProjectionManager) getSystemService(
Context.MEDIA_PROJECTION_SERVICE);
}
@Override
protected void onResume() {
super.onResume();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_capture:
startActivityForResult(mProjectionManager.createScreenCaptureIntent(),
REQUEST_CODE);
break;
default:
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d("WOW", "on result : requestCode = " + requestCode + " resultCode = " + resultCode);
if (RESULT_OK == resultCode && REQUEST_CODE == requestCode) {
sMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
if (sMediaProjection != null) {
Log.d("WOW", "Start capturing...");
new ScreenCapturer(this, sMediaProjection, "").startProjection();
}
}
}
}
BaseActivity
public class BaseActivity extends AppCompatActivity {
private static final int REQUEST_CODE = 9527;
private static String[] PERMISSIONS = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA
};
@Override
protected void onResume() {
super.onResume();
checkPermissions();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]
grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (REQUEST_CODE == requestCode) {
boolean isAllGranted = false;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
isAllGranted = false;
break;
}
isAllGranted = true;
}
if (!isAllGranted) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Need Permission");
builder.setPositiveButton("Grant now", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
});
builder.setNegativeButton("Fuck yourself", null);
builder.show();
} else {
}
}
}
//permission
private void checkPermissions() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return;
}
Boolean isAllPermissionGranted = false;
for (String permission : PERMISSIONS) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
isAllPermissionGranted = false;
break;
}
isAllPermissionGranted = true;
}
if (!isAllPermissionGranted) {
requestPermissions(PERMISSIONS, REQUEST_CODE);
}
}
}
ScreenCapture
public class ScreenCapturer {
private static MediaProjection sMediaProjection;
boolean isScreenCaptureStarted;
OnImageCaptureScreenListener listener;
private int mDensity;
private Display mDisplay;
private int mWidth;
private int mHeight;
private ImageReader mImageReader;
private VirtualDisplay mVirtualDisplay;
private Handler mHandler;
private String STORE_DIR;
private Context mContext;
public ScreenCapturer(Context context, MediaProjection mediaProjection, String savePath) {
sMediaProjection = mediaProjection;
mContext = context;
isScreenCaptureStarted = false;
new Thread() {
@Override
public void run() {
Looper.prepare();
mHandler = new Handler();
Looper.loop();
}
}.start();
if (TextUtils.isEmpty(savePath)) {
File externalFilesDir = mContext.getExternalFilesDir(null);
Log.d("WOW", "externalFilesDir:" + externalFilesDir.getAbsolutePath());
if (externalFilesDir != null) {
STORE_DIR = externalFilesDir.getAbsolutePath() + "/myScreenshots";
} else {
Toast.makeText(mContext, "No save path assigned!", Toast.LENGTH_SHORT).show();
}
} else {
STORE_DIR = savePath;
}
}
public ScreenCapturer startProjection() {
if (sMediaProjection != null) {
File storeDir = new File(STORE_DIR);
if (!storeDir.exists()) {
boolean success = storeDir.mkdirs();
if (!success) {
Log.d("WOW", "mkdir " + storeDir + " failed");
return this;
} else {
Log.d("WOW", "mkdir " + storeDir + " success");
}
} else {
Log.d("WOW", " " + storeDir + " exist");
}
} else {
Log.d("WOW", "get mediaprojection failed");
}
try {
Thread.sleep(500); // 防止截屏截到 显示截屏权限的窗口
isScreenCaptureStarted = true;
} catch (InterruptedException e) {
e.printStackTrace();
}
WindowManager window = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mDisplay = window.getDefaultDisplay();
final DisplayMetrics metrics = new DisplayMetrics();
// use getMetrics is 2030, use getRealMetrics is 2160, the diff is NavigationBar's height
mDisplay.getRealMetrics(metrics);
mDensity = metrics.densityDpi;
Log.d("WOW", "metrics.widthPixels is " + metrics.widthPixels);
Log.d("WOW", "metrics.heightPixels is " + metrics.heightPixels);
mWidth = metrics.widthPixels;
mHeight = metrics.heightPixels;
//start capture reader
mImageReader = ImageReader.newInstance(mWidth, mHeight, PixelFormat.RGBA_8888, 2);
mVirtualDisplay = sMediaProjection.createVirtualDisplay(
"ScreenShot",
mWidth,
mHeight,
mDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
mImageReader.getSurface(),
null,
mHandler);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
if (isScreenCaptureStarted) {
Image image = null;
FileOutputStream fos = null;
Bitmap bitmap = null;
try {
image = reader.acquireLatestImage();
if (image != null) {
// bitmap = ImageUtils.image_ARGB8888_2_bitmap(metrics, image);
bitmap = ImageUtils.image_2_bitmap(image, Bitmap.Config.ARGB_8888);
if (null != listener) {
listener.imageCaptured(ImageUtils.bitmap2byte(bitmap, 80));
}
Date currentDate = new Date();
SimpleDateFormat date = new SimpleDateFormat("yyyyMMddhhmmss");
String fileName = STORE_DIR + "/myScreen_" + date.format(
currentDate) + ".png";
fos = new FileOutputStream(fileName);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
Log.d("WOW", "End now!!!!!! Screenshot saved in " + fileName);
Toast.makeText(mContext, "Screenshot saved in " + fileName,
Toast.LENGTH_LONG).show();
stopProjection();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (null != fos) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != bitmap) {
bitmap.recycle();
}
if (null != image) {
image.close();
}
}
}
}
}, mHandler);
sMediaProjection.registerCallback(new MediaProjectionStopCallback(), mHandler);
return this;
}
public ScreenCapturer stopProjection() {
isScreenCaptureStarted = false;
Log.d("WOW", "Screen captured");
mHandler.post(new Runnable() {
@Override
public void run() {
if (sMediaProjection != null) {
sMediaProjection.stop();
}
}
});
return this;
}
public ScreenCapturer setListener(OnImageCaptureScreenListener listener) {
this.listener = listener;
return this;
}
public interface OnImageCaptureScreenListener {
public void imageCaptured(byte[] image);
}
private class MediaProjectionStopCallback extends MediaProjection.Callback {
@Override
public void onStop() {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
}
if (mImageReader != null) {
mImageReader.setOnImageAvailableListener(null, null);
}
sMediaProjection.unregisterCallback(MediaProjectionStopCallback.this);
}
});
}
}
}
ImageUtils
public class ImageUtils {
public static Bitmap image_ARGB8888_2_bitmap(DisplayMetrics metrics, Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int width = image.getWidth();
int height = image.getHeight();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
int offset = 0;
Bitmap bitmap;
bitmap = Bitmap.createBitmap(metrics, width, height, Bitmap.Config.ARGB_8888);
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
int pixel = 0;
pixel |= (buffer.get(offset) & 0xff) << 16; // R
pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G
pixel |= (buffer.get(offset + 2) & 0xff); // B
pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
bitmap.setPixel(j, i, pixel);
offset += pixelStride;
}
offset += rowPadding;
}
return bitmap;
}
/**
* 这个方法可以转换,但是得到的图片右边多了一列,比如上面方法得到1080x2160,这个方法得到1088x2160
* 所以要对得到的Bitmap裁剪一下
*
* @param image
* @param config
* @return
*/
public static Bitmap image_2_bitmap(Image image, Bitmap.Config config) {
int width = image.getWidth();
int height = image.getHeight();
Bitmap bitmap;
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;
Log.d("WOW",
"pixelStride:" + pixelStride + ". rowStride:" + rowStride + ". rowPadding" + rowPadding);
bitmap = Bitmap.createBitmap(
width + rowPadding / pixelStride/*equals: rowStride/pixelStride */
, height, config);
bitmap.copyPixelsFromBuffer(buffer);
return Bitmap.createBitmap(bitmap, 0, 0, width, height);
// return bitmap;
}
/**
* PNG
* @return
*/
public static byte[] bitmap2byte(Bitmap bmp, int quality) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, quality, baos);
return baos.toByteArray();
}
public static Bitmap byte2bitmap(byte[] data) {
if (data.length != 0) {
return BitmapFactory.decodeByteArray(data, 0, data.length);
} else {
return null;
}
}
}
User-permission
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
```