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

2025-05-04 17:50

本文主要是介绍Android实现一键录屏功能(附源码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个...

一、项目介绍

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.WRjsITE_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>
 
// ==================================================javascript=====
// 文件:App.kt
// 描述:Application,用于初始化通知渠道
// =======================================================
package com.example.screenrecorder
 
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.javascriptos.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.NotificationCjavascriptompat
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

  • 通过 MediaProjectionManager 发起录屏授权,使用 ActivityResultContracts.StartActivityForResult 回调获取授权 Intent 与结果码;
  • 请求存储与录音权限,授权后调用 startService() 启动 ScreenRecordService;

2.ScreenRecordService

  • 在 onStartCommand() 中根据授权信息获取 MediaProjection;
  • 调用 MediaRecorder 完成音视频同步编码配置,并通过 MediaProjection.createVirtualDisplay() 将屏幕内容输送到 MediaRecorder.getSurface();
  • 使用 前台 Service(startForeground())提高存活优先级,在通知栏显示录制状态;
  • 在 onDestroy() 中停止录制并释放资源,并发送广播刷新系统图库。

3.MediaRecorder 配置

  • setVideoSource(MediaRecorder.VideoSource.SURFACE):将虚拟屏幕 Surface 作为视频帧输入;
  • 音频源使用麦克风 AudioSource.MIC;
  • 视频编码器 H.264,音频编码器 AAC;
  • 分辨率动态获取当前屏幕分辨率,帧率 30 fps,码率 8 Mbps;

4.文件存储

  • 保存到应用私有外部存储 getExternalFilesDir("RecordVideos"),Android 10+ 可换成 MediaStore 接口;
  • 录制完成后广播 ACTION_MEDIA_SCANNER_SCAN_FILE,让系统图库立即识别新文件。

5.通知与图标

  • 使用 ic_record.xml 简单绘制录制状态图标,也可替换为自己的矢量资源;
  • 通知必须传入前台服务渠道 CHANNEL_ID,并设置 setOngoing(true) 锁定通知。

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

分辨率与码率自适应:在高端设备上可录制 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 保arfdSipOM证长期录制不中断

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

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

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

十一、常见问题解答(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录屏内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于Android实现一键录屏功能(附源码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154475

相关文章

Java根据IP地址实现归属地获取

《Java根据IP地址实现归属地获取》Ip2region是一个离线IP地址定位库和IP定位数据管理框架,这篇文章主要为大家详细介绍了Java如何使用Ip2region实现根据IP地址获取归属地,感兴趣... 目录一、使用Ip2region离线获取1、Ip2region简介2、导包3、下编程载xdb文件4、J

PyQt5+Python-docx实现一键生成测试报告

《PyQt5+Python-docx实现一键生成测试报告》作为一名测试工程师,你是否经历过手动填写测试报告的痛苦,本文将用Python的PyQt5和python-docx库,打造一款测试报告一键生成工... 目录引言工具功能亮点工具设计思路1. 界面设计:PyQt5实现数据输入2. 文档生成:python-

浅析如何使用xstream实现javaBean与xml互转

《浅析如何使用xstream实现javaBean与xml互转》XStream是一个用于将Java对象与XML之间进行转换的库,它非常简单易用,下面将详细介绍如何使用XStream实现JavaBean与... 目录1. 引入依赖2. 定义 JavaBean3. JavaBean 转 XML4. XML 转 J

Android 12解决push framework.jar无法开机的方法小结

《Android12解决pushframework.jar无法开机的方法小结》:本文主要介绍在Android12中解决pushframework.jar无法开机的方法,包括编译指令、框架层和s... 目录1. android 编译指令1.1 framework层的编译指令1.2 替换framework.ja

Flutter实现文字镂空效果的详细步骤

《Flutter实现文字镂空效果的详细步骤》:本文主要介绍如何使用Flutter实现文字镂空效果,包括创建基础应用结构、实现自定义绘制器、构建UI界面以及实现颜色选择按钮等步骤,并详细解析了混合模... 目录引言实现原理开始实现步骤1:创建基础应用结构步骤2:创建主屏幕步骤3:实现自定义绘制器步骤4:构建U

使用Python创建一个功能完整的Windows风格计算器程序

《使用Python创建一个功能完整的Windows风格计算器程序》:本文主要介绍如何使用Python和Tkinter创建一个功能完整的Windows风格计算器程序,包括基本运算、高级科学计算(如三... 目录python实现Windows系统计算器程序(含高级功能)1. 使用Tkinter实现基础计算器2.

SpringBoot中四种AOP实战应用场景及代码实现

《SpringBoot中四种AOP实战应用场景及代码实现》面向切面编程(AOP)是Spring框架的核心功能之一,它通过预编译和运行期动态代理实现程序功能的统一维护,在SpringBoot应用中,AO... 目录引言场景一:日志记录与性能监控业务需求实现方案使用示例扩展:MDC实现请求跟踪场景二:权限控制与

Android开发环境配置避坑指南

《Android开发环境配置避坑指南》本文主要介绍了Android开发环境配置过程中遇到的问题及解决方案,包括VPN注意事项、工具版本统一、Gerrit邮箱配置、Git拉取和提交代码、MergevsR... 目录网络环境:VPN 注意事项工具版本统一:android Studio & JDKGerrit的邮

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

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展