1 行答案: virtualdisplay.release() 如果创建 virtualdisplay 并将 null 作为回调的参数传递,则什么也不做。
我发现这是一个非常令人沮丧的问题,因为我在 Web 上遇到的所有示例都为回调参数传递了 NULL,但在所有示例中,调用 release() 却没有意识到由于 android 文档不明确而完全没有任何作用,因此内存泄漏。虽然我不得不提一下,但我在旧的 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() 函数在调用之前检查它是否为空的令牌
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());
}