• Android屏幕录制


    这里使用Java语言编写实现,完整代码如下:

    文件 AndroidMainfest.xml 的主要配置

    1. "1.0" encoding="utf-8"?>
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    3. package="com.example.demoapp">
    4. <uses-permission android:name="android.permission.RECORD_AUDIO" />
    5. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    6. <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    7. <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    8. <application
    9. android:allowBackup="true"
    10. android:icon="@mipmap/ic_launcher"
    11. android:label="@string/app_name"
    12. android:roundIcon="@mipmap/ic_launcher_round"
    13. android:supportsRtl="true">
    14. <activity
    15. android:name=".MainActivity"
    16. android:exported="true">
    17. <intent-filter>
    18. <action android:name="android.intent.action.MAIN" />
    19. <category android:name="android.intent.category.LAUNCHER" />
    20. intent-filter>
    21. activity>
    22. <activity
    23. android:name=".ScreenRecordingActivity"
    24. android:exported="false"
    25. android:launchMode="singleTask">activity>
    26. <service
    27. android:name=".ScreenRecordingService"
    28. android:enabled="true"
    29. android:exported="true"
    30. android:foregroundServiceType="mediaProjection" />
    31. application>
    32. manifest>

    文件activity_screen_recording.xml的内容

    1. "1.0" encoding="utf-8"?>
    2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. xmlns:app="http://schemas.android.com/apk/res-auto"
    4. xmlns:tools="http://schemas.android.com/tools"
    5. android:layout_width="match_parent"
    6. android:layout_height="match_parent"
    7. android:orientation="vertical"
    8. tools:context=".MainActivity"
    9. tools:ignore="MissingConstraints">
    10. <LinearLayout
    11. android:layout_width="fill_parent"
    12. android:layout_height="fill_parent"
    13. android:gravity="center_horizontal"
    14. android:orientation="vertical"
    15. android:paddingTop="0dp">
    16. <TextView
    17. android:id="@+id/textview_recording_info"
    18. android:layout_width="wrap_content"
    19. android:layout_height="100dp"
    20. android:layout_marginTop="20dp"
    21. android:layout_gravity="center"
    22. android:textColor="#333333"
    23. android:textSize="20sp"
    24. android:textStyle="bold"
    25. android:text="--"
    26. android:maxLines="5" />
    27. <Button
    28. android:id="@+id/btn_recording_start"
    29. android:layout_width="wrap_content"
    30. android:layout_height="wrap_content"
    31. android:layout_marginTop="40dp"
    32. android:text="开始录制" />
    33. <Button
    34. android:id="@+id/btn_recording_stop"
    35. android:layout_width="wrap_content"
    36. android:layout_height="wrap_content"
    37. android:layout_marginTop="40dp"
    38. android:text="停止录制" />
    39. LinearLayout>
    40. androidx.constraintlayout.widget.ConstraintLayout>

    文件ScreenRecordingActivity.java的完整代码

    1. package com.example.demoapp;
    2. import android.Manifest;
    3. import android.app.Activity;
    4. import android.content.ComponentName;
    5. import android.content.Intent;
    6. import android.content.ServiceConnection;
    7. import android.content.pm.PackageManager;
    8. import android.media.projection.MediaProjectionManager;
    9. import android.os.Build;
    10. import android.os.Bundle;
    11. import android.os.IBinder;
    12. import android.util.DisplayMetrics;
    13. import android.view.MenuItem;
    14. import android.widget.Button;
    15. import android.widget.TextView;
    16. import android.widget.Toast;
    17. import androidx.activity.result.ActivityResultLauncher;
    18. import androidx.activity.result.contract.ActivityResultContracts;
    19. import androidx.annotation.NonNull;
    20. import androidx.annotation.Nullable;
    21. import androidx.appcompat.app.ActionBar;
    22. import androidx.appcompat.app.AppCompatActivity;
    23. import androidx.core.app.ActivityCompat;
    24. import androidx.core.content.ContextCompat;
    25. import com.example.myandroidnfcapp.R;
    26. /**
    27. * @Function: 屏幕录制视频。
    28. * @Author: ChengJh。
    29. * @Date: 2023/09/06。
    30. * @Description: https://blog.csdn.net/qq_46546793/article/details/123279152 和 https://blog.csdn.net/weixin_42602900/article/details/128340037 。
    31. */
    32. public class ScreenRecordingActivity extends AppCompatActivity {
    33. private static final String TAG = ScreenRecordingActivity.class.getSimpleName();
    34. //录屏服务
    35. private ScreenRecordingService mService;
    36. private int mServiceStatus = ScreenRecordingService.statusInit;
    37. private boolean clickedStart = false;
    38. private ActivityResultLauncher activityLauncher;
    39. private TextView textViewInfo;
    40. private boolean prepared = false;
    41. @Override
    42. protected void onCreate(@Nullable Bundle savedInstanceState) {
    43. super.onCreate(savedInstanceState);
    44. setContentView(R.layout.activity_screen_recording);
    45. ActionBar actionBar = getSupportActionBar();
    46. if (actionBar != null) {
    47. actionBar.setHomeButtonEnabled(true);
    48. actionBar.setDisplayHomeAsUpEnabled(true);
    49. actionBar.setTitle("录屏管理页面");
    50. }
    51. textViewInfo = findViewById(R.id.textview_recording_info);
    52. Button btnStart = (Button) findViewById(R.id.btn_recording_start);
    53. btnStart.setOnClickListener(view -> {
    54. startRecord();
    55. });
    56. Button btnStop = (Button) findViewById(R.id.btn_recording_stop);
    57. btnStop.setOnClickListener(view -> {
    58. stopRecord();
    59. });
    60. //注: registerForActivityResult()方法, 只能在onCreate()中注册, onStart()之后就不能注册了。。
    61. activityLauncher = registerForActivityResult(
    62. new ActivityResultContracts.StartActivityForResult(), o -> {
    63. System.out.println(TAG + "_registerForActivityResult_callback_" + o.getResultCode());
    64. if (o.getResultCode() == Activity.RESULT_OK) {
    65. actionAfterConfirmAgreed(o.getResultCode(), o.getData());
    66. }
    67. });
    68. }
    69. @Override
    70. public void onStart() {
    71. super.onStart();
    72. }
    73. @Override
    74. public void onResume() {
    75. super.onResume();
    76. /** 在Activity的六个核心回调 onCreate()、onStart()、onResume()、onPause()、onStop() 和 onDestroy() 中,
    77. * onStart()、onResume()、onPause()、onStop() 会在 onRestart() 之后再次触发。
    78. * 因此, 有的任务如果放在其中某个函数内触发, 可能需要加个标记变量控制一下。
    79. */
    80. if (!prepared) {
    81. prepared = true;
    82. checkPermission();
    83. }
    84. }
    85. @Override
    86. public void onDestroy() {
    87. /** 页面销毁的时候, 可以不停止录屏 */
    88. // stopRecord();
    89. if (mServiceStatus >= ScreenRecordingService.statusServiceConnected) {
    90. unbindService(serviceConnection);
    91. }
    92. super.onDestroy();
    93. }
    94. // 标题栏返回按钮事件。
    95. @Override
    96. public boolean onOptionsItemSelected(MenuItem item) {
    97. switch (item.getItemId()) {
    98. case android.R.id.home:
    99. //this.finish();
    100. this.onBackPressed();
    101. return true;
    102. }
    103. return super.onOptionsItemSelected(item);
    104. }
    105. // @Override
    106. // //返回方法, 获取返回的信息。
    107. // protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    108. // super.onActivityResult(requestCode, resultCode, data);
    109. // System.out.println(TAG + "_onActivityResult");
    110. // //首先判断请求码是否一致, 结果是否ok 。
    111. // if (requestCode == ScreenRecordingService.requestCode && resultCode == RESULT_OK) {
    112. // actionAfterUserAgree(resultCode, data);
    113. // }
    114. // }
    115. @Override
    116. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]
    117. grantResults) {
    118. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    119. System.out.println(TAG + "_onRequestPermissionsResult");
    120. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && requestCode == ScreenRecordingService.requestCodeForPermisssion) {
    121. for (int i = 0; i < permissions.length; i++) {
    122. if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
    123. return;
    124. }
    125. }
    126. connectService();
    127. }
    128. }
    129. /**
    130. * 权限申请
    131. */
    132. private void checkPermission() {
    133. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    134. String[] permissions = new String[]{
    135. Manifest.permission.RECORD_AUDIO,
    136. Manifest.permission.WRITE_EXTERNAL_STORAGE,
    137. Manifest.permission.READ_EXTERNAL_STORAGE
    138. };
    139. for (String permission : permissions) {
    140. if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
    141. ActivityCompat.requestPermissions(this, permissions, ScreenRecordingService.requestCodeForPermisssion);
    142. return;
    143. }
    144. }
    145. }
    146. connectService();
    147. }
    148. //连接服务。
    149. public void connectService() {
    150. System.out.println(TAG + "_connectService");
    151. //通过intent为中介绑定Service, 会自动创建。
    152. Intent intent = new Intent(this, ScreenRecordingService.class);
    153. //绑定过程连接, 选择绑定模式。
    154. bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    155. }
    156. //连接服务成功与否, 具体连接过程。
    157. //调用连接接口, 实现连接, 回调连接结果。
    158. private final ServiceConnection serviceConnection = new ServiceConnection() {
    159. @Override
    160. public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    161. System.out.println(TAG + "_ServiceConnection_onServiceConnected");
    162. //服务连接成功, 需要通过Binder获取服务, 达到Activity和Service通信的目的
    163. ScreenRecordingService.ScreenRecordBinder binder = (ScreenRecordingService.ScreenRecordBinder) iBinder;
    164. //通过Binder获取Service
    165. mService = binder.getScreenRecordService();
    166. mService.setListener(new ScreenRecordingService.ScreenRecordingServiceListener() {
    167. @Override
    168. public void onPrepareComplete() {
    169. if (clickedStart && mService.startRecord()) {
    170. textViewInfo.setText("正在录屏");
    171. }
    172. }
    173. @Override
    174. public void onRecordingFinish(String videoPath) {
    175. textViewInfo.setText("视频文件路径: " + videoPath);
    176. }
    177. });
    178. if (mService.isRunning()) {
    179. System.out.println(TAG + "_ServiceConnection_RunAlready");
    180. mServiceStatus = ScreenRecordingService.statusConfirmAgreed;
    181. textViewInfo.setText("正在录屏");
    182. if (clickedStart) {
    183. Toast.makeText(ScreenRecordingActivity.this, "已在录屏服务中", Toast.LENGTH_SHORT).show();
    184. }
    185. return;
    186. }
    187. mServiceStatus = ScreenRecordingService.statusServiceConnected;
    188. if (clickedStart) {
    189. actionAfterServiceConnected();
    190. }
    191. }
    192. @Override
    193. public void onServiceDisconnected(ComponentName componentName) {
    194. //连接失败。
    195. Toast.makeText(ScreenRecordingActivity.this, "录屏服务未连接成功", Toast.LENGTH_SHORT).show();
    196. }
    197. };
    198. private void actionAfterConfirmAgreed(int resultCode, @Nullable Intent data) {
    199. mServiceStatus = ScreenRecordingService.statusConfirmAgreed;
    200. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    201. if (mService != null) {
    202. //获取录屏屏幕范围参数。
    203. DisplayMetrics metrics = new DisplayMetrics();
    204. getWindowManager().getDefaultDisplay().getMetrics(metrics);
    205. mService.setConfig(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
    206. Intent intent = new Intent(this, ScreenRecordingService.class);
    207. intent.putExtra("code", resultCode);
    208. intent.putExtra("data", data);
    209. startForegroundService(intent);
    210. } else {
    211. System.out.println(TAG + "_onActivityResult_exception");
    212. }
    213. }
    214. }
    215. private void actionAfterServiceConnected() {
    216. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    217. //获取到服务, 初始化录屏管理者。
    218. MediaProjectionManager projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
    219. //通过管理者, 创建录屏请求, 通过Intent。
    220. Intent captureIntent = projectionManager.createScreenCaptureIntent();
    221. //将请求码作为标识一起发送, 调用该接口, 需有返回方法。
    222. //startActivityForResult(captureIntent, ScreenRecordingService.requestCode);
    223. activityLauncher.launch(captureIntent);
    224. }
    225. }
    226. private void nextActionByStatus() {
    227. switch (mServiceStatus) {
    228. case ScreenRecordingService.statusInit:
    229. //点击请求录屏时, 第一件事, 检查权限。
    230. checkPermission();
    231. break;
    232. case ScreenRecordingService.statusPermissionOK:
    233. connectService();
    234. break;
    235. case ScreenRecordingService.statusServiceConnected:
    236. actionAfterServiceConnected();
    237. break;
    238. default:
    239. if (mService != null) {
    240. if (mService.startRecord()) {
    241. textViewInfo.setText("正在录屏");
    242. }
    243. }
    244. break;
    245. }
    246. }
    247. private void startRecord() {
    248. System.out.println(TAG + "_startRecord_" + (null == mService));
    249. clickedStart = true;
    250. if (mService != null && mService.isRunning()) {
    251. //如果在录制, 弹出提示。
    252. Toast.makeText(ScreenRecordingActivity.this, "当前正在录屏, 请不要重复点击哦", Toast.LENGTH_SHORT).show();
    253. } else {
    254. //如果不在录制, 就开启录制。
    255. nextActionByStatus();
    256. }
    257. }
    258. private void stopRecord() {
    259. clickedStart = false;
    260. if (null == mService || !mService.isRunning()) {
    261. //没有录屏, 无需停止, 弹出提示。
    262. Toast.makeText(ScreenRecordingActivity.this, "还没有录屏, 无需停止", Toast.LENGTH_SHORT).show();
    263. } else {
    264. //停止录屏。
    265. mService.stopRecord();
    266. }
    267. }
    268. }

    文件 ScreenRecordingService.java 的完整代码

    1. package com.example.demoapp;
    2. import android.app.Notification;
    3. import android.app.NotificationChannel;
    4. import android.app.NotificationManager;
    5. import android.app.PendingIntent;
    6. import android.app.Service;
    7. import android.content.Intent;
    8. import android.graphics.BitmapFactory;
    9. import android.hardware.display.DisplayManager;
    10. import android.hardware.display.VirtualDisplay;
    11. import android.media.MediaRecorder;
    12. import android.media.projection.MediaProjection;
    13. import android.media.projection.MediaProjectionManager;
    14. import android.os.Binder;
    15. import android.os.Build;
    16. import android.os.Environment;
    17. import android.os.IBinder;
    18. import android.util.Log;
    19. import android.widget.Toast;
    20. import androidx.annotation.Nullable;
    21. import com.example.myandroidnfcapp.MainActivity;
    22. import com.example.myandroidnfcapp.R;
    23. import java.io.File;
    24. import java.io.IOException;
    25. import java.util.Objects;
    26. public class ScreenRecordingService extends Service {
    27. private static final String TAG = ScreenRecordingService.class.getSimpleName();
    28. public static final int statusInit = 0;
    29. public static final int statusPermissionOK = 1;
    30. public static final int statusServiceConnected = 2;
    31. public static final int statusConfirmAgreed = 3;
    32. public static final int requestCodeForPermisssion = 110;
    33. public static final int requestCode = 111;
    34. private MediaProjectionManager mediaProjectionManager;
    35. //录屏工具MediaProjection。
    36. private MediaProjection mediaProjection;
    37. //录像机MediaRecorder。
    38. private MediaRecorder mediaRecorder;
    39. //用于录屏的虚拟屏幕。
    40. private VirtualDisplay virtualDisplay;
    41. //录制屏幕的宽高像素。
    42. private int mWidth = 720;
    43. private int mHeight = 1280;
    44. private int mDpi = 1;
    45. //视频存储路径
    46. private String mVideoPath = "";
    47. //标志, 判断是否正在录屏
    48. private boolean running = false;
    49. //回调接口, 以及接口中要做的事。
    50. public interface ScreenRecordingServiceListener {
    51. void onPrepareComplete();
    52. void onRecordingFinish(String videoPath);
    53. }
    54. private ScreenRecordingServiceListener listener;
    55. @Override
    56. public void onCreate() {
    57. super.onCreate();
    58. // HandlerThread serviceThread = new HandlerThread("service_thread", android.os.Process.THREAD_PRIORITY_BACKGROUND);
    59. // serviceThread.start();
    60. // running = false;
    61. }
    62. @Override
    63. public int onStartCommand(Intent intent, int flags, int startId) {
    64. // 注: mediaProjection的生成 必须在Service中进行。
    65. createNotificationChannel();
    66. int resultCode = intent.getIntExtra("code", -1);
    67. Intent resultData = intent.getParcelableExtra("data");
    68. Log.i(TAG, "onStartCommand_resultCode=" + resultCode);
    69. Log.i(TAG, "onStartCommand_resultData=" + resultData);
    70. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    71. if (null == mediaProjectionManager) {
    72. mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
    73. }
    74. mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, Objects.requireNonNull(resultData));
    75. //mediaProjection = ((MediaProjectionManager) Objects.requireNonNull(getSystemService(Context.MEDIA_PROJECTION_SERVICE))).getMediaProjection(resultCode, resultData);
    76. Log.i(TAG, "mediaProjection_created");
    77. }
    78. if (listener != null) {
    79. listener.onPrepareComplete();
    80. }
    81. return super.onStartCommand(intent, flags, startId);
    82. }
    83. @Override
    84. public void onDestroy() {
    85. super.onDestroy();
    86. }
    87. @Override
    88. public boolean onUnbind(Intent intent) {
    89. return super.onUnbind(intent);
    90. }
    91. //返回的Binder。
    92. public class ScreenRecordBinder extends Binder {
    93. //返回Service的方法。
    94. public ScreenRecordingService getScreenRecordService() {
    95. return ScreenRecordingService.this;
    96. }
    97. }
    98. @Nullable
    99. @Override
    100. //返回一个Binder用于通信, 需要一个获取Service的方法。
    101. public IBinder onBind(Intent intent) {
    102. return new ScreenRecordBinder();
    103. }
    104. //设置需要录制的屏幕参数。
    105. public void setConfig(int width, int height, int dpi) {
    106. mWidth = width;
    107. mHeight = height;
    108. mDpi = dpi;
    109. }
    110. public void setMediaProjectionManager(MediaProjectionManager projectionManager) {
    111. mediaProjectionManager = projectionManager;
    112. }
    113. public void setListener(ScreenRecordingServiceListener callback) {
    114. listener = callback;
    115. }
    116. //返回判断, 判断其是否在录屏。
    117. public boolean isRunning() {
    118. return running;
    119. }
    120. //服务的两个主要逻辑之一 ~ 开始录屏。
    121. public boolean startRecord() {
    122. Log.i(TAG, "startRecord");
    123. //首先判断是否有录屏工具以及是否在录屏
    124. if (null == mediaProjection || running) {
    125. return false;
    126. }
    127. //初始化录像机, 录音机Recorder。
    128. createRecorder();
    129. //根据获取的屏幕参数创建虚拟的录屏屏幕。
    130. createVirtualDisplay();
    131. //本来不加异常也可以, 但是这样就不知道是否start成功。
    132. //万一start没有成功, 但是running置为true了, 就产生了错误也无提示。
    133. //提示开始录屏了, 但是并没有工作。
    134. try {
    135. //准备工作都完成了, 可以开始录屏了。
    136. mediaRecorder.start();
    137. //标志位改为正在录屏。
    138. running = true;
    139. Toast.makeText(this, "录屏开启成功", Toast.LENGTH_SHORT).show();
    140. return true;
    141. } catch (Exception e) {
    142. e.printStackTrace();
    143. //有异常, start出错, 没有开始录屏。
    144. Toast.makeText(this, "录屏开启失败", Toast.LENGTH_SHORT).show();
    145. //标志位变回没有录屏的状态。
    146. running = false;
    147. return false;
    148. }
    149. }
    150. //服务的两个主要逻辑之一 ~ 停止录屏。
    151. public boolean stopRecord() {
    152. Log.i(TAG, "stopRecord");
    153. if (!running) {
    154. //没有在录屏, 无法停止。
    155. return false;
    156. }
    157. //无论设备是否还原或者有异常, 但是确实录屏结束, 修改标志位为未录屏。
    158. running = false;
    159. //本来加不加捕获异常都可以, 但是为了用户体验度, 添加会更好。
    160. try {
    161. //Recorder停止录像, 重置还原, 以便下一次使用。
    162. mediaRecorder.stop();
    163. mediaRecorder.reset();
    164. //释放virtualDisplay的资源。
    165. virtualDisplay.release();
    166. } catch (Exception e) {
    167. e.printStackTrace();
    168. //有异常, 保存失败。
    169. Toast.makeText(this, "录屏结束异常", Toast.LENGTH_SHORT).show();
    170. return false;
    171. }
    172. //无异常, 保存成功。
    173. Toast.makeText(this, "录屏结束 而且 保存成功", Toast.LENGTH_SHORT).show();
    174. if (listener != null) {
    175. listener.onRecordingFinish(mVideoPath);
    176. }
    177. return true;
    178. }
    179. //初始化Recorder录像机。
    180. public void createRecorder() {
    181. Log.i(TAG, "createRecorder");
    182. //创建Recorder。
    183. mediaRecorder = new MediaRecorder();
    184. //设置音频来源。
    185. mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    186. //设置视频来源。
    187. mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    188. //设置视频格式为mp4。
    189. mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    190. //设置视频存储地址。
    191. mVideoPath = getOutputFile().getAbsolutePath();
    192. //保存在该位置。
    193. mediaRecorder.setOutputFile(mVideoPath);
    194. //设置视频大小, 清晰度。
    195. mediaRecorder.setVideoSize(mWidth, mHeight);
    196. //设置视频编码为H264。
    197. mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    198. //设置音频编码。
    199. mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    200. //设置视频码率。
    201. mediaRecorder.setVideoEncodingBitRate(2 * mWidth * mHeight);
    202. mediaRecorder.setVideoFrameRate(16);
    203. //初始化完成, 进入准备阶段, 准备被使用。
    204. try {
    205. mediaRecorder.prepare();
    206. } catch (IOException e) {
    207. e.printStackTrace();
    208. //异常提示
    209. Toast.makeText(this, "Recorder录像机准备失败", Toast.LENGTH_SHORT).show();
    210. }
    211. }
    212. public void createVirtualDisplay() {
    213. //虚拟屏幕通过MediaProjection获取, 传入一系列传过来的参数。
    214. try {
    215. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    216. virtualDisplay = mediaProjection.createVirtualDisplay("VirtualScreen", mWidth, mHeight, mDpi,
    217. DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder.getSurface(), null, null);
    218. }
    219. } catch (Exception e) {
    220. e.printStackTrace();
    221. Toast.makeText(this, "virtualDisplay创建异常", Toast.LENGTH_SHORT).show();
    222. }
    223. }
    224. //获取输出存储文件夹的位置。
    225. public static File getOutputDirectory() {
    226. String directoryFilePathName = Environment.
    227. getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
    228. .getAbsolutePath()
    229. + "/ScreenRecording/";
    230. //创建该文件夹。
    231. File directoryFile = new File(directoryFilePathName);
    232. if (!directoryFile.exists()) {
    233. //如果该文件夹不存在。
    234. if (!directoryFile.mkdirs()) {
    235. //如果没有创建成功。
    236. return null;
    237. }
    238. }
    239. //创建成功了, 返回该目录。
    240. return directoryFile;
    241. }
    242. private File getOutputFile() {
    243. File directoryFile = getOutputDirectory();
    244. File file = new File(directoryFile, "SR" + System.currentTimeMillis() + ".mp4");
    245. // if (!file.exists()) {
    246. // try {
    247. // file.createNewFile();
    248. // } catch (Exception e) {
    249. // e.printStackTrace();
    250. // }
    251. // }
    252. Log.i(TAG, "filePath_" + file.getAbsolutePath());
    253. return file;
    254. }
    255. private void createNotificationChannel() {
    256. Notification.Builder builder = new Notification.Builder(this.getApplicationContext()); //获取一个Notification构造器。
    257. Intent nIntent = new Intent(this, MainActivity.class); //点击后跳转的界面, 可以设置跳转数据。
    258. builder.setContentIntent(PendingIntent.getActivity(this, 0, nIntent, 0))
    259. .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
    260. //.setContentTitle("ScreenRecording") // 设置下拉列表里的标题
    261. .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
    262. .setContentText("is running......") // 设置上下文内容
    263. .setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
    264. /** 以下是对Android 8.0的适配 */
    265. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    266. final String channelId = "MyChannelId";
    267. final String channelName = "MyChannelName";
    268. //普通notification适配。
    269. builder.setChannelId(channelId);
    270. //前台服务notification适配。
    271. NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    272. NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW);
    273. notificationManager.createNotificationChannel(channel);
    274. }
    275. Notification notification = builder.build(); // 获取构建好的Notification对象。
    276. notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音。
    277. startForeground(requestCode, notification);
    278. }
    279. }

    特别鸣谢下面链接:

    (0)  https://blog.csdn.net/qq_46546793/article/details/123279152

    (0)  https://blog.csdn.net/weixin_42602900/article/details/128340037

  • 相关阅读:
    Https加密过程
    记录报错_linux: su root 报错: su: Authentication failure
    【Milvus的以文搜图】
    【10. 差分】
    CAS:1260586-88-6_生物素-C5-叠氮_Biotin-C5-Azide
    利用 Forcing InnoDB Recovery 特性解决 MySQL 重启失败的问题
    Jekyll 的机制、转换步骤和结构介绍
    wpf主页面解析
    【ModbusTCP协议】
    Maven POM:掌握项目对象模型的艺术
  • 原文地址:https://blog.csdn.net/C201008/article/details/132755941