Search code examples
androidmemory-leaksandroid-viewandroid-source

When does Android consider a window is leaked?


I checked the Android source code, and found the following method. (core/java/android/view/WindowManagerGlobal.java)

It seems that when who != null the window/view is leaked. Can anyone explain what's going on behind this?

public void closeAll(IBinder token, String who, String what) {
        synchronized (mLock) {
            int count = mViews.size();
            //Log.i("foo", "Closing all windows of " + token);
            for (int i = 0; i < count; i++) {
                //Log.i("foo", "@ " + i + " token " + mParams[i].token
                //        + " view " + mRoots[i].getView());
                if (token == null || mParams.get(i).token == token) {
                    ViewRootImpl root = mRoots.get(i);

                    //Log.i("foo", "Force closing " + root);
                    if (who != null) {
                        WindowLeaked leak = new WindowLeaked(
                                what + " " + who + " has leaked window "
                                + root.getView() + " that was originally added here");
                        leak.setStackTrace(root.getLocation().getStackTrace());
                        Log.e(TAG, "", leak);
                    }

                    removeViewLocked(i, false);
                }
            }
        }
    }

Solution

  • I checked the source... I'm not totally sure about it but her goes my understanding...

    "who" argument is Activity Name only

    Checking closeAll() calling methods, you can see that who is just the Activity class name that was destroyed and left one window behind:

    WindowManagerGlobal.getInstance().closeAll(wtoken,
                            r.activity.getClass().getName(), "Activity");
    

    closeAll() is called if leaked occurred

    It seems that WindowManagerGlobal.closeAll() is called when the Windows has leaked already. So, who != null it just a check to ensure that String is not NULL.

    If not null, a WindowLeaked is created and log is printed. WindowLeaked is an Class that extends AndroidRuntimeException

    final class WindowLeaked extends AndroidRuntimeException {
        public WindowLeaked(String msg) {
            super(msg);
        }
    }
    

    Most important is the fact that if WindowManagerGlobal.closeAll() is called, it means that the Windows has leaked already.

    CloseAll() calling method

    In View.java, we can see that WindowManagerGlobal.closeAll() is called when a leacked is detected:

    ActivityThread.java

    private void handleDestroyActivity(IBinder token, boolean finishing,
                                        int configChanges, boolean getNonConfigInstance) {
        ...
        IBinder wtoken = v.getWindowToken();
        ...
        if (wtoken != null && r.mPendingRemoveWindow == null) {
            WindowManagerGlobal.getInstance().closeAll(wtoken,
                    r.activity.getClass().getName(), "Activity");
        }
    

    In the code above, we can see that WindowManagerGlobal.closeAll() is fired when an inconsistency is found:

    1. wtoken != null wtoken shows that View has an mAttachInfo.mWindowToken information. In other words, it is still held by some window.
    2. r.mPendingRemoveWindow == null there's no pending view to be removed.

    So, it is inconsistent. One view is attached (has an mAttachInfo yet) but I've already remove all pending windows (mPendingRemoveWindow is null)... So, that view has leaked.

    Hope I could help you Regards

    REF:

    WindowManagerGlobal

    ActivityThread

    View