Free tools Windows power users keep installed
One-click scans. No signup required.
Direct answer: Android screen capture that continues after an activity leaves the foreground must run in a user-visible foreground service. The activity obtains fresh MediaProjection consent, starts a service declared with the mediaProjection foreground-service type, and passes the result data to that service. The service registers a projection callback, creates one virtual display connected to your own Surface-based frame pipeline, and releases everything when Android or the user stops projection.
This is not silent capture. The user approves each capture session, Android can revoke it, and createVirtualDisplay() routes frames to a Surface—it does not return a Bitmap or image file by itself.
What “background service” means on modern Android
A process that captures the screen invisibly is not the supported model. In this context, “background service” means a foreground service with a persistent user-visible notification and an active, user-approved MediaProjection session. The activity starts the consent flow; the service owns the projection while the activity is no longer visible.
Android Q (API 29) and newer require projection to run from a foreground service of type mediaProjection. Targets of Android 14 (API 34) and newer have additional ordering and permission rules. A session can end when the user stops sharing, the screen locks, another projection begins, or your process dies.
#1 Best Overall
- Android Screen Recorder
Capture flow at a glance
- Request a new consent Intent with
MediaProjectionManager.createScreenCaptureIntent(). - Show the system consent UI from an Activity and check the result code.
- Start a foreground service using the
mediaProjectiontype. - Give the service the result code and Intent data.
- Inside the service, call
getMediaProjection(), registerMediaProjection.Callback, then create oneVirtualDisplayconnected to your Surface. - Feed Surface frames into a separately designed image encoder/storage pipeline.
- On
onStop(), release the display, Surface and frame resources; require fresh consent for another session.
Declare the service and permissions
Add the base foreground-service permission. If your app targets API 34 or later, add the media-projection-specific permission and type:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<application ...>
<service
android:name=".ScreenCaptureService"
android:exported="false"
android:foregroundServiceType="mediaProjection" />
</application>
Do not treat these declarations as user consent. The system consent dialog is still required before every capture session.
Request consent in the Activity
Use the Activity Result API (or an equivalent result mechanism) and retain the returned result code and data only for the session you are starting. A denied result must stop the flow.
private val projectionLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK || result.data == null) {
// User denied capture; do not start the service.
return@registerForActivityResult
}
val serviceIntent = Intent(this, ScreenCaptureService::class.java).apply {
action = ScreenCaptureService.ACTION_START
putExtra(ScreenCaptureService.EXTRA_RESULT_CODE, result.resultCode)
putExtra(ScreenCaptureService.EXTRA_RESULT_DATA, result.data)
}
ContextCompat.startForegroundService(this, serviceIntent)
}
fun requestScreenCapture() {
val manager = getSystemService(MediaProjectionManager::class.java)
projectionLauncher.launch(manager.createScreenCaptureIntent())
}
On Android 14 and later, start the correctly typed foreground service before the service calls getMediaProjection(). The service should call startForeground() promptly with a notification and the media-projection service type.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #2
- One-Tap Screen Recording: Start and stop recording instantly with a single tap.
- High-Quality Video: Capture your screen in up to 1080p HD resolution and smooth frame rates.
- Audio Recording: Record internal audio and microphone for complete narration or commentary.
- Customizable Settings: Choose your preferred resolution, bitrate, and frame rate for the perfect balance of quality and file size.
- Record with or without Audio: Flexible options for silent or narrated recordings.
Implement the foreground service lifecycle
The following skeleton shows the ordering and teardown. It intentionally leaves frame extraction and file writing behind an explicit interface: those details depend on whether you use an ImageReader, a video encoder, OpenGL, or another Surface consumer and must be threaded and sized for your workload.
class ScreenCaptureService : Service() {
companion object {
const val ACTION_START = "capture.START"
const val ACTION_STOP = "capture.STOP"
const val EXTRA_RESULT_CODE = "result_code"
const val EXTRA_RESULT_DATA = "result_data"
}
private var projection: MediaProjection? = null
private var virtualDisplay: VirtualDisplay? = null
private var captureSurface: Surface? = null
private var framePipeline: FramePipeline? = null
private val projectionCallback = object : MediaProjection.Callback() {
override fun onStop() {
// Android or the user revoked the session.
releaseCapture()
stopSelf()
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
ACTION_START -> startCapture(intent)
ACTION_STOP -> {
releaseCapture()
stopSelf()
}
}
return START_NOT_STICKY
}
private fun startCapture(intent: Intent) {
val notification = buildCaptureNotification()
if (Build.VERSION.SDK_INT >= 29) {
startForeground(
1001,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
)
} else {
startForeground(1001, notification)
}
val resultCode = intent.getIntExtra(EXTRA_RESULT_CODE, Activity.RESULT_CANCELED)
val resultData = intent.getParcelableExtra<Intent>(EXTRA_RESULT_DATA)
?: return stopSelf()
val manager = getSystemService(MediaProjectionManager::class.java)
projection = manager.getMediaProjection(resultCode, resultData)
projection?.registerCallback(projectionCallback, Handler(Looper.getMainLooper()))
// Choose dimensions and create a Surface consumed by your frame pipeline.
val metrics = resources.displayMetrics
val width = metrics.widthPixels
val height = metrics.heightPixels
val density = metrics.densityDpi
framePipeline = FramePipeline(width, height).also { it.start() }
captureSurface = framePipeline!!.surface
virtualDisplay = projection?.createVirtualDisplay(
"ScreenCapture",
width,
height,
density,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
captureSurface,
null,
null
)
}
private fun releaseCapture() {
virtualDisplay?.release()
virtualDisplay = null
framePipeline?.stop()
framePipeline = null
captureSurface?.release()
captureSurface = null
projection?.unregisterCallback(projectionCallback)
projection?.stop()
projection = null
}
override fun onBind(intent: Intent?) : IBinder? = null
override fun onDestroy() {
releaseCapture()
super.onDestroy()
}
}
The callback must be registered before createVirtualDisplay(). Without registration, display creation can fail with IllegalStateException. A stopped projection cannot create another virtual display.
Turn the Surface stream into a screenshot
MediaProjection supplies authorization and display plumbing. It does not hand you a still image. Your Surface consumer must acquire frames, convert or encode them, and persist the result. Common designs include an ImageReader Surface for CPU-readable frames or a hardware/video pipeline for repeated captures. Whichever design you choose, define ownership and threading clearly:
- Close each acquired image and release buffers promptly.
- Keep the producer and consumer dimensions identical, including row stride and pixel format handling.
- Do not block the projection callback or main thread while encoding or writing files.
- Bound queues so a slow disk or encoder cannot exhaust memory.
- Store output in app-private or user-selected storage according to your app’s sharing requirements.
The official projection guidance establishes the session and Surface lifecycle, not one universal still-image extraction recipe. Verify the current API and resource details for your chosen frame reader before shipping.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallRank #3
- ⭐⭐ Read and follow this App institutions ⭐⭐
- ⭐ Open app slide bar section ⭐.
- ⭐ Click settings option ⭐.
- ⭐ We first option see -
- Audio Configuration ⭐.
Full display versus a selected app window
| Scope | Privacy boundary | Initial dimensions | Resize handling |
|---|---|---|---|
| Full device display | Everything the user shares, subject to Android’s capture rules | Use maximum display bounds for initial sizing | Resize the existing display and Surface on configuration changes |
| User-selected app window | Only the window selected in the system dialog | Unknown until selection completes | Handle onCapturedContentResize(), then resize the existing display and Surface |
Android 14 introduced user selection of a single app window. Do not assume its dimensions equal the physical display. For rotation, window changes, or other configuration changes, call VirtualDisplay.resize() and update its Surface rather than creating a second display.
Android 14 session rules
- Each
createVirtualDisplay()call defines a capture session. - For apps targeting API 34 or later, one
MediaProjectioninstance can be used for only one such call. - The consent result and Intent cannot be reused to obtain multiple projection instances; reuse can produce
SecurityException. - The user must grant consent before each capture session.
If capture stops, release all resources and run the Activity consent flow again. Do not try to revive a stopped projection by creating another display from the same token.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshooting
SecurityException from getMediaProjection()
Check that the user approved this session, the service is already running as a media-projection foreground service, and you are not reusing old result data or a projection instance on an API 34-targeting app.
IllegalStateException from display creation
Register MediaProjection.Callback first. Also verify that the projection has not already been stopped and that you create only one virtual display for that session.
Rank #4
- - Record screen without root, unlimited screen recording time.
- - The screen recorder feature is compatible and runs well on all kinds of Android phones such as Samsung, LG, Oppo, Xiaomi, Huawei, etc.
- - Videos recorded by the application have low capacity and high resolution.
- - Manage recorded videos: add, edit, delete, share videos recorded with the application.
- - Useful video editing features Crop video: Cut the part from a video into a new video, Join video: Combine multiple videos into 1 video.
Black, frozen, or empty output
Confirm that a live Surface is attached, the frame consumer is running, and its dimensions match the virtual display. Check that acquired frames are closed and that your encoder or image conversion is not blocking the producer.
Capture ends when the Activity closes
Move projection ownership and frame processing into the foreground service. The Activity should only request consent, pass the result, and provide controls to stop the session.
Wrong size after rotation or window selection
Handle resize callbacks and configuration changes. Resize the existing VirtualDisplay and replace or resize the Surface; do not call createVirtualDisplay() again.
No capture after screen lock or another sharing session
This is an expected revocation path. Release resources in onStop(), notify the user, and require a new consent flow.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Best Value
- High quality recording to movie
- No root required
- Audio recording
- Camera and controls overlay
- Free
Performance, reliability, and privacy checklist
- Use a foreground notification that explains the active capture.
- Capture only the scope the user selected and stop promptly when they revoke it.
- Choose the smallest practical dimensions and frame rate for your output.
- Move image conversion, compression, and file I/O off the main thread.
- Handle process death and service recreation as a new session; do not assume the old token remains valid.
- Test rotation, multi-window mode, app-window selection, screen lock, another projection, denied consent, and low-memory conditions.
Or skip the browser setup
If your actual requirement is a screenshot of a web page rather than the Android device display, ScreenshotNeo avoids MediaProjection entirely. One request returns a PNG, JPEG, WebP, or PDF; cookie and consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status.
See the ScreenshotNeo API documentation for all options. This cURL example captures a page as WebP:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Equivalent Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Equivalent Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Start with a free ScreenshotNeo account.
Frequently Asked Questions
Can MediaProjection capture without showing a notification?
No. Supported background operation requires a user-visible foreground service and an active consent grant.
Recommended Free Tools
Can I reuse one consent result for several screenshots?
For API 34-targeting apps, treat each capture session as one consent and one virtual-display call; request consent again after the session ends.
Does createVirtualDisplay() return a Bitmap?
No. It sends display content to your Surface. Frame acquisition, encoding, and storage are separate application code.
What should happen when onStop() fires?
Release the virtual display, Surface, frame resources, and projection, then require fresh consent before starting again.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




