android——录制屏幕

录制屏幕

1、界面

2、核心代码

Kotlin 复制代码
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.MediaRecorder
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.os.IBinder
import android.util.DisplayMetrics
import android.util.Log
import android.view.WindowManager
import androidx.core.app.NotificationCompat
import java.io.IOException

class ScreenRecordService : Service() {
    private var mContext: Context? = null
    private var projectionManager: MediaProjectionManager? = null
    private var mMediaProjection: MediaProjection? = null
    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        mContext = this
        var resultCode = intent?.getIntExtra("resultCode", -1)
        var path = intent?.getStringExtra("path")
        var resultData: Intent? = intent?.getParcelableExtra("data")
        Log.e("TAG", "录制的路径为:" + path)
        startNotification();
        projectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
        mMediaProjection = resultCode?.let { resultData?.let { it1 -> projectionManager?.getMediaProjection(it, it1) } }
        path?.let { startRecording(it) }
        return super.onStartCommand(intent, flags, startId)
    }

    private var NOTIFICATION_CHANNEL_ID = "id";
    private var NOTIFICATION_CHANNEL_NAME = "channel";
    private var NOTIFICATION_CHANNEL_DESC = "desc";
    private fun startNotification() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            var notificationIntent = Intent(mContext, ScreenRecordService::class.java)
            var pendingIntent: PendingIntent? = null
            pendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
                PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
            } else {
                PendingIntent.getActivity(
                    this, 0, notificationIntent,
                    PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
                );
            }
            var notificationBuilder = mContext?.let {
                NotificationCompat.Builder(it, NOTIFICATION_CHANNEL_ID)
                    .setLargeIcon(BitmapFactory.decodeResource(mContext!!.resources, R.drawable.ic_launcher_foreground))
                    .setSmallIcon(R.drawable.ic_launcher_foreground)
                    .setContentTitle("start record")
                    .setContentText("=== start record ===")
                    .setContentIntent(pendingIntent)
            };
            var notification = notificationBuilder?.build();
            var channel = NotificationChannel(
                NOTIFICATION_CHANNEL_ID,
                NOTIFICATION_CHANNEL_NAME,
                NotificationManager.IMPORTANCE_DEFAULT
            );
            channel.description = NOTIFICATION_CHANNEL_DESC;
            var notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
            startForeground(1, notification);
        }
    }


    private var isScreenRecoding = false
    private var mMediaRecorder: MediaRecorder? = null
    private var mVirtualDisplay: VirtualDisplay? = null
    private fun startRecording(filePath: String) {
        if (!isScreenRecoding) {
            try {
                // 创建 MediaRecorder 并设置参数
                val metrics = DisplayMetrics()
                val windowManager: WindowManager = mContext?.getSystemService(WINDOW_SERVICE) as WindowManager
                windowManager.defaultDisplay.getMetrics(metrics)
                mMediaRecorder = MediaRecorder()
                mMediaRecorder?.setVideoSource(MediaRecorder.VideoSource.SURFACE)
                mMediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
                mMediaRecorder?.setOutputFile(filePath)
                mMediaRecorder?.setVideoSize(metrics.widthPixels, metrics.heightPixels)
                mMediaRecorder?.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
                mMediaRecorder?.setVideoEncodingBitRate(1920 * 1080 * 3)
                mMediaRecorder?.setVideoFrameRate(30)

                // 准备 MediaRecorder
                mMediaRecorder?.prepare()

                // 创建 VirtualDisplay 以获取屏幕内容
                mVirtualDisplay = mMediaProjection?.createVirtualDisplay(
                    "ScreenRecord",
                    metrics.widthPixels, metrics.heightPixels, metrics.densityDpi,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    mMediaRecorder?.surface, null, null
                )

                // 开始录制
                mMediaRecorder?.start()
                isScreenRecoding = true
                Log.i("TAG", "开始录屏 $filePath")
            } catch (e: IOException) {
                Log.e("TAG", "录屏失败: " + e.message)
                e.printStackTrace()
            }
        }
    }

    public fun stopRecording() {
        if (isScreenRecoding) {
            try {
                // 停止录制
                mMediaRecorder?.stop()
                mMediaRecorder?.reset()
                mMediaRecorder?.release()
                mMediaRecorder = null

                // 停止 VirtualDisplay
                mVirtualDisplay?.release()

                // 停止 MediaProjection
                mMediaProjection?.stop()
                Log.i("TAG", "结束录屏")
            } catch (e: Exception) {
                Log.e("TAG", "停止录屏失败: " + e.message)
                e.printStackTrace()
            }
            isScreenRecoding = false
        }
    }

    override fun onDestroy() {
        stopRecording()
        super.onDestroy()
    }
}
Kotlin 复制代码
/**
 * 注意:
 * 1、需要手动给文件读写权限
 * 2、保存的视频文件在/storage/emulated/0/screen.mp4
 */
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 500) {
            val path = Environment.getExternalStorageDirectory().absolutePath
            // 录屏权限
            screenService = Intent(this, ScreenRecordService::class.java)
            screenService?.let {
                it.putExtra("resultCode", resultCode)
                it.putExtra("data", data)
                it.putExtra("path", "$path/screen.mp4")
                startForegroundService(it)
            }
        }
    }

    /** 开始录制 **/
    fun startRecord(view: View) {
        start()
    }

    /** 停止录制 **/
    fun stopRecord(view: View) {
        stop()
    }

    private var mProjectionManager: MediaProjectionManager? = null
    private var screenService: Intent? = null
    private fun start() {
        mProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
        // 请求录屏权限
        mProjectionManager?.createScreenCaptureIntent()?.let { startActivityForResult(it, 500) };
    }

    private fun stop() {
        stopService(screenService)
    }
}
Kotlin 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ScreenRecord"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".ScreenRecordService"
            android:enabled="true"
            android:exported="true"
            android:foregroundServiceType="mediaProjection" />
    </application>

</manifest>

3、下载地址

https://download.csdn.net/download/wy313622821/90115690

4、操作注意

点击开始录制后,切换到需要录制的界面,如果想要结束则回到当前应用点击停止录屏按钮

相关推荐
AI玫瑰助手1 小时前
Python模块:import导入模块与模块的搜索路径
android·开发语言·python
IT WorryFree2 小时前
GitHub / Gitee / Gitea / GitLab 四平台完整对比(定位、优缺点、适用场景)
gitee·github·gitea
yewq-cn2 小时前
Android Log System
android
问心无愧05133 小时前
ctf show web入门107
android·前端·笔记·android studio
AI科技星3 小时前
第三卷:质数王朝志 第四章:RSA护国玄阵,质数锁天地,一数镇万法
android·人工智能·架构·概率论·学习方法
AFinalStone3 小时前
Android12 U盘插拔链路源码全解析(八)实战调试与案例分析
android·frameworks
我命由我123453 小时前
Android 开发问题:View 的 getWidth、getHeight 方法返回的值都为 0
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
JohnnyDeng9413 小时前
【Android】Hilt 依赖注入:原理与最佳实践
android·kotlin·mvvm·hilt
星间都市山脉16 小时前
Android STS(Security Test Suite)完整介绍与测试流程
android·java·linux·windows·ubuntu·android studio·androidx
Yeyu17 小时前
你真的了解AIDL吗? 附:AIDL 与 Binder 交互全解析
android