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

Android實現(xiàn)一鍵錄屏功能(附源碼)

 更新時間:2025年05月04日 09:42:40   作者:Katie。  
在 Android 5.0及以上版本,系統(tǒng)提供了 MediaProjection API,允許應(yīng)用在用戶授權(quán)下錄制屏幕內(nèi)容并輸出到視頻文件,所以本文將基于此實現(xiàn)一個一鍵錄屏功能,感興趣的可以了解下

一、項目介紹

在 Android 5.0(API 21)及以上版本,系統(tǒng)提供了 MediaProjection API,允許應(yīng)用在用戶授權(quán)下錄制屏幕內(nèi)容并輸出到視頻文件?;诖?,我們可以實現(xiàn)一個 一鍵錄屏 功能,無需 ROOT,也無需系統(tǒng)簽名。完整功能包含:

申請錄屏權(quán)限:使用 MediaProjectionManager 觸發(fā)系統(tǒng)授權(quán)對話框

屏幕采集與編碼:結(jié)合 MediaProjection、VirtualDisplay 與 MediaRecorder(或 MediaCodec + Muxer)進行音視頻同步錄制

后臺服務(wù)管理:將錄屏流程封裝在 Service 或 Foreground Service 中,確保在應(yīng)用切后臺時仍可錄制

用戶交互:在應(yīng)用內(nèi)通過按鈕控制錄屏的啟動、暫停、停止,并提供錄制時長實時顯示

文件存儲與管理:自動將錄制文件保存到外部存儲或 MediaStore,支持列表預(yù)覽與播放

性能與兼容:兼容不同分辨率、適配屏幕方向變化,優(yōu)化視頻碼率與幀率,控制錄屏對系統(tǒng)性能的影響

二、相關(guān)技術(shù)與原理

技術(shù)功能
MediaProjection提供屏幕內(nèi)容采集權(quán)限,輸出到 VirtualDisplay
VirtualDisplay將屏幕內(nèi)容鏡像到指定 Surface(例如 MediaRecorder 的輸入)
MediaRecorder原生音視頻錄制 API,簡化同步編碼、封裝 AV 文件
MediaCodec + Muxer手動編碼與封裝,獲得更細粒度控制(可選高級方案)
Service后臺管理錄屏任務(wù),結(jié)合前臺服務(wù)確保錄制不中斷
MediaStoreAndroid 10+ 存儲協(xié)議,安全寫入公共相冊
Notification前臺服務(wù)需在通知欄持續(xù)顯示錄制狀態(tài)

API 要求:最早 API 21,建議應(yīng)用最小支持 API 23 以上,以便簡化外部存儲和權(quán)限管理。

編解碼參數(shù):典型分辨率與幀率組合:1080×1920@30fps、720×1280@30fps;視頻碼率 4–8 Mbps;音頻碼率 128 kbps;

三、系統(tǒng)權(quán)限與用戶授權(quán)

Manifest 權(quán)限

<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"/>

運行時授權(quán)

Android 6.0+ 需請求存儲與麥克風(fēng)權(quán)限

通過 MediaProjectionManager.createScreenCaptureIntent() 發(fā)起系統(tǒng)錄屏授權(quán)對話框

四、項目架構(gòu)與流程

用戶點擊“開始錄制”按鈕
          │
          ▼
  MainActivity 發(fā)起錄屏授權(quán) Intent
          │
          ▼
  onActivityResult 收到授權(quán) RESULT_OK & data Intent
          │
          ▼
  啟動 ScreenRecordService(前臺服務(wù)),傳入 data
          │
          ▼
  Service 中:
  ├─ 初始化 MediaRecorder
  ├─ 獲取 MediaProjection
  ├─ 創(chuàng)建 VirtualDisplay,Surface 指向 MediaRecorder
  ├─ 調(diào)用 MediaRecorder.start()
          │
          ▼
  循環(huán)錄制屏幕與麥克風(fēng)數(shù)據(jù)
          │
          ▼
  用戶點擊“停止錄制”
          │
          ▼
  Service 調(diào)用 MediaRecorder.stop(), reset(), release()
  │
  └─ 通知主進程錄制完成,保存文件到 MediaStore

五、環(huán)境配置與依賴

// 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
// 描述:權(quán)限申請與 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
// 描述:應(yīng)用主題
// =======================================================
<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
// 描述:申請權(quán)限、發(fā)起錄屏授權(quán)、啟動/停止 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()
    // 通知系統(tǒng)圖庫更新文件
    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
// 描述:錄制狀態(tài)圖標(示例可用系統(tǒng)資源)
// =======================================================
<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 發(fā)起錄屏授權(quán),使用 ActivityResultContracts.StartActivityForResult 回調(diào)獲取授權(quán) Intent 與結(jié)果碼;
  • 請求存儲與錄音權(quán)限,授權(quán)后調(diào)用 startService() 啟動 ScreenRecordService;

2.ScreenRecordService

  • 在 onStartCommand() 中根據(jù)授權(quán)信息獲取 MediaProjection;
  • 調(diào)用 MediaRecorder 完成音視頻同步編碼配置,并通過 MediaProjection.createVirtualDisplay() 將屏幕內(nèi)容輸送到 MediaRecorder.getSurface();
  • 使用 前臺 Service(startForeground())提高存活優(yōu)先級,在通知欄顯示錄制狀態(tài);
  • 在 onDestroy() 中停止錄制并釋放資源,并發(fā)送廣播刷新系統(tǒng)圖庫。

