简介 Android录屏可以用在很多方面,比如录制精彩的游戏过程,录制喜欢的视频片段,或是给朋友展示手机桌面等等
常见的录屏软件 AZ Screen Recorder 免费使用,付费增值 Android 5.0以上系统,无须ROOT
ilos screen recorder 免费使用,付费增值 Android 5.0以上系统,无须ROOT
SCR Screen Recorder 免费使用,付费增值 Android系统,需要ROOT
Android录屏API 在Android 4.4 以前,是无法使用录屏功能的,既没有内置工具支持,也没有开放API;
从Android 4.4 KitKat系统开始,通过自带的 ScreenRecord工具支持屏幕录制(截屏保存成mp4),不提供开放API;
从Android 5.0 Lollipop系统开始,提供开放API用于屏幕截取和共享;
使用限制: 仅允许捕获非安全的屏幕内容,不允许捕获系统音频。
注: APP可以通过使用SurfaceView.setSecure()方法来拒绝画面被录制;
不过凡事都有例外,以上方法均是基于非ROOT系统的,一旦成功ROOT,所有的限制就都不存在了。
Android录屏BUG 由于这个BUG出现的几率太高,而且是必现,所以有必要单独拿出来说一下。
在 Android 5.1 系统中,当首次开启屏幕录制功能的时候,系统会弹出一个提示框,英文是”Do not show again”,中文是”不再显示”,不要勾选此对话框,否则会导致UI出问题,如果你不小心勾选了也不要紧,重装一下录屏软件,再次启动就又可以选了。
Android 4.4录屏示例 使用SDK自带的adb工具来完成录屏:
adb shell screenrecord /sdcard/output.mp4
此命令将录制从当前时间开始180秒长度的视频,分辨率为手机屏幕分辨率,码率为4M,视频格式为MP4,存储在SD卡,名字为output.mp4。
注意事项:
输入 –time-limit N,限制视频录制时间为N秒。如果不限制,默认180秒。
输入–size N*N,限制录制视频分辨率为N*N。如果未指定,默认使用手机的分辨率。
输入 –bit-rate,指定视频的比特率为6Mbps。如果不指定,默认为4Mbps。
某些设备可能无法直接录制,原因是分辨率太高。如果遇到此类问题,系统将自行指定较低的分辨率。
不支持录制过程中屏幕旋转,如果录制过程中旋转,有可能画面被切断。
无法同步录制音频。
Android 5.0录屏示例 录屏API位于SDK的 android.media.projection 类下,共有3个子类:
MediaProjection 用于捕获屏幕内容或录制系统声音; MediaProjection.Callback 用于回调消息; MediaProjectionManager 用于管理MediaProjection对象;
Android项目示例 中包含了一个录屏的示例 MediaProjectionDemo samples/ApiDemos/src/com/example/android/apis/media/projection/MediaProjectionDemo.java
代码如下
package com.example.android.apis.media.projection; import com.example.android.apis.R; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.Toast; import android.widget.ToggleButton; import java.util.ArrayList; import java.util.List; public class MediaProjectionDemo extends Activity { private static final String TAG = "MediaProjectionDemo"; private static final int PERMISSION_CODE = 1; // 录屏输出的分辨率 private static final List<Resolution> RESOLUTIONS = new ArrayList<Resolution>() {{ add(new Resolution(640,360)); add(new Resolution(960,540)); add(new Resolution(1366,768)); add(new Resolution(1600,900)); }}; private int mScreenDensity; private MediaProjectionManager mProjectionManager; private int mDisplayWidth; private int mDisplayHeight; private boolean mScreenSharing; private MediaProjection mMediaProjection; private VirtualDisplay mVirtualDisplay; private Surface mSurface; private SurfaceView mSurfaceView; private ToggleButton mToggle; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.media_projection); // 获取当前屏幕的DPI DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); mScreenDensity = metrics.densityDpi; mSurfaceView = (SurfaceView) findViewById(R.id.surface); // Surface的获取放在此处并不合适,因为可能为空 // 最好是放在 SurfaceHolder 的回调函数 surfaceCreated() 中赋值。 mSurface = mSurfaceView.getHolder().getSurface(); // 获取录屏管理对名 mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); ArrayAdapter<Resolution> arrayAdapter = new ArrayAdapter<Resolution>( this, android.R.layout.simple_list_item_1, RESOLUTIONS); Spinner s = (Spinner) findViewById(R.id.spinner); s.setAdapter(arrayAdapter); s.setOnItemSelectedListener(new ResolutionSelector()); s.setSelection(0); mToggle = (ToggleButton) findViewById(R.id.screen_sharing_toggle); } @Override public void onDestroy() { super.onDestroy(); if (mMediaProjection != null) { mMediaProjection.stop(); mMediaProjection = null; } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode != PERMISSION_CODE) { Log.e(TAG, "Unknown request code: " + requestCode); return; } if (resultCode != RESULT_OK) { Toast.makeText(this, "User denied screen sharing permission", Toast.LENGTH_SHORT).show(); return; } mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data); mMediaProjection.registerCallback(new MediaProjectionCallback(), null); mVirtualDisplay = createVirtualDisplay(); } public void onToggleScreenShare(View view) { if (((ToggleButton) view).isChecked()) { shareScreen(); } else { stopScreenSharing(); } } private void shareScreen() { mScreenSharing = true; if (mSurface == null) { return; } if (mMediaProjection == null) { startActivityForResult(mProjectionManager.createScreenCaptureIntent(), PERMISSION_CODE); return; } mVirtualDisplay = createVirtualDisplay(); } private void stopScreenSharing() { if (mToggle.isChecked()) { mToggle.setChecked(false); } mScreenSharing = false; if (mVirtualDisplay != null) { mVirtualDisplay.release(); mVirtualDisplay = null; } } private VirtualDisplay createVirtualDisplay() { return mMediaProjection.createVirtualDisplay("ScreenSharingDemo", mDisplayWidth, mDisplayHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mSurface, null /*Callbacks*/, null /*Handler*/); } private void resizeVirtualDisplay() { if (mVirtualDisplay == null) { return; } mVirtualDisplay.resize(mDisplayWidth, mDisplayHeight, mScreenDensity); } private class ResolutionSelector implements Spinner.OnItemSelectedListener { @Override public void onItemSelected(AdapterView<?> parent, View v, int pos, long id) { Resolution r = (Resolution) parent.getItemAtPosition(pos); ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams(); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { mDisplayHeight = r.y; mDisplayWidth = r.x; } else { mDisplayHeight = r.x; mDisplayWidth = r.y; } lp.height = mDisplayHeight; lp.width = mDisplayWidth; mSurfaceView.setLayoutParams(lp); } @Override public void onNothingSelected(AdapterView<?> parent) { /* Ignore */ } } private class MediaProjectionCallback extends MediaProjection.Callback { @Override public void onStop() { mMediaProjection = null; stopScreenSharing(); } } private class SurfaceCallbacks implements SurfaceHolder.Callback { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mDisplayWidth = width; mDisplayHeight = height; resizeVirtualDisplay(); } @Override public void surfaceCreated(SurfaceHolder holder) { mSurface = holder.getSurface(); if (mScreenSharing) { shareScreen(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (!mScreenSharing) { stopScreenSharing(); } } } private static class Resolution { int x; int y; public Resolution(int x, int y) { this.x = x; this.y = y; } @Override public String toString() { return x + "x" + y; } } }
核心的几个方法如下
MediaProjectionManager.createScreenCaptureIntent() 生成一个用于屏幕捕获的对象
startActivityForResult() 启动屏幕捕获对象
onActivityResult() 接收屏幕捕获的返回数据
MediaProjection.createVirtualDisplay() 将屏幕内容捕获到 Surface 对象中
参考 Android 5.0 API 屏幕截图和共享 获取Activity的结果