Android高级之WindowManager源码剖析


 本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!


PopupWindow上套PopupWindow,报错:unable to add window ,is your activity running ?


经查在windowManager执行addView操作时出错

[java]  view plain  copy
  1. @Override  
  2. public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {  
  3.     applyDefaultToken(params);  
  4.     mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);  
  5. }  

WindowManagerGlobal
[java]  view plain  copy
  1. public void addView(View view, ViewGroup.LayoutParams params,  
  2.         Display display, Window parentWindow) {  
  3.     if (view == null) {  
  4.         throw new IllegalArgumentException("view must not be null");  
  5.     }  
  6.     if (display == null) {  
  7.         throw new IllegalArgumentException("display must not be null");  
  8.     }  
  9.     if (!(params instanceof WindowManager.LayoutParams)) {  
  10.         throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");  
  11.     }  
  12.   
  13.     // do this last because it fires off messages to start doing things  
  14.     try {  
  15.         root.setView(view, wparams, panelParentView);  
  16.     } catch (RuntimeException e) {  
  17.         // BadTokenException or InvalidDisplayException, clean up.  
  18.         synchronized (mLock) {  
  19.             final int index = findViewLocked(view, false);  
  20.             if (index >= 0) {  
  21.                 removeViewLocked(index, true);  
  22.             }  
  23.         }  
  24.         throw e;  
  25.     }  
  26. }  
ViewRootImpl出错出现ADD_BAD_APP_TOKEN或ADD_BAD_SUBWINDOW_TOKEN,要查WindowSession.addToDisplay
[java]  view plain  copy
  1. /** 
  2.     * We have one child 
  3.     */  
  4.    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {  
  5.        synchronized (this) {  
  6.            if (mView == null) {  
  7.                mView = view;  
  8.   
  9.                int res; /* = WindowManagerImpl.ADD_OKAY; */  
  10.   
  11.                // Schedule the first layout -before- adding to the window  
  12.                // manager, to make sure we do the relayout before receiving  
  13.                // any other events from the system.  
  14.                requestLayout();  
  15.                if ((mWindowAttributes.inputFeatures  
  16.                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {  
  17.                    mInputChannel = new InputChannel();  
  18.                }  
  19.                mForceDecorViewVisibility = (mWindowAttributes.privateFlags  
  20.                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;  
  21.                try {  
  22.                    mOrigWindowType = mWindowAttributes.type;  
  23.                    mAttachInfo.mRecomputeGlobalAttributes = true;  
  24.                    collectViewAttributes();  
  25.                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,  
  26.                            getHostVisibility(), mDisplay.getDisplayId(),  
  27.                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,  
  28.                            mAttachInfo.mOutsets, mInputChannel);  
  29.                } catch (RemoteException e) {  
  30.                    mAdded = false;  
  31.                    mView = null;  
  32.                    mAttachInfo.mRootView = null;  
  33.                    mInputChannel = null;  
  34.                    mFallbackEventHandler.setView(null);  
  35.                    unscheduleTraversals();  
  36.                    setAccessibilityFocus(nullnull);  
  37.                    throw new RuntimeException("Adding window failed", e);  
  38.                } finally {  
  39.                    if (restore) {  
  40.                        attrs.restore();  
  41.                    }  
  42.                }  
  43.   
  44.                if (mTranslator != null) {  
  45.                    mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);  
  46.                }  
  47.                mPendingOverscanInsets.set(0000);  
  48.                mPendingContentInsets.set(mAttachInfo.mContentInsets);  
  49.                mPendingStableInsets.set(mAttachInfo.mStableInsets);  
  50.                mPendingVisibleInsets.set(0000);  
  51.                mAttachInfo.mAlwaysConsumeNavBar =  
  52.                        (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;  
  53.                mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;  
  54.                if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);  
  55.                if (res < WindowManagerGlobal.ADD_OKAY) {  
  56.                    mAttachInfo.mRootView = null;  
  57.                    mAdded = false;  
  58.                    mFallbackEventHandler.setView(null);  
  59.                    unscheduleTraversals();  
  60.                    setAccessibilityFocus(nullnull);  
  61.                    switch (res) {  
  62.                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:  
  63.                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:  
  64.                            throw new WindowManager.BadTokenException(  
  65.                                    "Unable to add window -- token " + attrs.token  
  66.                                    + " is not valid; is your activity running?");  
  67.                        case WindowManagerGlobal.ADD_NOT_APP_TOKEN:  
  68.                            throw new WindowManager.BadTokenException(  
  69.                                    "Unable to add window -- token " + attrs.token  
  70.                                    + " is not for an application");  
  71.                        case WindowManagerGlobal.ADD_APP_EXITING:  
  72.                            throw new WindowManager.BadTokenException(  
  73.                                    "Unable to add window -- app for token " + attrs.token  
  74.                                    + " is exiting");  
  75.                        case WindowManagerGlobal.ADD_DUPLICATE_ADD:  
  76.                            throw new WindowManager.BadTokenException(  
  77.                                    "Unable to add window -- window " + mWindow  
  78.                                    + " has already been added");  
  79.                        case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:  
  80.                            // Silently ignore -- we would have just removed it  
  81.                            // right away, anyway.  
  82.                            return;  
  83.                        case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:  
  84.                            throw new WindowManager.BadTokenException("Unable to add window "  
  85.                                    + mWindow + " -- another window of type "  
  86.                                    + mWindowAttributes.type + " already exists");  
  87.                        case WindowManagerGlobal.ADD_PERMISSION_DENIED:  
  88.                            throw new WindowManager.BadTokenException("Unable to add window "  
  89.                                    + mWindow + " -- permission denied for window type "  
  90.                                    + mWindowAttributes.type);  
  91.                        case WindowManagerGlobal.ADD_INVALID_DISPLAY:  
  92.                            throw new WindowManager.InvalidDisplayException("Unable to add window "  
  93.                                    + mWindow + " -- the specified display can not be found");  
  94.                        case WindowManagerGlobal.ADD_INVALID_TYPE:  
  95.                            throw new WindowManager.InvalidDisplayException("Unable to add window "  
  96.                                    + mWindow + " -- the specified window type "  
  97.                                    + mWindowAttributes.type + " is not valid");  
  98.                    }  
  99.                    throw new RuntimeException(  
  100.                            "Unable to add window -- unknown error code " + res);  
  101.                }  
  102.        }  
  103.    }  

WindowSession

[java]  view plain  copy
  1. @Override  
  2. public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,  
  3.         int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,  
  4.         InputChannel outInputChannel) {  
  5.     return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,  
  6.             outContentInsets, outStableInsets, outInputChannel);  
  7. }  

