正确使用WindowManager.removeViewImmediate()

Ben*_* P. 5 android android-windowmanager

的文档WindowManager.removeViewImmediate()包含警告(重点为我):

它的特殊变体在返回之前ViewManager.removeView(View)立即调用给定视图层次结构的View.onDetachedFromWindow()方法。这不是正常应用;正确使用它需要非常小心。

我很好奇“精心护理”在这里意味着什么。大概这表明除非我知道如何处理使用该方法的所有副作用,否则我不应该调用此方法……但是我什至不知道这些副作用可能是什么。


考虑以下活动,我尽可能将其制成。没有遗漏的代码(除了导入),并且Activity的主题只是一个普通的AppCompat主题:

public class MainActivity extends AppCompatActivity {

    private View overlay;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getWindow().getDecorView().post(() -> {
            overlay = new View(this);
            overlay.setBackgroundColor(Color.RED);

            WindowManager.LayoutParams params =
                    new WindowManager.LayoutParams(WindowManager.LayoutParams.FLAG_FULLSCREEN);

            getWindowManager().addView(overlay, params);
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getWindowManager().removeView(overlay);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行我的应用程序并将设备从纵向旋转到横向(或导致其被破坏和重新创建的任何其他方式)时,这将显示在日志中:

2018-10-09 13:58:02.162 11270-11270/com.example.stackoverflow E/WindowManager: android.view.WindowLeaked: Activity com.example.stackoverflow.MainActivity has leaked window android.view.View{e99861 V.ED..... ........ 0,0-1080,1920} that was originally added here
        at android.view.ViewRootImpl.<init>(ViewRootImpl.java:511)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:346)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
        at com.example.stackoverflow.MainActivity.lambda$onCreate$0(MainActivity.java:25)
Run Code Online (Sandbox Code Playgroud)

但是,如果我onDestroy()改为使用方法removeViewImmedate(),则此错误永远不会出现在日志中。不过,我很犹豫,因为我不知道切换这些呼叫还会影响什么。仅仅说“有效”不足以让我放心。

The*_*rer 0

WinowManager是由WindowManagerImpl实现的。

WindowManagerImpl#removeViewImmediate(View)并且WindowManagerImpl#removeView(View)都调用WindowManagerGlobal#removeView(View, boolean).

不同之处在于removeViewImmediate()传递true给该布尔参数,该参数告诉 WindowManagerGlobal 应立即删除该 View 而不是等待。

但这可能是不言自明的。

我能想到的唯一可能导致立即删除问题的事情是奇怪的绘图错误。虽然对源方法的评论如下:

/** * @paramimmediate True,如果不在遍历中,则立即执行。错误,放入队列,稍后再做。* @return True,请求已排队。错误,请求已完成。*/

这没有多大意义。

老实说,如果它不会导致崩溃,而是消除泄漏警告,那么您不妨使用它。

WindowManagerGlobal#removeViewLocked(视图,布尔值)

ViewRootImpl#die(布尔值)