SoFunction
Updated on 2025-05-04

Android realizes one-click screen recording function (with source code)

1. Project introduction

In Android 5.0 (API 21) and above, the system provides the MediaProjection API, allowing applications to record screen content and output to video files under user authorization. Based on this, we can implement a one-click screen recording function without ROOT and system signature. Full features include:

Apply for screen recording permission: Use MediaProjectionManager to trigger the system authorization dialog box

Screen acquisition and encoding: combine MediaProjection, VirtualDisplay and MediaRecorder (or MediaCodec + Muxer) for audio and video synchronous recording

Background service management: Encapsulate the screen recording process in Service or Foreground Service to ensure that the application can still record when it is cut into the background.

User interaction: Control the start, pause and stop of the recording screen through buttons within the application, and provide real-time display of the recording time

File storage and management: Automatically save recording files to external storage or MediaStore, supporting list preview and playback

Performance and compatibility: compatible with different resolutions, adapt to changes in screen direction, optimize video code rate and frame rate, and control the impact of screen recording on system performance

2. Related technologies and principles

technology Function
MediaProjection Provide screen content collection permissions and output to VirtualDisplay
VirtualDisplay Mirror screen content to a specified Surface (for example, the input of MediaRecorder)
MediaRecorder Native audio and video recording API, simplifying synchronous encoding and packaging AV files
MediaCodec + Muxer Manual encoding and packaging for finer granular control (optional advanced solutions)
Service Backstage management of screen recording tasks, and combine front-end services to ensure that recording is not interrupted
MediaStore Android 10+ storage protocol, safely write to public albums
Notification The front desk service needs to continuously display the recording status in the notification bar

API requirements: The earliest API 21 is recommended to apply minimum support API 23 or above to simplify external storage and permission management.

Codec parameters: typical resolution and frame rate combination: 1080×1920@30fps, 720×1280@30fps; video code rate 4–8 Mbps; audio code rate 128 kbps;

3. System permissions and user authorization

Manifest Permissions

<uses-permission android:name=".FOREGROUND_SERVICE"/>
<uses-permission android:name=".WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name=".RECORD_AUDIO"/>

Runtime Authorization

Android 6.0+ requires storage and microphone permissions

Initiate the system screen recording authorization dialog box through ()

4. Project architecture and process

User clicks the "Start Recording" button
          │
          ▼
MainActivity Initiate screen recording authorization Intent
          │
          ▼
onActivityResult Authorization received RESULT_OK & data Intent
          │
          ▼
Start ScreenRecordService (foreground service), pass in data
          │
          ▼
In Service:
├─ Initialize MediaRecorder
├─ Get MediaProjection
├─ Create VirtualDisplay, Surface points to MediaRecorder
├─ Call ()
          │
          ▼
Record screen and microphone data loop
          │
          ▼
The user clicks "Stop recording"
          │
          ▼
Service calls (), reset(), release()
  │
└─ Notify the main process to complete the recording and save the file to MediaStore

5. Environment configuration and dependency

// app/
plugins {
  id ''
  id 'kotlin-android'
}
 
android {
  compileSdk 34
  defaultConfig {
    applicationId ""
    minSdk 23
    targetSdk 34
  }
  buildFeatures { viewBinding true }
}
 
dependencies {
  implementation ":appcompat:1.6.1"
  implementation ":core-ktx:1.10.1"
  implementation ":lifecycle-runtime-ktx:2.6.1"
}

6. Complete code integration

// =======================================================
// document:// Description: Permission application and Service statement// =======================================================
&lt;manifest xmlns:andro
    package=""&gt;
 
  &lt;uses-permission android:name=".FOREGROUND_SERVICE"/&gt;
  &lt;uses-permission android:name=".WRITE_EXTERNAL_STORAGE"/&gt;
  &lt;uses-permission android:name=".RECORD_AUDIO"/&gt;
 
  &lt;application
      android:name=".App"
      android:theme="@style/"&gt;
    &lt;activity android:name=".MainActivity"
        android:exported="true"&gt;
      &lt;intent-filter&gt;
        &lt;action android:name=""/&gt;
        &lt;category android:name=""/&gt;
      &lt;/intent-filter&gt;
    &lt;/activity&gt;
    &lt;service android:name=".ScreenRecordService"
        android:exported="false"/&gt;
  &lt;/application&gt;
&lt;/manifest&gt;
 
// =======================================================
// document:// Description: Application, used to initialize notification channels// =======================================================
package 
 
import 
import 
import 
import 
 