WindowManagerService

[java]  view plain  copy
  1. public int addWindow(Session session, IWindow client, int seq,  
  2.            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,  
  3.            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,  
  4.            InputChannel outInputChannel) {  
  5.        int[] appOp = new int[1];  
  6.        int res = mPolicy.checkAddPermission(attrs, appOp);  
  7.        if (res != WindowManagerGlobal.ADD_OKAY) {  
  8.            return res;  
  9.        }  
  10.   
  11.        boolean reportNewConfig = false;  
  12.        WindowState attachedWindow = null;  
  13.        long origId;  
  14.        final int type = attrs.type;  
  15.   
  16.        synchronized(mWindowMap) {  
  17.            if (!mDisplayReady) {  
  18.                throw new IllegalStateException("Display has not been initialialized");  
  19.            }  
  20.   
  21.            final DisplayContent displayContent = getDisplayContentLocked(displayId);  
  22.            if (displayContent == null) {  
  23.                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "  
  24.                        + displayId + ".  Aborting.");  
  25.                return WindowManagerGlobal.ADD_INVALID_DISPLAY;  
  26.            }  
  27.            if (!displayContent.hasAccess(session.mUid)) {  
  28.                Slog.w(TAG_WM, "Attempted to add window to a display for which the application "  
  29.                        + "does not have access: " + displayId + ".  Aborting.");  
  30.                return WindowManagerGlobal.ADD_INVALID_DISPLAY;  
  31.            }  
  32.   
  33.            if (mWindowMap.containsKey(client.asBinder())) {  
  34.                Slog.w(TAG_WM, "Window " + client + " is already added");  
  35.                return WindowManagerGlobal.ADD_DUPLICATE_ADD;  
  36.            }  
  37.   
  38.            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {  
  39.                attachedWindow = windowForClientLocked(null, attrs.token, false);  
  40.                if (attachedWindow == null) {  
  41.                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "  
  42.                          + attrs.token + ".  Aborting.");  
  43.                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;  
  44.                }  
  45.                if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW  
  46.                        && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {  
  47.                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "  
  48.                            + attrs.token + ".  Aborting.");  
  49.                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;  
  50.                }  
  51.            }  
  52.   
  53.            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {  
  54.                Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display.  Aborting.");  
  55.                return WindowManagerGlobal.ADD_PERMISSION_DENIED;  
  56.            }  
  57.   
  58.            boolean addToken = false;  
  59.            WindowToken token = mTokenMap.get(attrs.token);  
  60.            AppWindowToken atoken = null;  
  61.            if (token == null) {  
  62.                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {  
  63.                    Slog.w(TAG_WM, "Attempted to add application window with unknown token "  
  64.                          + attrs.token + ".  Aborting.");  
  65.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  66.                }  
  67.                if (type == TYPE_INPUT_METHOD) {  
  68.                    Slog.w(TAG_WM, "Attempted to add input method window with unknown token "  
  69.                          + attrs.token + ".  Aborting.");  
  70.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  71.                }  
  72.                if (type == TYPE_VOICE_INTERACTION) {  
  73.                    Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "  
  74.                          + attrs.token + ".  Aborting.");  
  75.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  76.                }  
  77.                if (type == TYPE_WALLPAPER) {  
  78.                    Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "  
  79.                          + attrs.token + ".  Aborting.");  
  80.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  81.                }  
  82.                if (type == TYPE_DREAM) {  
  83.                    Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "  
  84.                          + attrs.token + ".  Aborting.");  
  85.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  86.                }  
  87.                if (type == TYPE_QS_DIALOG) {  
  88.                    Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "  
  89.                          + attrs.token + ".  Aborting.");  
  90.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  91.                }  
  92.                if (type == TYPE_ACCESSIBILITY_OVERLAY) {  
  93.                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "  
  94.                            + attrs.token + ".  Aborting.");  
  95.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  96.                }  
  97.                token = new WindowToken(this, attrs.token, -1false);  
  98.                addToken = true;  
  99.            } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {  
  100.                atoken = token.appWindowToken;  
  101.                if (atoken == null) {  
  102.                    Slog.w(TAG_WM, "Attempted to add window with non-application token "  
  103.                          + token + ".  Aborting.");  
  104.                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;  
  105.                } else if (atoken.removed) {  
  106.                    Slog.w(TAG_WM, "Attempted to add window with exiting application token "  
  107.                          + token + ".  Aborting.");  
  108.                    return WindowManagerGlobal.ADD_APP_EXITING;  
  109.                }  
  110.                if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {  
  111.                    // No need for this guy!  
  112.                    if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v(  
  113.                            TAG_WM, "**** NO NEED TO START: " + attrs.getTitle());  
  114.                    return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;  
  115.                }  
  116.            } else if (type == TYPE_INPUT_METHOD) {  
  117.                if (token.windowType != TYPE_INPUT_METHOD) {  
  118.                    Slog.w(TAG_WM, "Attempted to add input method window with bad token "  
  119.                            + attrs.token + ".  Aborting.");  
  120.                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  121.                }  
  122.            } else if (type == TYPE_VOICE_INTERACTION) {  
  123.                if (token.windowType != TYPE_VOICE_INTERACTION) {  
  124.                    Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "  
  125.                            + attrs.token + ".  Aborting.");  
  126.                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  127.                }  
  128.            } else if (type == TYPE_WALLPAPER) {  
  129.                if (token.windowType != TYPE_WALLPAPER) {  
  130.                    Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "  
  131.                            + attrs.token + ".  Aborting.");  
  132.                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  133.                }  
  134.            } else if (type == TYPE_DREAM) {  
  135.                if (token.windowType != TYPE_DREAM) {  
  136.                    Slog.w(TAG_WM, "Attempted to add Dream window with bad token "  
  137.                            + attrs.token + ".  Aborting.");  
  138.                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  139.                }  
  140.            } else if (type == TYPE_ACCESSIBILITY_OVERLAY) {  
  141.                if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {  
  142.                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "  
  143.                            + attrs.token + ".  Aborting.");  
  144.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  145.                }  
  146.            } else if (type == TYPE_QS_DIALOG) {  
  147.                if (token.windowType != TYPE_QS_DIALOG) {  
  148.                    Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "  
  149.                            + attrs.token + ".  Aborting.");  
  150.                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;  
  151.                }  
  152.            } else if (token.appWindowToken != null) {  
  153.                Slog.w(TAG_WM, "Non-null appWindowToken for system window of type=" + type);  
  154.                // It is not valid to use an app token with other system types; we will  
  155.                // instead make a new token for it (as if null had been passed in for the token).  
  156.                attrs.token = null;  
  157.                token = new WindowToken(thisnull, -1false);  
  158.                addToken = true;  
  159.            }  
