You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android MediaProjection内存泄漏求助:复现步骤与规避方法?

Android MediaProjection 内存泄漏问题及排查方案

问题概述

MediaProjection在启动并停止后,会出现内存泄漏问题,导致关联的Service无法被GC回收,LeakCanary可检测到该泄漏。泄漏链路为:Native全局变量 → MediaProjectionCallback → MediaProjection → Context → Service

复现步骤

  • 遵循Android官方MediaProjection使用指引,完成启动、停止MediaProjection的流程即可复现。
  • 核心操作步骤:
    1. 请求屏幕捕获权限:
      startActivityForResult(
          mediaProjectionManager.createScreenCaptureIntent(),
          REQUEST_CODE
      )
      
      (代码位于Demo的MainActivity类中)
    2. 初始化MediaProjection:
      mediaProjection = mediaProjectionManager.getMediaProjection(
          intent.getIntExtra("RESULT_CODE", 0),
          intent.getParcelableExtra<Intent>("DATA")!!
      )
      
      (代码位于Demo的ScreenCaptureService类中)

泄漏追踪信息

LeakCanary检测到的泄漏日志如下:

References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.

625 bytes retained by leaking objects
Displaying only 1 leak trace out of 5 with the same signature
Signature: a986e3ada7a0f3d1cc20672fc5b8f41b2c2189be
┬───
│ GC Root: Global variable in native code
│
├─ android.media.projection.MediaProjection$MediaProjectionCallback instance
│    Leaking: UNKNOWN
│    Retaining 3.2 kB in 27 objects
│    ↓ MediaProjection$MediaProjectionCallback.this$0
│                                              ~~~~~~
├─ android.media.projection.MediaProjection instance
│    Leaking: UNKNOWN
│    Retaining 2.7 kB in 26 objects
│    mContext instance of android.app.ContextImpl
│    ↓ MediaProjection.mContext
│                      ~~~~~~~~
├─ android.app.ContextImpl instance
│    Leaking: UNKNOWN
│    Retaining 1.6 kB in 20 objects
│    mOuterContext instance of rpetrov.test.mediaprojectionleakdemo.ScreenCaptureService
│    ContextImpl.mOuterContext is an instance of rpetrov.test.mediaprojectionleakdemo.ScreenCaptureService
│    ↓ ContextImpl.mOuterContext
│                  ~~~~~~~~~~~~~
╰→ rpetrov.test.mediaprojectionleakdemo.ScreenCaptureService instance
​     Leaking: YES (ObjectWatcher was watching this because rpetrov.test.mediaprojectionleakdemo.ScreenCaptureService
​     received Service#onDestroy() callback and Service not held by ActivityThread)
​     Retaining 125 B in 2 objects
​     key = 68f51a96-1353-4155-b47b-4c9adeee759e
​     watchDurationMillis = 5904
​     retainedDurationMillis = 900
​     mApplication instance of android.app.Application
​     mBase instance of android.app.ContextImpl

源码分析

MediaProjectionManager创建MediaProjection的逻辑

public MediaProjection getMediaProjection(int resultCode, @NonNull Intent resultData) {
    if (resultCode != Activity.RESULT_OK || resultData == null) {
        return null;
    }
    IBinder projection = resultData.getIBinderExtra(EXTRA_MEDIA_PROJECTION);
    if (projection == null) {
        return null;
    }
    return new MediaProjection(mContext, IMediaProjection.Stub.asInterface(projection));
}

MediaProjection构造方法内部逻辑

public MediaProjection(Context context, IMediaProjection impl) {
    mCallbacks = new ArrayMap<Callback, CallbackRecord>();
    mContext = context;
    mImpl = impl;
    try {
        mImpl.start(new MediaProjectionCallback());
    } catch (RemoteException e) {
        throw new RuntimeException("Failed to start media projection", e);
    }
}

泄漏原因:

  1. MediaProjectionCallback是MediaProjection的内部类,会持有外部类MediaProjection的强引用;
  2. MediaProjection持有传入的Context实例(此处为Service的Context);
  3. MediaProjectionCallback被Native层的全局变量引用,导致整个引用链无法被GC回收,最终Service实例泄漏。

可行规避方案

  1. 使用Application Context替代组件Context
    获取MediaProjection时,传入Application Context而非Service/Activity的Context,因为Application Context的生命周期与应用一致,不会因组件销毁导致泄漏:

    mediaProjection = mediaProjectionManager.getMediaProjection(
        resultCode,
        resultData,
        applicationContext // 替换为Application Context
    )
    
  2. 主动销毁MediaProjection并置空引用
    在组件(如Service)的onDestroy方法中,主动调用MediaProjection.stop(),并将引用置空,切断引用链:

    override fun onDestroy() {
        super.onDestroy()
        mediaProjection?.stop()
        mediaProjection = null
    }
    
  3. 避免长期持有MediaProjection引用
    仅在需要屏幕捕获时持有MediaProjection实例,完成操作后立即释放,减少引用持有时间。


内容的提问来源于stack exchange,提问作者Puzirki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 18:06:03