it编程 > App开发 > Android

Android实现一键录屏功能(附源码)

21人参与 2025-05-04 Android

一、项目介绍

在 android 5.0(api 21)及以上版本,系统提供了 mediaprojection api,允许应用在用户授权下录制屏幕内容并输出到视频文件。基于此,我们可以实现一个 一键录屏 功能,无需 root,也无需系统签名。完整功能包含:

申请录屏权限:使用 mediaprojectionmanager 触发系统授权对话框

屏幕采集与编码:结合 mediaprojection、virtualdisplay 与 mediarecorder(或 mediacodec + muxer)进行音视频同步录制

后台服务管理:将录屏流程封装在 service 或 foreground service 中,确保在应用切后台时仍可录制

用户交互:在应用内通过按钮控制录屏的启动、暂停、停止,并提供录制时长实时显示

文件存储与管理:自动将录制文件保存到外部存储或 mediastore,支持列表预览与播放

性能与兼容:兼容不同分辨率、适配屏幕方向变化,优化视频码率与帧率,控制录屏对系统性能的影响

二、相关技术与原理

技术功能
mediaprojection提供屏幕内容采集权限,输出到 virtualdisplay
virtualdisplay将屏幕内容镜像到指定 surface(例如 mediarecorder 的输入)
mediarecorder原生音视频录制 api,简化同步编码、封装 av 文件
mediacodec + muxer手动编码与封装,获得更细粒度控制(可选高级方案)
service后台管理录屏任务,结合前台服务确保录制不中断
mediastoreandroid 10+ 存储协议,安全写入公共相册
notification前台服务需在通知栏持续显示录制状态

api 要求:最早 api 21,建议应用最小支持 api 23 以上,以便简化外部存储和权限管理。

编解码参数:典型分辨率与帧率组合:1080×1920@30fps、720×1280@30fps;视频码率 4–8 mbps;音频码率 128 kbps;

三、系统权限与用户授权

manifest 权限

<uses-permission android:name="android.permission.foreground_service"/>
<uses-permission android:name="android.permission.write_external_storage"/>
<uses-permission android:name="android.permission.record_audio"/>

运行时授权

android 6.0+ 需请求存储与麦克风权限

通过 mediaprojectionmanager.createscreencaptureintent() 发起系统录屏授权对话框

四、项目架构与流程

用户点击“开始录制”按钮
          │
          ▼
  mainactivity 发起录屏授权 intent
          │
          ▼
  onactivityresult 收到授权 result_ok & data intent
          │
          ▼
  启动 screenrecordservice(前台服务),传入 data
          │
          ▼
  service 中:
  ├─ 初始化 mediarecorder
  ├─ 获取 mediaprojection
  ├─ 创建 virtualdisplay,surface 指向 mediarecorder
  ├─ 调用 mediarecorder.start()
          │
          ▼
  循环录制屏幕与麦克风数据
          │
          ▼
  用户点击“停止录制”
          │
          ▼
  service 调用 mediarecorder.stop(), reset(), release()
  │
  └─ 通知主进程录制完成,保存文件到 mediastore

五、环境配置与依赖

// app/build.gradle
plugins {
  id 'com.android.application'
  id 'kotlin-android'
}
 
android {
  compilesdk 34
  defaultconfig {
    applicationid "com.example.screenrecorder"
    minsdk 23
    targetsdk 34
  }
  buildfeatures { viewbinding true }
}
 
dependencies {
  implementation "androidx.appcompat:appcompat:1.6.1"
  implementation "androidx.core:core-ktx:1.10.1"
  implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"
}

六、完整代码整合

// =======================================================
// 文件:androidmanifest.xml
// 描述:权限申请与 service 声明
// =======================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.screenrecorder">
 
  <uses-permission android:name="android.permission.foreground_service"/>
  <uses-permission android:name="android.permission.write_external_storage"/>
  <uses-permission android:name="android.permission.record_audio"/>
 
  <application
      android:name=".app"
      android:theme="@style/theme.screenrecorder">
    <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:exported="false"/>
  </application>