[java]  view plain  copy
  1. if (addToken) {  
  2.     mTokenMap.put(attrs.token, token);  
  3. }  
  4. win.attach();  
  5. mWindowMap.put(client.asBinder(), win);  
  6. if (win.mAppOp != AppOpsManager.OP_NONE) {  
  7.     int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),  
  8.             win.getOwningPackage());  
  9.     if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&  
  10.             (startOpResult != AppOpsManager.MODE_DEFAULT)) {  
  11.         win.setAppOpVisibilityLw(false);  
  12.     }  
  13. }  

[java]  view plain  copy
  1.     Binder.restoreCallingIdentity(origId);  
  2.   
  3.     return res;  
  4. }  
如果加入成功(mWindowMap.put(attr.token,token)),则子View与父View建立连接关系。

查到mTokenMap.get(attr.token)为null,同时又是子窗口,因此报错。

至于为什么是null,继续

回到PopupWindow

[java]  view plain  copy
  1. public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {  
  2.     if (isShowing() || mContentView == null) {  
  3.         return;  
  4.     }  
  5.   
  6.     TransitionManager.endTransitions(mDecorView);  
  7.   
  8.     attachToAnchor(anchor, xoff, yoff, gravity);  
  9.   
  10.     mIsShowing = true;  
  11.     mIsDropdown = true;  
  12.   
  13.     final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());  
  14.     preparePopup(p);  
  15.   
  16.     final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,  
  17.             p.width, p.height, gravity);  
  18.     updateAboveAnchor(aboveAnchor);  
  19.     p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;  
  20.   
  21.     invokePopup(p);  
  22. }  

