欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android實(shí)現(xiàn)屏幕錄制與本地保存功能的完整指南

 更新時(shí)間:2025年07月31日 10:10:17   作者:onthewaying  
本文將詳細(xì)介紹如何在?Android?應(yīng)用中實(shí)現(xiàn)屏幕錄制功能,并將錄制的視頻保存到本地存儲(chǔ),我們將涵蓋從權(quán)限獲取到最終視頻保存的完整流程,包括關(guān)鍵代碼實(shí)現(xiàn),感興趣的小伙伴跟著小編一起來(lái)看看吧

一、實(shí)現(xiàn)原理概述

Android 屏幕錄制主要依賴以下幾個(gè)核心組件:

  1. MediaProjection:獲取屏幕內(nèi)容的入口,出于安全和隱私的考慮,每次錄制前,系統(tǒng)都會(huì)彈出一個(gè)對(duì)話框,明確請(qǐng)求用戶的授權(quán)。
  2. MediaProjectionManager: 管理MediaProjection實(shí)例
  3. VirtualDisplay:虛擬顯示設(shè)備,將屏幕內(nèi)容投射到編碼器
  4. MediaRecorder: 負(fù)責(zé)錄制和編碼

由于屏幕錄制通常是持續(xù)性任務(wù),即使用戶切換到其他應(yīng)用或返回桌面,錄制也應(yīng)繼續(xù)。因此,我們必須將錄制邏輯放置在前臺(tái)服務(wù) (Foreground Service)  中。 這不僅能防止我們的應(yīng)用在后臺(tái)被系統(tǒng)終止,還能通過(guò)一個(gè)持續(xù)的通知告知用戶,屏幕正在被錄制,保證了操作的透明性。

二、環(huán)境準(zhǔn)備

1.配置 Manifest 文件

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 運(yùn)行前臺(tái)服務(wù)的必要權(quán)限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<!-- 聲明用于屏幕錄制的 Service --> 
<service android:name=".ScreenCaptureService"
    android:exported="false"
    android:foregroundServiceType="mediaProjection"/>

2.請(qǐng)求用戶授權(quán)

我們無(wú)法直接請(qǐng)求屏幕捕獲權(quán)限。相反,我們必須通過(guò) MediaProjectionManager 創(chuàng)建一個(gè) Intent,然后啟動(dòng)這個(gè) Intent 來(lái)顯示一個(gè)系統(tǒng)對(duì)話框。

val mediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager

// 使用 ActivityResultLauncher 來(lái)處理返回結(jié)果
val screenCaptureLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val serviceIntent = Intent(this, ScreenCaptureService::class.java).apply {
            action = "START"
            putExtra("resultCode", result.resultCode)
            putExtra("data", result.data)
        }
        startForegroundService(serviceIntent)
    } else {
        // 用戶拒絕了授權(quán)
        Toast.makeText(this, "需要屏幕捕獲權(quán)限才能錄制", Toast.LENGTH_SHORT).show()
    }
}

// 點(diǎn)擊錄屏按鈕調(diào)用
fun startScreenCapture() {
    screenCaptureLauncher.launch(mediaProjectionManager.createScreenCaptureIntent())
}

3.創(chuàng)建并實(shí)現(xiàn)前臺(tái)服務(wù)

class ScreenCaptureService : Service() {

    private lateinit var mediaProjection: MediaProjection
    private lateinit var virtualDisplay: VirtualDisplay
    private lateinit var mediaRecorder: MediaRecorder
    private lateinit var callBack:MediaProjection.Callback

    private var currentVideoUri: Uri? = null

    companion object {
        const val RESULT_CODE = "resultCode"
        const val RESULT_DATA = "resultData"
        const val NOTIFICATION_ID = 1001
        const val CHANNEL_ID = "screen_record_channel"
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val resultCode = intent?.getIntExtra(RESULT_CODE, 0) ?: 0
        val resultData = intent?.getParcelableExtra<Intent>(RESULT_DATA)

        if (resultCode != 0 && resultData != null) {
            startRecording(resultCode, resultData)
        }

        return START_STICKY
    }

