Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
866 views
in Technique[技术] by (71.8m points)

android - call release() did not work after creating virtual display

I used MediaProjection to create VirtualDisplay to take screenshots. Afterward, I tried to release the virtualDisplay, but it did not work:

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

After a few minutes, I called this function displayManager.getDisplays() --> I saw a few virtual displays that did not get released.

How to release it completely? Is there anything I missed?

P/s: it's pretty much like this question: Android VirtualDisplay.release() not releasing the display, but I could not find the solution yet.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

1 line answer : virtualdisplay.release() do nothing if you create the virtualdisplay passing a null as the parameter for the callback.

I find this is a very frustating problem because all of the samples I came across on the web pass NULL for the callback parameter and yet in all of the samples, call release() without realizing it does absolutely nothing due to unclear android documentation thus the memory leak. Though I do have to mention I didn't find this problem in older Android versions

Found the answer from VirtualDisplay source code. When you are creating a VirtualDisplay, you NEED to make a VirtualDisplay.Callback and pass is as a parameter, not a NULL. because virtualDisplay.release() function checks if the token is null or not.

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;
    }
}

So before you call createVirtualDisplay, make a 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);

if you go see the VirtualDisplay class, you will find this variable and constructor

VirtualDisplay.java

private IVirtualDisplayCallback mToken;

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

the mToken / VirtualDisplay callback on the constructor is that token that the release() function checks on whether it is null or not before calling

   mGlobal.releaseVirtualDisplay(mToken);

This is very frustating on because release() function documentation doesn't mention this at all.

So to check whether this solution works, check the number of displays and the ids before releasing the virtual display and check again after releasing it. The supposed display id should be released.

 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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...