class App : Application() {
  companion object { const val CHANNEL_ID = "screen_recorder_channel" }
  override fun onCreate() {
    ()
    if (.SDK_INT &gt;= Build.VERSION_CODES.O) {
      NotificationManager::
        .getSystemService(NotificationManager::)
        .createNotificationChannel(
          NotificationChannel(
            CHANNEL_ID,
            "Screen Recorder Service",
            NotificationManager.IMPORTANCE_LOW
          )
        )
    }
  }
}
 
// =======================================================
// File: res/values/// Description: Apply topic// =======================================================
&lt;resources&gt;
  &lt;style name="" parent=""/&gt;
&lt;/resources&gt;
 
// =======================================================
// File: res/layout/activity_main.xml// Description: Main interface, control recording and playback// =======================================================
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;FrameLayout xmlns:andro
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:padding="24dp"&gt;
 
  &lt;LinearLayout
      android:layout_width="match_parent" android:layout_height="wrap_content"
      android:orientation="vertical" android:gravity="center"&gt;
 
    &lt;Button
        android:
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="Start recording"/&gt;
 
    &lt;Button
        android:
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="Stop recording"
        android:layout_marginTop="16dp"
        android:enabled="false"/&gt;
 
    &lt;TextView
        android:
        android:layout_width="match_parent" android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Recording Path:"/&gt;
  &lt;/LinearLayout&gt;
&lt;/FrameLayout&gt;
 
// =======================================================
// document:// Description: Apply for permission, initiate screen recording authorization, start/stop Service// =======================================================
package 
 
import 
import 
import 
import 
import 
import 
import 
import 
 
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(
    ()
  ) { granted -&gt;
    if (!granted) finish()
  }
 
  private val reqScreen = registerForActivityResult(
    ()
  ) { res -&gt;
    if ( == Activity.RESULT_OK) {
      resultCode = 
      resultData = 
      startService()
    }
  }
 
  override fun onCreate(s: Bundle?) {
    (s)
    b = (layoutInflater)
    setContentView()
 
    projectionManager = getSystemService(MEDIA_PROJECTION_SERVICE)
      as MediaProjectionManager
 
    (.WRITE_EXTERNAL_STORAGE)
 
     {
      (())
    }
     {
      stopService(Intent(this, ScreenRecordService::))
    }
  }
 
  private fun startService() {
    val intent = Intent(this, ScreenRecordService::).apply {
      putExtra("code", resultCode)
      putExtra("data", resultData)
    }
    startService(intent)
     = false
     = true
  }
}
 
// =======================================================
// document:// Description: Front Desk Service, Manage MediaRecorder and MediaProjection// =======================================================
package 
 
import 
import 
import 
import 
import 
import 
import 
import 
import 
import .*
import 
import 
import 
import 
import .*
 
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() {
    ()
    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&lt;Intent&gt;("data")
    if (code != -1 &amp;&amp; data != null) {
      initRecorder()
      projection = (code, data)
      virtualDisplay = projection?.createVirtualDisplay(
        "ScreenRecorder",
        , , ,
        DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
        , null, null
      )
      ()
      startForeground(1, buildNotification())
    }
    return START_NOT_STICKY
  }
 
  private fun initRecorder() {
    val metrics = 
    val width = 
    val height = 
     {
      setAudioSource()
      setVideoSource()
      setOutputFormat(.MPEG_4)
      outputFile = createOutputFile()
      setOutputFile(outputFile)
      setVideoEncoder(.H264)
      setAudioEncoder()
      setVideoSize(width, height)
      setVideoFrameRate(30)
      setVideoEncodingBitRate(8_000_000)
      setOrientationHint(0)
      prepare()
    }
  }
 
  private fun createOutputFile(): String {
    val sd = File(getExternalFilesDir(null), "RecordVideos")
    if (!()) ()
    val name = "SCR_${SimpleDateFormat("yyyyMMdd_HHmmss",
      ).format(Date())}.mp4"
    return File(sd, name).absolutePath
  }
 
  private fun buildNotification(): Notification {
    val pi = (
      this,0,
      Intent(this, MainActivity::), PendingIntent.FLAG_IMMUTABLE
    )
    return (this, App.CHANNEL_ID)
      .setContentTitle("Recording screen")
      .setSmallIcon(.ic_record)
      .setContentIntent(pi)
      .setOngoing(true)
      .build()
  }
 
  override fun onDestroy() {
    ()
    ()
    ()
    virtualDisplay?.release()
    projection?.stop()
    // Notify the system library to update files    sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).apply {
      data = (File(outputFile))
    })
  }
 
  override fun onBind(intent: Intent?) = null
}
 
// =======================================================
// File: res/drawable/ic_record.xml// Description: Recording status icon (Sample available system resources)// =======================================================
&lt;vector xmlns:andro
  android:width="24dp" android:height="24dp" android:viewportWidth="24"
  android:viewportHeight="24"&gt;
  &lt;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"/&gt;
  &lt;circle
      android:fillColor="#F44336"
      android:cx="12" android:cy="12" android:r="5"/&gt;