    private fun startRecording(resultCode: Int, resultData: Intent) {
        //創(chuàng)建通知并啟動(dòng)前臺(tái)服務(wù)
        startForeground(NOTIFICATION_ID, createNotification())

        // 獲取mediaProjection實(shí)例
        val projectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
        mediaProjection = projectionManager.getMediaProjection(resultCode, resultData)
        val fileName = "ScreenRecord_${System.currentTimeMillis()}.mp4"
        // 配置 MediaRecorder,設(shè)置視頻源、輸出格式、編碼器、文件路徑等。
        mediaRecorder = MediaRecorder().apply {
            setVideoSource(MediaRecorder.VideoSource.SURFACE)
            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
            setOutputFile(getOutputFileDescriptor(applicationContext,fileName))
            setVideoEncoder(MediaRecorder.VideoEncoder.H264)
            setVideoSize(1080, 1920) // 根據(jù)實(shí)際需求調(diào)整
            setVideoFrameRate(30)
            prepare()
        }

        callBack = object : MediaProjection.Callback() {
            override fun onStop() {

            }
        }

        // 注冊(cè)回調(diào)
        mediaProjection.registerCallback(callBack, null)

        // 創(chuàng)建一個(gè)虛擬顯示 (VirtualDisplay),并將其渲染的畫(huà)面輸出到 MediaRecorder 的 Surface 上
        virtualDisplay = mediaProjection.createVirtualDisplay(
            "ScreenRecorder",
            1080, 1920, resources.displayMetrics.densityDpi,
            DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
            mediaRecorder.surface, null, null
        )

        // 開(kāi)始錄制
        mediaRecorder.start()
    }

    private fun createNotification(): Notification {
        createNotificationChannel()

        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("屏幕錄制中")
            .setContentText("正在錄制您的屏幕操作")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                "屏幕錄制",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "屏幕錄制服務(wù)正在運(yùn)行"
            }

            (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
                .createNotificationChannel(channel)
        }
    }

    // 設(shè)置視頻保存路徑
    private fun getOutputFileDescriptor(context: Context, fileName: String): FileDescriptor? {
        val contentValues = ContentValues().apply {
            put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
            put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/")
                put(MediaStore.Video.Media.IS_PENDING, 1)
            }
        }

        val collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        val itemUri = context.contentResolver.insert(collection, contentValues)

        currentVideoUri = itemUri

        return if (itemUri != null) {
            context.contentResolver.openFileDescriptor(itemUri, "w")?.fileDescriptor
        } else {
            null
        }
    }

    override fun onDestroy() {
        mediaProjection.unregisterCallback(callBack)
        super.onDestroy()
        stopRecording()
    }

     // 停止錄制并釋放資源
    private fun stopRecording() {
        mediaRecorder.apply {
            stop()
            reset()
            release()
        }

        virtualDisplay.release()

        if (::mediaProjection.isInitialized) {
            mediaProjection.stop()
        }
        // 將錄制的視頻保存到本地
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && currentVideoUri != null) {
            val contentValues = ContentValues().apply {
                put(MediaStore.Video.Media.IS_PENDING, 0)
            }
            contentResolver.update(currentVideoUri!!, contentValues, null, null)
        }
    }

    override fun onBind(intent: Intent?): IBinder? = null
}

三、總結(jié)

本文利用Android屏幕錄制API完成了基本的屏幕錄制功能,后續(xù)還可以結(jié)合音視頻編碼將屏幕錄制的數(shù)據(jù)利用RTMP推流到服務(wù)端實(shí)現(xiàn)錄屏直播功能。

到此這篇關(guān)于Android實(shí)現(xiàn)屏幕錄制與本地保存功能的完整指南的文章就介紹到這了,更多相關(guān)Android屏幕錄制與本地保存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論