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