&lt;/vector&gt;

7. Code interpretation

  • Initiate screen recording authorization through MediaProjectionManager, and use callbacks to obtain authorization Intent and result code;
  • Request storage and recording permissions, and after authorization, call startService() to start ScreenRecordService;

  • Obtain MediaProjection based on authorized information in onStartCommand();
  • Call MediaRecorder to complete the audio and video synchronous encoding configuration, and transfer the screen content to () through ();
  • Use the front desk service (startForeground()) to increase survival priority and display the recording status in the notification bar;
  • Stop recording in onDestroy() and free resources and send a broadcast to refresh the system gallery.

Configuration

  • setVideoSource(): Enter the virtual screen Surface as a video frame;
  • The audio source uses a microphone;
  • Video encoder H.264, audio encoder AAC;
  • Dynamically obtain the current screen resolution, frame rate 30 fps, and bit rate 8 Mbps;

4. File storage

  • Save to the application private external storage getExternalFilesDir("RecordVideos"), Android 10+ can be replaced with MediaStore interface;
  • After recording is completed, broadcast ACTION_MEDIA_SCANNER_SCAN_FILE, allowing the system gallery to immediately recognize the new file.

5. Notifications and Icons

  • Use ic_record.xml to simply draw the recording status icon, or replace it with your own vector resource;
  • The notification must be passed to the foreground service channel CHANNEL_ID and set setOngoing(true) to lock the notification.

8. Performance optimization and compatibility considerations

Resolution and Bit Rate Adaptive: 1080p or higher can be recorded on high-end devices; downgrade to 720p in low-end or background to reduce CPU load;

Dynamic pause and recovery: The recording interruption and resumption are achieved through () / resume() (API 24+), to prevent the screen recording from being too long and the file is too large;

Storage location selection: Used instead to insert according to Android 10+ Scoped Storage mode, allowing users to see recorded videos in public albums;

Recording duration limit: Use Handler in Service to automatically stop recording with timing logic to avoid users forgetting to close;

Screen direction and rotation processing: Use setOrientationHint() to pass in the correct rotation angle to ensure the correct direction of the video after recording; the () value can be read dynamically;

Multi-channel screen recording: If you need to record the application internal view and full screen at the same time, you can combine SurfaceView + MediaCodec to customize the encoding and convergence;

9. Extended functions

Preview and share: After recording is completed, jump to the play page to preview the video and provide links to WeChat, QQ, Douyin and other sharing;

Watermarks and filters: Use OpenGL ES to overlay text and image watermarks on the recorded frame, or add filter effects before encoding with VirtualDisplay or MediaCodec;

Picture-in-picture: While recording the home screen, superimpose small windows of the front camera to realize game explanation or teaching scenes;

Track marking: supports users to draw screen content during recording, such as hand-drawn arrows and circle key points, suitable for teaching and demonstration;

Cloud synchronization: After recording is completed, it will be automatically uploaded to OSS/S3/Tencent Cloud and other object storage, and will be combined with the short video processing platform for subsequent editing and distribution.

10. Project summary and implementation suggestions

This article is based on MediaProjection + MediaRecorder to implement a complete and extensible Android screen recording function, covering:

System authorization and runtime permission management

Front desk Service ensures long-term recording without interruption

High-quality audio and video synchronous encoding and file saving

Recording status notification and system album update

In actual projects, more humanized functions and enterprise-level needs can be added in combination with application business scenarios, such as recording and playback editing, cloud uploading, picture-in-picture, gesture annotation, etc., to create a more competitive screen recording product.

11. FAQ

Q1: Why is the recording content completely black or black?

You need to configure setVideoSource() in MediaRecorder before calling prepare().

Check whether () is called correctly and pass ().

Q2: What should I do if the recording file is huge?

Reduce the video bit rate, or increase the compression rate. Use of high-efficiency encoder (H.265) requires the integration of MediaCodec + MediaMuxer by itself.

Q3: How to achieve pause and resumption?

Android N (API 24) or above, () and resume() can control recording interrupts;

The old version needs to stop the current recording and start the next shard, and merge the shard files later.

Q4: Recording silent or silent?

Check whether the microphone is correctly called by setAudioSource() and whether the application has RECORD_AUDIO permission;

The background recording of some devices requires the foreground mode to be specified in AudioAttributes.

Q5: How to display the duration during recording?

Start the timer (()) in the Service and update the duration to the UI regularly via notification or broadcast.

This is the end of this article about Android implementing simple screen recording function (with source code). For more related content on Android screen recording, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!