</manifest>
 
// =======================================================
// 文件:app.kt
// 描述:application,用于初始化通知渠道
// =======================================================
package com.example.screenrecorder
 
import android.app.application
import android.app.notificationchannel
import android.app.notificationmanager
import android.os.build
 
class app : application() {
  companion object { const val channel_id = "screen_recorder_channel" }
  override fun oncreate() {
    super.oncreate()
    if (build.version.sdk_int >= build.version_codes.o) {
      notificationmanager::class.java
        .getsystemservice(notificationmanager::class.java)
        .createnotificationchannel(
          notificationchannel(
            channel_id,
            "screen recorder service",
            notificationmanager.importance_low
          )
        )
    }
  }
}
 
// =======================================================
// 文件:res/values/themes.xml
// 描述:应用主题
// =======================================================
<resources>
  <style name="theme.screenrecorder" parent="theme.materialcomponents.daynight.noactionbar"/>
</resources>
 
// =======================================================
// 文件:res/layout/activity_main.xml
// 描述:主界面,控制录制与播放
// =======================================================
<?xml version="1.0" encoding="utf-8"?>
<framelayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:padding="24dp">
 
  <linearlayout
      android:layout_width="match_parent" android:layout_height="wrap_content"
      android:orientation="vertical" android:gravity="center">
 
    <button
        android:id="@+id/btnstart"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="开始录制"/>
 
    <button
        android:id="@+id/btnstop"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="停止录制"
        android:layout_margintop="16dp"
        android:enabled="false"/>
 
    <textview
        android:id="@+id/tvpath"
        android:layout_width="match_parent" android:layout_height="wrap_content"
        android:layout_margintop="16dp"
        android:text="录制路径:"/>
  </linearlayout>
</framelayout>
 
// =======================================================
// 文件:mainactivity.kt
// 描述:申请权限、发起录屏授权、启动/停止 service
// =======================================================
package com.example.screenrecorder
 
import android.app.activity
import android.content.intent
import android.media.projection.mediaprojectionmanager
import android.os.bundle
import androidx.activity.result.contract.activityresultcontracts
import androidx.appcompat.app.appcompatactivity
import androidx.core.app.activitycompat
import com.example.screenrecorder.databinding.activitymainbinding
 
class mainactivity : appcompatactivity() {
  private lateinit var b: activitymainbinding
  private lateinit var projectionmanager: mediaprojectionmanager
  private var resultcode = activity.result_canceled
  private var resultdata: intent? = null
 
  private val reqstorage = registerforactivityresult(
    activityresultcontracts.requestpermission()
  ) { granted ->
    if (!granted) finish()
  }
 
  private val reqscreen = registerforactivityresult(
    activityresultcontracts.startactivityforresult()
  ) { res ->
    if (res.resultcode == activity.result_ok) {
      resultcode = res.resultcode
      resultdata = res.data
      startservice()
    }
  }
 
  override fun oncreate(s: bundle?) {
    super.oncreate(s)
    b = activitymainbinding.inflate(layoutinflater)
    setcontentview(b.root)
 
    projectionmanager = getsystemservice(media_projection_service)
      as mediaprojectionmanager
 
    reqstorage.launch(android.manifest.permission.write_external_storage)
 
    b.btnstart.setonclicklistener {
      reqscreen.launch(projectionmanager.createscreencaptureintent())
    }
    b.btnstop.setonclicklistener {
      stopservice(intent(this, screenrecordservice::class.java))
    }
  }
 
  private fun startservice() {
    val intent = intent(this, screenrecordservice::class.java).apply {
      putextra("code", resultcode)
      putextra("data", resultdata)
    }
    startservice(intent)
    b.btnstart.isenabled = false
    b.btnstop.isenabled = true
  }
}
 
// =======================================================
// 文件:screenrecordservice.kt
// 描述:前台 service,管理 mediarecorder 与 mediaprojection
// =======================================================
package com.example.screenrecorder
 