3.MediaRecorder 配置

  • setVideoSource(MediaRecorder.VideoSource.SURFACE):將虛擬屏幕 Surface 作為視頻幀輸入;
  • 音頻源使用麥克風(fēng) AudioSource.MIC;
  • 視頻編碼器 H.264,音頻編碼器 AAC;
  • 分辨率動態(tài)獲取當前屏幕分辨率,幀率 30 fps,碼率 8 Mbps;

4.文件存儲

  • 保存到應(yīng)用私有外部存儲 getExternalFilesDir("RecordVideos"),Android 10+ 可換成 MediaStore 接口;
  • 錄制完成后廣播 ACTION_MEDIA_SCANNER_SCAN_FILE,讓系統(tǒng)圖庫立即識別新文件。

5.通知與圖標

  • 使用 ic_record.xml 簡單繪制錄制狀態(tài)圖標,也可替換為自己的矢量資源;
  • 通知必須傳入前臺服務(wù)渠道 CHANNEL_ID,并設(shè)置 setOngoing(true) 鎖定通知。

八、性能優(yōu)化與兼容性考慮

分辨率與碼率自適應(yīng):在高端設(shè)備上可錄制 1080p 或更高分辨率;在低端或后臺時可降級到 720p 以降低 CPU 負載;

動態(tài)暫停與恢復(fù):通過 MediaRecorder.pause() / resume()(API 24+)實現(xiàn)錄制的中斷與續(xù)錄,防止錄屏過長文件過大;

存儲位置選擇:根據(jù) Android 10+ Scoped Storage 模式改用 MediaStore.Video.Media 插入,可讓用戶在公共相冊中看到錄制視頻;

錄制時長限制:在 Service 中使用 Handler 配合定時邏輯自動停止錄制,避免用戶忘記關(guān)閉;

屏幕方向與旋轉(zhuǎn)處理:使用 setOrientationHint() 傳入正確的旋轉(zhuǎn)角度,保證錄制后視頻方向正確;可動態(tài)讀取 Display.getRotation() 值;

多路屏幕錄制:如果需要同時錄制應(yīng)用內(nèi)部視圖和全屏,可結(jié)合 SurfaceView + MediaCodec 自定義編碼合流;

九、擴展功能

預(yù)覽與分享:在錄制完成后,跳轉(zhuǎn)到 播放頁面 預(yù)覽視頻,并提供分享到微信、QQ、抖音等鏈接;

水印與濾鏡:在 VirtualDisplay 或 MediaCodec 編碼前,使用 OpenGL ES 在錄制幀上疊加文字與圖片水印,或添加濾鏡效果;

畫中畫:在錄制主屏幕的同時,疊加前置攝像頭小窗口,實現(xiàn)游戲解說或教學(xué)場景;

軌跡標記:支持用戶在錄制時繪制屏幕內(nèi)容,如手繪箭頭、圈注關(guān)鍵點,適用于教學(xué)與演示;

云端同步:錄制完成后自動上傳到 OSS/S3/騰訊云等對象存儲,結(jié)合短視頻處理平臺進行后續(xù)剪輯與分發(fā)。

十、項目總結(jié)與落地建議

本文基于 MediaProjection + MediaRecorder 實現(xiàn)了一個完整、可擴展的 Android 錄屏功能,涵蓋:

系統(tǒng)授權(quán)與運行時權(quán)限管理

前臺 Service 保證長期錄制不中斷

高質(zhì)量音視頻同步編碼與文件保存

錄制狀態(tài)通知與系統(tǒng)相冊更新

在實際項目中,可結(jié)合應(yīng)用業(yè)務(wù)場景,增添更多人性化功能與企業(yè)級需求,如錄制回放剪輯、云端上傳、畫中畫、手勢標注等,打造更具競爭力的錄屏產(chǎn)品。

十一、常見問題解答(FAQ)

Q1:為什么錄制內(nèi)容全黑或黑屏?

需在 MediaRecorder 配置好 setVideoSource(MediaRecorder.VideoSource.SURFACE) 后再調(diào)用 prepare()。

檢查是否正確調(diào)用 projection.createVirtualDisplay() 并傳入 recorder.getSurface()。

Q2:錄制文件巨大怎么辦?

降低視頻碼率,或增加壓縮率。使用高效編碼器(H.265)需自行集成 MediaCodec + MediaMuxer。

Q3:如何實現(xiàn)暫停和續(xù)錄?

Android N(API 24)以上,MediaRecorder.pause() 與 resume() 可控制錄制中斷;

舊版本則需停止當前錄制并啟動下一段分片,后期合并分片文件。

Q4:錄制靜音或無聲?

檢查麥克風(fēng)是否被 setAudioSource() 正確調(diào)用,應(yīng)用是否擁有 RECORD_AUDIO 權(quán)限;

部分設(shè)備后臺錄音需在 AudioAttributes 中指定前臺模式。

Q5:錄制過程中怎樣顯示時長?

在 Service 中啟動定時器(Handler.postDelayed()),定期通過通知或廣播更新時長到 UI。

到此這篇關(guān)于Android實現(xiàn)簡單的錄屏功能(附源碼)的文章就介紹到這了,更多相關(guān)Android錄屏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論