Android 实现屏幕录制

  1. 添加权限和服务声明

    xml 复制代码
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <service
                android:name=".ScreenService"
                android:enabled="true"
                android:exported="true"
                android:foregroundServiceType="mediaProjection"></service>
  2. 创建屏幕录制的 Service

    kotlin 复制代码
    import android.app.*
    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.CamcorderProfile
    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 ScreenService : 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")
            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, ScreenService::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);
                }
                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(ScreenUtil.TAG,"开始录屏 $filePath")
                } catch (e: IOException) {
                    Log.e(ScreenUtil.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(ScreenUtil.TAG,"结束录屏")
                } catch (e: Exception) {
                    Log.e(ScreenUtil.TAG, "停止录屏失败: " + e.message)
                    e.printStackTrace()
                }
                isScreenRecoding = false
            }
        }
    
        override fun onDestroy() {
            stopRecording()
            super.onDestroy()
        }
    }
  3. 启动和停止录制

    kotlin 复制代码
    private var mProjectionManager: MediaProjectionManager? = null
    var screenService:Intent?=null
    fun start(){
    	mProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
    	// 请求录屏权限
    	startActivityForResult(mProjectionManager?.createScreenCaptureIntent(), 500);
    }
    fun stop(){
    	stopService(screenService)
    }
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 500){
            screenService = Intent(mConext, ScreenService::class.java)
            screenService?.let {
                it.putExtra("resultCode", resultCode);
                it.putExtra("data", data);
                it.putExtra("path", "screen.mp4");
                startForegroundService(it)
            }
        }
    }
相关推荐
hai_android3 小时前
Kotlin / Android 常用函数使用示例手册
android·java·kotlin
hai_android5 小时前
Android MeasureSpec 详解
android·java·kotlin
又见情义6 小时前
RK3568 Android 13 本地 U 盘 OTA 升级实战:基于 RKUpdateService 的完整流程
android
Godikov7 小时前
Android10后台弹窗与APK自动更新
android
又见情义8 小时前
RK3568 Android 13 USB OTG/Host 切换调试经验分享
android
终端安全笔记9 小时前
iOS 27 之后「策略空转」:设备升级不报错,但旧策略不再管它
android·网络·安全·ios·智能手机
JMchen10 小时前
实战案例:实现120fps流畅的渐变进度条
android·kotlin·canvas
敲代码的瓦龙11 小时前
Jetpack?DataBinding!!!
android·java·开发语言·mysql·android-studio
Android-Flutter12 小时前
Compose CompositionLocal 详解
android·compose
三少爷的鞋13 小时前
Kotlin 协程闯关:看代码,猜结果
android