创建虚拟显示后调用 release() 不起作用

问题描述

我使用 MediaProjection 创建 Virtualdisplay截取屏幕截图。之后,我尝试释放 virtualdisplay,但没有奏效:

// create virtual display...
mVirtualdisplay = sMediaProjection.createVirtualdisplay(disPLAY,mWidth,mHeight,mDensity,VIRTUAL_disPLAY_FLAGS,mImageReader.getSurface(),null,null);

// release it after taking screenshot successfully
if (mImageReader != null){
    mImageReader.setonImageAvailableListener(null,null);
    if (mImageReader.getSurface() != null) {
        mImageReader.getSurface().release();
    }
    mImageReader.close();
}
if (mVirtualdisplay != null) mVirtualdisplay.release();
if (sMediaProjection != null) sMediaProjection.unregisterCallback(MediaProjectionStopCallback.this);
mVirtualdisplay = null;
mImageReader = null;

几分钟后,我调用了这个函数 displayManager.getdisplays() --> 我看到一些没有被释放的虚拟显示器。

如何彻底释放?有什么我错过的吗?

P/s:很像这个question: Android VirtualDisplay.release() not releasing the display,但我还没有找到解决方案。

解决方法

1 行答案:virtualdisplay.release() 如果您创建的 virtualdisplay 传递一个 null 作为回调参数,则什么都不做。

我发现这是一个非常令人沮丧的问题,因为我在网络上遇到的所有示例都为回调参数传递了 NULL,但在所有示例中,由于不清楚 android,在没有意识到它完全没有任何作用的情况下调用了 release()文档因此内存泄漏。虽然我不得不提到我在旧的 Android 版本中没有发现这个问题

从 VirtualDisplay 源代码中找到答案。 创建 VirtualDisplay 时,您需要创建 VirtualDisplay.Callback 并传递 is 作为参数,而不是 NULL。因为 virtualDisplay.release() 函数检查令牌是否为空。

https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/hardware/display/VirtualDisplay.java

VirtualDisplay.java

   /**
 * Releases the virtual display and destroys its underlying surface.
 * <p>
 * All remaining windows on the virtual display will be forcibly removed
 * as part of releasing the virtual display.
 * </p>
 */
public void release() {
    if (mToken != null) { //mToken is the callback
        mGlobal.releaseVirtualDisplay(mToken);
        mToken = null;
    }
}

所以在调用 createVirtualDisplay 之前,先创建一个 VirtualDisplay.Callback

VirtualDisplay.Callback mVirtualDisplayCallback = new VirtualDisplay.Callback() {
        @Override
        public void onPaused() {
            super.onPaused();
        }

        @Override
        public void onResumed() {
            super.onResumed();
        }

        @Override
        public void onStopped() {
            super.onStopped();
        }
    };
    mVirtualDisplay = mProjection.createVirtualDisplay("screen-mirror",mWidth,mHeight,mDensity,flags,mImageReader.getSurface(),mVirtualDisplayCallback,handler);

如果你去看VirtualDisplay类,你会发现这个变量和构造函数

VirtualDisplay.java

private IVirtualDisplayCallback mToken;

VirtualDisplay(DisplayManagerGlobal global,Display display,IVirtualDisplayCallback token,Surface surface) {
    mGlobal = global;
    mDisplay = display;
    mToken = token;
    mSurface = surface;
}

构造函数上的mToken/VirtualDisplay回调就是release()函数在调用前检查是否为null的那个token

   mGlobal.releaseVirtualDisplay(mToken);

这很令人沮丧,因为 release() 函数文档根本没有提到这一点。

因此要检查此解决方案是否有效,请在释放虚拟显示器之前检查显示器的数量和 id,并在释放虚拟显示器后再次检查。应该释放假定的显示 ID。

 DisplayManager disp = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
    Display[] allDisplays = disp.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
    Log.e(TAG,text);
    Log.e(TAG,"Display Count  " + allDisplays.length);
    for (Display dl : allDisplays) {
        Log.e(TAG,"Display name: " + dl.getName() + " Display id: " + dl.getDisplayId());
    }

enter image description here

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...