如何使用 MediaProjection API 从后台服务类中截取屏幕截图?
Posted
技术标签:
【中文标题】如何使用 MediaProjection API 从后台服务类中截取屏幕截图?【英文标题】:How to take a Screenshot from a background-service class using MediaProjection API? 【发布时间】:2020-11-06 08:58:49 【问题描述】:在对该主题进行了大量研究后,虽然我找到了一些答案,但我无法理解 MediaProjection API 的工作原理。
我想从后台服务类中截取设备的屏幕截图。有没有可能做到。我有一个 MainActivity.java 启动一个 serviceIntent 到另一个类,它是一个服务(不是一个活动)。所以我想在这个服务类中实现这个API。请帮帮我
【问题讨论】:
【参考方案1】:这是实现这一目标的棘手方法。
首先你需要创建透明的背景主题。
<style name="transparentTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:background">#00000000</item> <!-- Or any transparency or color you need -->
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:navigationBarColor" tools:ignore="NewApi">#00000000</item>
<item name="android:statusBarColor" tools:ignore="NewApi">#00000000</item>
</style>
现在您需要在 Manifest 文件中的 ScreenShotActivity 上添加此应用此主题。
<activity
android:name=".Activities.ScreenShotActivity"
android:theme="@style/transparentTheme" />
<activity
你的 ScreenShotActivity 类。
public class ScreenShotActivity extends Activity
private static final int videoTime = 5000;
private static final int REQUEST_CODE = 1000;
private static final int REQUEST_PERMISSION = 1000;
private static final SparseIntArray ORIENTATION = new SparseIntArray();
private MediaProjectionManager mediaProjectionManager;
private MediaProjection mediaProjection;
private VirtualDisplay virtualDisplay;
private ScreenShotActivity.MediaProjectionCallback mediaProjectionCallback;
private MediaRecorder mediaRecorder;
PostWebAPIData postWebAPIData;
private int mScreenDensity;
private static int DISPLAY_WIDTH = 720;
private static int DISPLAY_HEIGHT = 1280;
static
ORIENTATION.append(Surface.ROTATION_0, 90);
ORIENTATION.append(Surface.ROTATION_90, 0);
ORIENTATION.append(Surface.ROTATION_180, 270);
ORIENTATION.append(Surface.ROTATION_270, 180);
private String screenShotUri = "";
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_shot);
init();
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void init()
//Screen tracking Code Started here..............................
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mScreenDensity = metrics.densityDpi;
postWebAPIData = new PostWebAPIData();
DISPLAY_HEIGHT = metrics.heightPixels;
DISPLAY_WIDTH = metrics.widthPixels;
mediaRecorder = new MediaRecorder();
mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if (ContextCompat.checkSelfPermission(ScreenShotActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ ContextCompat.checkSelfPermission(ScreenShotActivity.this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED)
if (ActivityCompat.shouldShowRequestPermissionRationale(ScreenShotActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ||
ActivityCompat.shouldShowRequestPermissionRationale(ScreenShotActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE))
else
ActivityCompat.requestPermissions(ScreenShotActivity.this, new String[]
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO
, REQUEST_PERMISSION);
else
new Handler().postDelayed(new Runnable()
@Override
public void run()
toogleScreenShare();
, 500);
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void toogleScreenShare()
initRecorder();
recordScreen();
public void getPathScreenShot(String filePath)
FFmpegMediaMetadataRetriever med = new FFmpegMediaMetadataRetriever();
med.setDataSource(filePath);
Bitmap bmp = med.getFrameAtTime(2 * 1000000, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
String myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + new StringBuilder("/screenshot").append(".bmp").toString();
File myDir = new File(myPath);
myDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String fname = "Image-" + n + ".jpg";
File file = new File(myDir, fname);
Log.i(TAG, "" + myDir);
if (myDir.exists())
myDir.delete();
try
FileOutputStream out = new FileOutputStream(myDir);
bmp.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
catch (Exception e)
e.printStackTrace();
postScreenShot(myPath);
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void recordScreen()
if (mediaProjection == null)
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
else
virtualDisplay = createVirtualDisplay();
mediaRecorder.start();
onBackPressed();
new Handler().postDelayed(new Runnable()
@Override
public void run()
mediaRecorder.stop();
mediaRecorder.reset();
stopRecordScreen();
destroyMediaProjection();
new Handler().postDelayed(new Runnable()
@Override
public void run()
getPathScreenShot(screenShotUri);
, 2000);
, videoTime);
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private VirtualDisplay createVirtualDisplay()
return mediaProjection.createVirtualDisplay("MainActivity", DISPLAY_WIDTH, DISPLAY_HEIGHT, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mediaRecorder.getSurface(), null, null);
private void initRecorder()
try
mediaRecorder.setAudiosource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
screenShotUri = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + new StringBuilder("/screenshot").append(".mp4").toString();
mediaRecorder.setOutputFile(screenShotUri);
mediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.setVideoEncodingBitRate(512 * 1000);
mediaRecorder.setVideoFrameRate(5);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int orientation = ORIENTATION.get(rotation + 90);
mediaRecorder.setOrientationHint(orientation);
mediaRecorder.prepare();
catch (IOException e)
e.printStackTrace();
Log.d("ExceptionOccured", "" + e.getMessage());
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data)
super.onActivityResult(requestCode, resultCode, data);
if (requestCode != REQUEST_CODE)
stopService(new Intent(this, BackgroundService.class));
startService(new Intent(this, BackgroundService.class));
Toast.makeText(ScreenShotActivity.this, "Unknown Error", Toast.LENGTH_SHORT).show();
Log.d("Livetracking", "ScreenShot" + requestCode + " " + resultCode + " " + data);
return;
if (resultCode != RESULT_OK)
stopService(new Intent(this, BackgroundService.class));
startService(new Intent(this, BackgroundService.class));
Toast.makeText(ScreenShotActivity.this, "Permission denied" + requestCode, Toast.LENGTH_SHORT).show();
Log.d("Livetracking", "Screenshot" + requestCode + " " + resultCode + " " + data);
return;
Log.d("Livetracking", "Screenshot" + requestCode + " " + resultCode + " " + data);
mediaProjectionCallback = new ScreenShotActivity.MediaProjectionCallback();
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
mediaProjection.registerCallback(mediaProjectionCallback, null);
virtualDisplay = createVirtualDisplay();
mediaRecorder.start();
onBackPressed();
new Handler().postDelayed(new Runnable()
@Override
public void run()
mediaRecorder.stop();
mediaRecorder.reset();
stopRecordScreen();
destroyMediaProjection();
new Handler().postDelayed(new Runnable()
@Override
public void run()
getPathScreenShot(screenShotUri);
, 2000);
, videoTime);
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
if (am != null)
List<ActivityManager.AppTask> tasks = am.getAppTasks();
if (tasks != null && tasks.size() > 0)
Log.d("RemovingApp", "recent");
tasks.get(0).setExcludeFromRecents(true);
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private class MediaProjectionCallback extends MediaProjection.Callback
@Override
public void onStop()
mediaRecorder.stop();
mediaRecorder.reset();
mediaProjection = null;
stopRecordScreen();
destroyMediaProjection();
if (mediaProjection != null)
destroyMediaProjection();
super.onStop();
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void stopRecordScreen()
if (virtualDisplay == null)
virtualDisplay.release();
if (mediaProjection != null)
destroyMediaProjection();
return;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void destroyMediaProjection()
if (mediaProjection != null)
mediaProjection.unregisterCallback(mediaProjectionCallback);
mediaProjection.stop();
mediaProjection = null;
在您的清单文件中添加这些权限。
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
现在魔术从这里开始,您需要像这样从您的服务中调用 ScreenShotActivity。
Intent dialogIntent = new Intent(BackgroundService.this, ScreenShotActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(dialogIntent);
【讨论】:
哦,好吧。这是一种非常聪明的做法。谢谢 我应该如何导入 PostWebAPIData 以及它有什么作用? @Saatwik 只需从您的代码中删除 PostWebAPIData 并运行 好的,但是 postScreenShot 方法呢? @Saatwik 你想在服务器上的某个地方发布这个截图,比如使用 Larave; API 的..?以上是关于如何使用 MediaProjection API 从后台服务类中截取屏幕截图?的主要内容,如果未能解决你的问题,请参考以下文章
使用 MediaProjection API 从后台服务记录:这种方法是唯一可能的吗?
是否可以使用 MediaProjection API 并在录制过程中处理每个录制的帧?
android Q中无法识别MediaProjection服务类型