当前位置: 代码网 > it编程>App开发>Android > Android实现屏幕录制与本地保存功能的完整指南

Android实现屏幕录制与本地保存功能的完整指南

2025年07月31日 Android 我要评论
一、实现原理概述android 屏幕录制主要依赖以下几个核心组件:mediaprojection:获取屏幕内容的入口,出于安全和隐私的考虑,每次录制前,系统都会弹出一个对话框,明确请求用户的授权。me

一、实现原理概述

android 屏幕录制主要依赖以下几个核心组件:

  1. mediaprojection:获取屏幕内容的入口,出于安全和隐私的考虑,每次录制前,系统都会弹出一个对话框,明确请求用户的授权。
  2. mediaprojectionmanager: 管理mediaprojection实例
  3. virtualdisplay:虚拟显示设备,将屏幕内容投射到编码器
  4. mediarecorder: 负责录制和编码

由于屏幕录制通常是持续性任务,即使用户切换到其他应用或返回桌面,录制也应继续。因此,我们必须将录制逻辑放置在前台服务 (foreground service)  中。 这不仅能防止我们的应用在后台被系统终止,还能通过一个持续的通知告知用户,屏幕正在被录制,保证了操作的透明性。

二、环境准备

1.配置 manifest 文件

<uses-permission android:name="android.permission.write_external_storage" />
<!-- 运行前台服务的必要权限 -->
<uses-permission android:name="android.permission.foreground_service" />

<!-- 声明用于屏幕录制的 service --> 
<service android:name=".screencaptureservice"
    android:exported="false"
    android:foregroundservicetype="mediaprojection"/>

2.请求用户授权

我们无法直接请求屏幕捕获权限。相反,我们必须通过 mediaprojectionmanager 创建一个 intent,然后启动这个 intent 来显示一个系统对话框。

val mediaprojectionmanager = getsystemservice(context.media_projection_service) as mediaprojectionmanager

// 使用 activityresultlauncher 来处理返回结果
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 {
        // 用户拒绝了授权
        toast.maketext(this, "需要屏幕捕获权限才能录制", toast.length_short).show()
    }
}

// 点击录屏按钮调用
fun startscreencapture() {
    screencapturelauncher.launch(mediaprojectionmanager.createscreencaptureintent())
}

3.创建并实现前台服务

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) {
        //创建通知并启动前台服务
        startforeground(notification_id, createnotification())

        // 获取mediaprojection实例
        val projectionmanager = getsystemservice(context.media_projection_service) as mediaprojectionmanager
        mediaprojection = projectionmanager.getmediaprojection(resultcode, resultdata)
        val filename = "screenrecord_${system.currenttimemillis()}.mp4"
        // 配置 mediarecorder,设置视频源、输出格式、编码器、文件路径等。
        mediarecorder = mediarecorder().apply {
            setvideosource(mediarecorder.videosource.surface)
            setoutputformat(mediarecorder.outputformat.mpeg_4)
            setoutputfile(getoutputfiledescriptor(applicationcontext,filename))
            setvideoencoder(mediarecorder.videoencoder.h264)
            setvideosize(1080, 1920) // 根据实际需求调整
            setvideoframerate(30)
            prepare()
        }

        callback = object : mediaprojection.callback() {
            override fun onstop() {

            }
        }

        // 注册回调
        mediaprojection.registercallback(callback, null)

        // 创建一个虚拟显示 (virtualdisplay),并将其渲染的画面输出到 mediarecorder 的 surface 上
        virtualdisplay = mediaprojection.createvirtualdisplay(
            "screenrecorder",
            1080, 1920, resources.displaymetrics.densitydpi,
            displaymanager.virtual_display_flag_auto_mirror,
            mediarecorder.surface, null, null
        )

        // 开始录制
        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 = "屏幕录制服务正在运行"
            }

            (getsystemservice(notification_service) as notificationmanager)
                .createnotificationchannel(channel)
        }
    }

    // 设置视频保存路径
    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
}

三、总结

本文利用android屏幕录制api完成了基本的屏幕录制功能,后续还可以结合音视频编码将屏幕录制的数据利用rtmp推流到服务端实现录屏直播功能。

到此这篇关于android实现屏幕录制与本地保存功能的完整指南的文章就介绍到这了,更多相关android屏幕录制与本地保存内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com