import android.app.notification
import android.app.pendingintent
import android.app.service
import android.content.intent
import android.hardware.display.displaymanager
import android.hardware.display.mediaprojection
import android.hardware.display.mediaprojectionmanager
import android.hardware.display.virtualdisplay
import android.media.mediarecorder
import android.os.*
import android.provider.mediastore
import androidx.core.app.notificationcompat
import java.io.file
import java.text.simpledateformat
import java.util.*
 
class screenrecordservice : service() {
  private lateinit var recorder: mediarecorder
  private var projection: mediaprojection? = null
  private var virtualdisplay: virtualdisplay? = null
  private lateinit var projectionmanager: mediaprojectionmanager
  private lateinit var outputfile: string
 
  override fun oncreate() {
    super.oncreate()
    projectionmanager = getsystemservice(media_projection_service)
      as mediaprojectionmanager
    recorder = mediarecorder()
  }
 
  override fun onstartcommand(intent: intent?, flags: int, startid: int): int {
    val code = intent?.getintextra("code", -1) ?: -1
    val data = intent?.getparcelableextra<intent>("data")
    if (code != -1 && data != null) {
      initrecorder()
      projection = projectionmanager.getmediaprojection(code, data)
      virtualdisplay = projection?.createvirtualdisplay(
        "screenrecorder",
        recorder.videowidth, recorder.videoheight, resources.displaymetrics.densitydpi,
        displaymanager.virtual_display_flag_auto_mirror,
        recorder.surface, null, null
      )
      recorder.start()
      startforeground(1, buildnotification())
    }
    return start_not_sticky
  }
 
  private fun initrecorder() {
    val metrics = resources.displaymetrics
    val width = metrics.widthpixels
    val height = metrics.heightpixels
    recorder.apply {
      setaudiosource(mediarecorder.audiosource.mic)
      setvideosource(mediarecorder.videosource.surface)
      setoutputformat(mediarecorder.outputformat.mpeg_4)
      outputfile = createoutputfile()
      setoutputfile(outputfile)
      setvideoencoder(mediarecorder.videoencoder.h264)
      setaudioencoder(mediarecorder.audioencoder.aac)
      setvideosize(width, height)
      setvideoframerate(30)
      setvideoencodingbitrate(8_000_000)
      setorientationhint(0)
      prepare()
    }
  }
 
  private fun createoutputfile(): string {
    val sd = file(getexternalfilesdir(null), "recordvideos")
    if (!sd.exists()) sd.mkdirs()
    val name = "scr_${simpledateformat("yyyymmdd_hhmmss",
      locale.us).format(date())}.mp4"
    return file(sd, name).absolutepath
  }
 
  private fun buildnotification(): notification {
    val pi = pendingintent.getactivity(
      this,0,
      intent(this, mainactivity::class.java), pendingintent.flag_immutable
    )
    return notificationcompat.builder(this, app.channel_id)
      .setcontenttitle("正在录屏")
      .setsmallicon(r.drawable.ic_record)
      .setcontentintent(pi)
      .setongoing(true)
      .build()
  }
 
  override fun ondestroy() {
    super.ondestroy()
    recorder.stop()
    recorder.reset()
    virtualdisplay?.release()
    projection?.stop()
    // 通知系统图库更新文件
    sendbroadcast(intent(intent.action_media_scanner_scan_file).apply {
      data = android.net.uri.fromfile(file(outputfile))
    })
  }
 
  override fun onbind(intent: intent?) = null
}
 
// =======================================================
// 文件:res/drawable/ic_record.xml
// 描述:录制状态图标(示例可用系统资源)
// =======================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="24dp" android:height="24dp" android:viewportwidth="24"
  android:viewportheight="24">
  <path
      android:fillcolor="#f44336"
      android:pathdata="m12,3c7.03,3 3,7.03 3,12s4.03,9 9,9 9,-4.03 9,-9s16.97,3 12,3zm12,19c-3.86,0 -7,-3.14 -7,-7s3.14,-7 7,-7 7,3.14 7,7 -3.14,7 -7,7z"/>
  <circle
      android:fillcolor="#f44336"
      android:cx="12" android:cy="12" android:r="5"/>
