MediaRecorder
/**
* Used to record audio and video. The recording control is based on a
* simple state machine (see below).
*
* <p><img src="{@docRoot}images/mediarecorder_state_diagram.gif" border="0" />
* </p>
*
* <p>A common case of using MediaRecorder to record audio works as follows:
*
* <pre>MediaRecorder recorder = new MediaRecorder();
* recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
* recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
* recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
* recorder.setOutputFile(PATH_NAME);
* recorder.prepare();
* recorder.start(); // Recording is now started
* ...
* recorder.stop();
* recorder.reset(); // You can reuse the object by going back to setAudioSource() step
* recorder.release(); // Now the object cannot be reused
* </pre>
*
* <p>Applications may want to register for informational and error
* events in order to be informed of some internal update and possible
* runtime errors during recording. Registration for such events is
* done by setting the appropriate listeners (via calls
* (to {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener and/or
* {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener).
* In order to receive the respective callback associated with these listeners,
* applications are required to create MediaRecorder objects on threads with a
* Looper running (the main UI thread by default already has a Looper running).
*
* <p><strong>Note:</strong> Currently, MediaRecorder does not work on the emulator.
*
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about how to use MediaRecorder for recording video, read the
* <a href="{@docRoot}guide/topics/media/camera.html#capture-video">Camera</a> developer guide.
* For more information about how to use MediaRecorder for recording sound, read the
* <a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a> developer guide.</p>
* </div>
*/
public class MediaRecorder implements AudioRouting
本文录屏实现 SDK必须大于或等于 Build.VERSION_CODES.LOLLIPOP;
权限申请
悬浮框权限申请:
@RequiresApi(api = Build.VERSION_CODES.M)
private fun checkSystemAlertPermission() {
if (Build.VERSION.SDK_INT >= 23) { // Android6.0及以后需要动态申请权限
if (!Settings.canDrawOverlays(this)) {
//启动Activity让用户授权
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
startActivityForResult(intent, 1010)
} else {
// 弹出悬浮窗
hasSystemAlertPermission = true
}
} else {
// 弹出悬浮窗
hasSystemAlertPermission = true
}
}
存储,音频权限申请:
@RequiresApi(api = Build.VERSION_CODES.M)
private fun permissions() {
val permissionList = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO)
val needList: MutableList<String> = ArrayList()
for (i in permissionList.indices) {
val checkPermission = checkSelfPermission(permissionList[i])
if (checkPermission != PackageManager.PERMISSION_GRANTED) {
needList.add(permissionList[i])
}
}
if (needList.isEmpty()) {
requestRecord()
} else {
val permissionArray = needList.toTypedArray()
ActivityCompat.requestPermissions(this, permissionArray, REQUEST_PERMISSION)
}
}
开启录屏
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private fun requestRecord() {
val metrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(metrics)
mScreenWidth = metrics.widthPixels
mScreenHeight = metrics.heightPixels
mScreenDensity = metrics.densityDpi
val projectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startActivityForResult(projectionManager.createScreenCaptureIntent(), REQUEST_RECORD)
}
启动服务
Service提供主要功能包含
弹出悬浮计时框
public void showRecordView() {
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
//设置效果为背景透明.
params.format = PixelFormat.RGBA_8888;
//设置flags.不可聚焦及不可使用按钮对悬浮窗进行操控.
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//设置窗口初始停靠位置.
params.gravity = Gravity.LEFT | Gravity.BOTTOM;
params.x = (int) context.getResources().getDimension(R.dimen.dip200);
params.y = statusBarHeight;
//设置悬浮窗口长宽数据.
params.width = (int) context.getResources().getDimension(R.dimen.dip180);
params.height = (int) context.getResources().getDimension(R.dimen.dip60);
LayoutInflater inflater = LayoutInflater.from(context);
recordView = inflater.inflate(R.layout.layout_record, null);
windowManager.addView(recordView, params);
btnStop = recordView.findViewById(R.id.btn_stop);
btnStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (recordListener != null) {
new Handler().post(new Runnable() {
@Override
public void run() {
windowManager.removeView(recordView);
}
});
recordStatus = RecordStateManager.STOP;
recordListener.stop();
}
}
});
tv_time = recordView.findViewById(R.id.tv_time);
tv_time.setText(sdf.format(calendar.getTime()));
timeCounter();
drag();
}
- 开始录屏
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private fun startRecord() {
try {
initRecorder()
mediaProjection = createMediaProjection()
virtualDisplay = mediaProjection!!.createVirtualDisplay("MainScreen",
mScreenWidth, mScreenHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mediaRecorder!!.surface, null, null)
mediaRecorder!!.start()
} catch (e: Exception) {
e.printStackTrace()
}
}
注册广播监听
监听广播
class RecordReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (PathConfig.ACTION_RECORD_SCREEN_STATE == intent.action) {
return
}
val state = intent.getIntExtra("state", -1)
RecordStateManager.instance.setRecordState(state)
}
}
由于屏幕录制需要在各个Activity 页面都允许,所以Service不能使用bindService启动,因此需要使用广播来监听录屏的状态。来通知页面变化。
GitHub 地址 :https://github.com/freemanDing/ScreenRecord