Android實現(xiàn)屏幕錄制與本地保存功能的完整指南
一、實現(xiàn)原理概述
Android 屏幕錄制主要依賴以下幾個核心組件:
- MediaProjection:獲取屏幕內容的入口,出于安全和隱私的考慮,每次錄制前,系統(tǒng)都會彈出一個對話框,明確請求用戶的授權。
- MediaProjectionManager: 管理MediaProjection實例
- VirtualDisplay:虛擬顯示設備,將屏幕內容投射到編碼器
- MediaRecorder: 負責錄制和編碼
由于屏幕錄制通常是持續(xù)性任務,即使用戶切換到其他應用或返回桌面,錄制也應繼續(xù)。因此,我們必須將錄制邏輯放置在前臺服務 (Foreground Service) 中。 這不僅能防止我們的應用在后臺被系統(tǒng)終止,還能通過一個持續(xù)的通知告知用戶,屏幕正在被錄制,保證了操作的透明性。
二、環(huán)境準備
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 創(chuàng)建一個 Intent,然后啟動這個 Intent 來顯示一個系統(tǒng)對話框。
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.創(chuàng)建并實現(xiàn)前臺服務
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)建通知并啟動前臺服務
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)
// 創(chuàng)建一個虛擬顯示 (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完成了基本的屏幕錄制功能,后續(xù)還可以結合音視頻編碼將屏幕錄制的數據利用RTMP推流到服務端實現(xiàn)錄屏直播功能。
到此這篇關于Android實現(xiàn)屏幕錄制與本地保存功能的完整指南的文章就介紹到這了,更多相關Android屏幕錄制與本地保存內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
利用kotlin實現(xiàn)統(tǒng)計文件字符個數的方法示例
最近在學習kotlin,發(fā)現(xiàn)了一些不錯的小技巧,所以下面這篇文章主要給大家介紹了關于利用kotlin實現(xiàn)統(tǒng)計文件字符個數的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧。2017-12-12
Android高效加載大圖、多圖解決方案 有效避免程序OOM
這篇文章主要為大家詳細介紹了Android高效加載大圖、多圖解決方案,有效避免程序OOM,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10