</vector>

七、代码解读

1.mainactivity

2.screenrecordservice

3.mediarecorder 配置

4.文件存储

5.通知与图标

八、性能优化与兼容性考虑

分辨率与码率自适应:在高端设备上可录制 1080p 或更高分辨率;在低端或后台时可降级到 720p 以降低 cpu 负载;

动态暂停与恢复:通过 mediarecorder.pause() / resume()(api 24+)实现录制的中断与续录,防止录屏过长文件过大;

存储位置选择:根据 android 10+ scoped storage 模式改用 mediastore.video.media 插入,可让用户在公共相册中看到录制视频;

录制时长限制:在 service 中使用 handler 配合定时逻辑自动停止录制,避免用户忘记关闭;

屏幕方向与旋转处理:使用 setorientationhint() 传入正确的旋转角度,保证录制后视频方向正确;可动态读取 display.getrotation() 值;

多路屏幕录制:如果需要同时录制应用内部视图和全屏,可结合 surfaceview + mediacodec 自定义编码合流;

九、扩展功能

预览与分享:在录制完成后,跳转到 播放页面 预览视频,并提供分享到微信、qq、抖音等链接;

水印与滤镜:在 virtualdisplay 或 mediacodec 编码前,使用 opengl es 在录制帧上叠加文字与图片水印,或添加滤镜效果;

画中画:在录制主屏幕的同时,叠加前置摄像头小窗口,实现游戏解说或教学场景;

轨迹标记:支持用户在录制时绘制屏幕内容,如手绘箭头、圈注关键点,适用于教学与演示;

云端同步:录制完成后自动上传到 oss/s3/腾讯云等对象存储,结合短视频处理平台进行后续剪辑与分发。

十、项目总结与落地建议

本文基于 mediaprojection + mediarecorder 实现了一个完整、可扩展的 android 录屏功能,涵盖:

系统授权与运行时权限管理

前台 service 保证长期录制不中断

高质量音视频同步编码与文件保存

录制状态通知与系统相册更新

在实际项目中,可结合应用业务场景,增添更多人性化功能与企业级需求,如录制回放剪辑、云端上传、画中画、手势标注等,打造更具竞争力的录屏产品。

十一、常见问题解答(faq)

q1:为什么录制内容全黑或黑屏?

需在 mediarecorder 配置好 setvideosource(mediarecorder.videosource.surface) 后再调用 prepare()。

检查是否正确调用 projection.createvirtualdisplay() 并传入 recorder.getsurface()。

q2:录制文件巨大怎么办?

降低视频码率,或增加压缩率。使用高效编码器(h.265)需自行集成 mediacodec + mediamuxer。

q3:如何实现暂停和续录?

android n(api 24)以上,mediarecorder.pause() 与 resume() 可控制录制中断;

旧版本则需停止当前录制并启动下一段分片,后期合并分片文件。

q4:录制静音或无声?

检查麦克风是否被 setaudiosource() 正确调用,应用是否拥有 record_audio 权限;

部分设备后台录音需在 audioattributes 中指定前台模式。

q5:录制过程中怎样显示时长?

在 service 中启动定时器(handler.postdelayed()),定期通过通知或广播更新时长到 ui。

到此这篇关于android实现简单的录屏功能(附源码)的文章就介绍到这了,更多相关android录屏内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

Android开发环境配置避坑指南

05-03

Android实现定时任务的几种方式汇总(附源码)

05-03

Android实现文字滚动播放效果的示例代码

05-06

Android 实现一个隐私弹窗功能

05-06

基于Android实现写字板功能的代码详解

04-29

Android NDK版本迭代与FFmpeg交叉编译完全指南

05-11

猜你喜欢

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

发表评论