可以看到,二次添加的PopupWidow,使用一次添加的PopupWindow对象的token,而此token为

[java]  view plain  copy
  1. private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;  
[java]  view plain  copy
  1. private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {  
  2.     final WindowManager.LayoutParams p = new WindowManager.LayoutParams();  
  3.   
  4.     // These gravity settings put the view at the top left corner of the  
  5.     // screen. The view is then positioned to the appropriate location by  
  6.     // setting the x and y offsets to match the anchor's bottom-left  
  7.     // corner.  
  8.     p.gravity = computeGravity();  
  9.     p.flags = computeFlags(p.flags);  
  10.     p.type = mWindowLayoutType;  
  11.     p.token = token;  
  12.     p.softInputMode = mSoftInputMode;  
  13.     p.windowAnimations = computeAnimationResource();  
  14.     return p;  
  15. }  

但在WindowManagerService里,PopupWindow依赖show的控件是传过来的,而一次PopupWindow的type刚好等于

[java]  view plain  copy
  1. /** 
  2.      * Window type: a panel on top of an application window.  These windows 
  3.      * appear on top of their attached window. 
  4.      */  
  5.     public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;  
所在WindowManagerService不允许在子窗口上创建子窗口。
[java]  view plain  copy
  1. if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {  
  2.     attachedWindow = windowForClientLocked(null, attrs.token, false);  
  3.     if (attachedWindow == null) {  
  4.         Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "  
  5.               + attrs.token + ".  Aborting.");  
  6.         return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;  
  7.     }  
  8.     if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW  
  9.             && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {  
  10.         Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "  
  11.                 + attrs.token + ".  Aborting.");  
  12.         return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;  
  13.     }  
  14. }  

完。


补充:Dialog则无此限制

[java]  view plain  copy
  1. public void show() {  
  2.     if (mShowing) {  
  3.         if (mDecor != null) {  
  4.             if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {  
  5.                 mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);  
  6.             }  
  7.             mDecor.setVisibility(View.VISIBLE);  
  8.         }  
  9.         return;  
  10.     }  
  11.   
  12.     WindowManager.LayoutParams l = mWindow.getAttributes();  
  13.     if ((l.softInputMode  
  14.             & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {  
  15.         WindowManager.LayoutParams nl = new WindowManager.LayoutParams();  
  16.         nl.copyFrom(l);  
  17.         nl.softInputMode |=  
  18.                 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;  
  19.         l = nl;  
  20.     }  
  21.   
  22.     mWindowManager.addView(mDecor, l);  
  23.     mShowing = true;  
  24.   
  25.     sendShowMessage();  
  26. }  
[java]  view plain  copy
  1. Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {  
  2.   
  3.     final Window w = new PhoneWindow(mContext);  
  4.     mWindow = w;  
  5.   
  6. }  

PhoneWindow

[java]  view plain  copy
  1. public final WindowManager.LayoutParams getAttributes() {  
  2.     return mWindowAttributes;  
  3. }  
[java]  view plain  copy
  1. private final WindowManager.LayoutParams mWindowAttributes =  
  2.     new WindowManager.LayoutParams();  
[java]  view plain  copy
  1. public LayoutParams() {  
  2.     super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);  
  3.     type = TYPE_APPLICATION;  
  4.     format = PixelFormat.OPAQUE;  
  5. }  
[java]  view plain  copy
  1. public static final int TYPE_APPLICATION        = 2;  

WindowManagerService要求大于等1000,小于等于1999才报ADD_BAD_SUB_WINDOW。

因此Dialog无此限制,所以PopupWindow上弹Dialog即可解决此问题。

猜你喜欢

转载自blog.csdn.net/liuxian13183/article/details/80563147