亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android中WindowManager與WMS的解析

2019-10-21 21:30:21
字體:
來源:轉載
供稿:網友

最近在改bug的時候發現在windowManager.addView的時候會發生莫名其妙的崩潰,那個崩潰真的是讓你心態爆炸,潛心研究了兩天window相關的東西,雖然不是很深奧的東西,本人也只是弄清楚了window的添加邏輯,在此分享給大家:

一、懸浮窗的概念

在android中,無論我們的app界面,還是系統桌面,再或者是手機下方的幾個虛擬按鍵和最上方的狀態欄,又或者是一個吐司。。。我們所看到的所有界面,都是由一個個懸浮窗口組成的。

但是這些窗口有不同的級別:

  1. 系統的是老大,是最高級別,你沒見過你下載的什么app把你的下拉菜單蓋住了吧-。=
  2. 其次是每一個應用,都有自己的一個應用級別窗口。
  3. 在應用之內能創建好多的界面,所以還有一種是應用內的窗口。

基于上述三種,android把懸浮窗劃分成三個級別,并通過靜態int型變量來表示:

    /**     * Start of system-specific window types. These are not normally     * created by applications.     **/    public static final int FIRST_SYSTEM_WINDOW   = 2000;    /**     * End of types of system windows.     **/    public static final int LAST_SYSTEM_WINDOW   = 2999;

2000~2999:在系統級別的懸浮窗范圍內,一般我們要想創建是需要申請權限。

    public static final int FIRST_SUB_WINDOW = 1000;    /**     * End of types of sub-windows.     **/    public static final int LAST_SUB_WINDOW = 1999;

1000~1999:子窗口級別的懸浮窗,他如果想要創建必須在一個父窗口下。

public static final int TYPE_BASE_APPLICATION  = 1;public static final int LAST_APPLICATION_WINDOW = 99;

1~99:應用程序級別的懸浮窗,作為每個應用程序的基窗口。

在每段的范圍內都有眾多個窗口類型,這個具體就不說了,因為太多了根本說不完。。

但是說了這么半天,懸浮窗到底是個啥東西,可能這個名詞聽得很多,但是仔細想想android中用到的哪個控件還是哪個類叫懸浮窗?沒有吧,那么View總該知道吧(不知道別說你是做android的)

其實說白了懸浮窗就是一個被包裹的view。因為除了一個view他還有很多的屬性:長寬深度,類型,證書等等東西,只是屬性很多而且屬性之間的依賴關系有一些復雜而已。簡單的來說可以這么理解。

二、WindowManager介紹

上面簡單介紹了懸浮窗的概念,而WindowManager是對懸浮窗進行操作的一個媒介。

WindowManager是一個接口,他是繼承了ViewManager接口中的三個方法:

public interface ViewManager{  public void addView(View view, ViewGroup.LayoutParams params);  public void updateViewLayout(View view, ViewGroup.LayoutParams params);  public void removeView(View view);}

windowManage暴露給我們的只是這個三個方法,真的是簡單粗暴,但是很實用。

這三個方法看名字就知道含義了,增刪改嘛,就不多說啦。

而在上面提到的對于懸浮窗的三種分類,也是WindowManager的內部類:WindowManager.LayoutParams,關于LayoutParams是什么在這里就不多說了。這不是我們的重點。

我們平時想要添加一個懸浮窗,就會使用第一個方法:

  WindowManager windowManager = getWindowManager();  windowManager.addView(.....);

我們在getWindowManager獲取的類,實際上是WindowManager的是WindowManager的實現類:WindowManagerImpl。接下來我們走一下添加懸浮窗的流程。

三、懸浮窗添加流程

入口肯定是從自己的addView中,上面說到了WindowManager的實現類是WindowManagerImpl,來看一下:

    @Override    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {    applyDefaultToken(params);    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);  }

這里有兩步:第一步是給layoutparams設置一個默認的令牌(就是token這個屬性,至于這個干什么的等會再說)

  private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {    // 設置條件:有默認令牌,而且不是子窗口級別的懸浮窗    if (mDefaultToken != null && mParentWindow == null) {      if (!(params instanceof WindowManager.LayoutParams)) {        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");      }      // 如果沒有令牌就設置默認令牌      final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;      if (wparams.token == null) {        wparams.token = mDefaultToken;      }    }  }

然后調用了mGlobal的addView:

  public void addView(View view, ViewGroup.LayoutParams params,      Display display, Window parentWindow) {    /**進行一系列判空操作。。。**/    if (parentWindow != null) {      parentWindow.adjustLayoutParamsForSubWindow(wparams);    } else {      // If there's no parent, then hardware acceleration for this view is      // set from the application's hardware acceleration setting.      final Context context = view.getContext();      if (context != null          && (context.getApplicationInfo().flags              & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {        wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;      }    }    ViewRootImpl root;      root = new ViewRootImpl(view.getContext(), display);      view.setLayoutParams(wparams);      mViews.add(view);      mRoots.add(root);      mParams.add(wparams);      // do this last because it fires off messages to start doing things      try {        root.setView(view, wparams, panelParentView);      } catch (RuntimeException e) {        // BadTokenException or InvalidDisplayException, clean up.        if (index >= 0) {          removeViewLocked(index, true);        }        throw e;      }    }  }

看到WindowManagerGLobal中有三個屬性: mViews、mRoots、mParams,可以大膽猜測這個類中保存了我們進程中的所有視圖以及相關屬性。在這里主要關注一下ViewRootImpl的這個實例對象root,接下來的會走進root的setView中。

ViewRootImpl的setView方法內容有點多,我這里就截取關鍵的兩部分:

1.

  int res; /** = WindowManagerImpl.ADD_OKAY; **/  try {    mOrigWindowType = mWindowAttributes.type;    mAttachInfo.mRecomputeGlobalAttributes = true;    collectViewAttributes();    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,        getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);

創建了一個名為res的int類型變量,他要獲取到的是懸浮窗添加的結果:成功或者失敗。

2.

    if (res < WindowManagerGlobal.ADD_OKAY) {        mAttachInfo.mRootView = null;        mAdded = false;        mFallbackEventHandler.setView(null);        unscheduleTraversals();        setAccessibilityFocus(null, null);        switch (res) {          case WindowManagerGlobal.ADD_BAD_APP_TOKEN:          case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:            throw new WindowManager.BadTokenException(                "Unable to add window -- token " + attrs.token                + " is not valid; is your activity running?");          case WindowManagerGlobal.ADD_NOT_APP_TOKEN:            throw new WindowManager.BadTokenException(                "Unable to add window -- token " + attrs.token                + " is not for an application");          case WindowManagerGlobal.ADD_APP_EXITING:            throw new WindowManager.BadTokenException(                "Unable to add window -- app for token " + attrs.token                + " is exiting");          case WindowManagerGlobal.ADD_DUPLICATE_ADD:            throw new WindowManager.BadTokenException(                "Unable to add window -- window " + mWindow                + " has already been added");          case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:            // Silently ignore -- we would have just removed it            // right away, anyway.            return;          case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:            throw new WindowManager.BadTokenException("Unable to add window "                + mWindow + " -- another window of type "                + mWindowAttributes.type + " already exists");          case WindowManagerGlobal.ADD_PERMISSION_DENIED:            throw new WindowManager.BadTokenException("Unable to add window "                + mWindow + " -- permission denied for window typ                  + mWindowAttributes.type);          case WindowManagerGlobal.ADD_INVALID_DISPLAY:              throw new WindowManager.InvalidDisplayException("Unable to add window "                  + mWindow + " -- the specified display can not be found");          case WindowManagerGlobal.ADD_INVALID_TYPE:              throw new WindowManager.InvalidDisplayException("Unable to add window "                  + mWindow + " -- the specified window type "                  + mWindowAttributes.type + " is not valid");        }        throw new RuntimeException(              "Unable to add window -- unknown error code " + res);      }

第二部分是res返回失敗的所有情況,在添加成功的時候res為OKAY,而非OKAY的情況就是上述情況。

接下來來看一下添加懸浮窗的操作,就是1中mWindowSession.addToDisplay。mWindowSession類型如下:

  final IWindowSession mWindowSession;

在這里其實用到了aidl跨進程通信,最終執行該方法的類是Session:

  @Override  public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,      int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,      Rect outStableInsets, Rect outOutsets,      DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,        outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);  }

這個mService就是一個關鍵了系統類——WindowMamagerService(WMS)。到了這里我們簡單過一下思路:在addView之后,通過WindowManagerGlobal進行一些相關配置,傳入ViewRootImpl,再通過aidl方式發送給WMS系統服務。

可能有小伙伴會疑惑。好端端的為什么要用aidl實現?最開始本人也有這個疑惑,但是后來想了想所有的窗口無論系統窗口還是第三方app,窗口都是要通過一個類去進行添加允許判斷,這里使用aidl是在合適不過的了。我們接著看一下WMS的addWindow方法:

這個addWindow方法又是一段超長的代碼,所以也就不全粘,說一下他的簡單流程吧,主要是分為三步:權限判斷、條件篩選、添加窗口

WMS的addWindow方法:

  int res = mPolicy.checkAddPermission(attrs, appOp);  if (res != WindowManagerGlobal.ADD_OKAY) {    return res;  }

首先進行一個權限判斷,

final WindowManagerPolicy mPolicy;

WindowManagerPolicy的實現類是PhoneWindowManagerPolicy,看一下他的實現:

又是小一百行的代碼,我們拆開來看:

  //排除不屬于三種類型懸浮窗范圍內的type  //很明顯的三段排除。  if (!((type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW)      || (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW)      || (type >= FIRST_SYSTEM_WINDOW && type <= LAST_SYSTEM_WINDOW))) {    return WindowManagerGlobal.ADD_INVALID_TYPE;  }  //不是系統級別的懸浮窗直接滿足條件  if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) {    return ADD_OKAY;  }    //以下幾種不是系統警告類型的系統彈窗,會滿足條件,除此之外的使用默認判斷的方式    if (!isSystemAlertWindowType(type)) {      switch (type) {        case TYPE_TOAST:          outAppOp[0] = OP_TOAST_WINDOW;          return ADD_OKAY;        case TYPE_DREAM:        case TYPE_INPUT_METHOD:        case TYPE_WALLPAPER:        case TYPE_PRESENTATION:        case TYPE_PRIVATE_PRESENTATION:        case TYPE_VOICE_INTERACTION:        case TYPE_ACCESSIBILITY_OVERLAY:        case TYPE_QS_DIALOG:          // The window manager will check these.          return ADD_OKAY;      }      return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)          == PERMISSION_GRANTED ? ADD_OKAY : ADD_PERMISSION_DENIED;    }

后面的幾段代碼會頻繁出現最后的這段代碼:mContext.checkCallingOrSelfPermission,具體實現的類是ContextFixture:

    @Override    public int checkCallingOrSelfPermission(String permission) {      if (mPermissionTable.contains(permission)          || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) {        logd("checkCallingOrSelfPermission: " + permission + " return GRANTED");        return PackageManager.PERMISSION_GRANTED;      } else {        logd("checkCallingOrSelfPermission: " + permission + " return DENIED");        return PackageManager.PERMISSION_DENIED;      }    }

這里會使用默認權限判斷的方式,要么允許對應權限,要么就是擁有全部權限,否則就會返回DENIED。

這個說完接著回到checkPermission方法。

    //對于系統進程直接滿足允許    final int callingUid = Binder.getCallingUid();    if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {      return ADD_OKAY;    }

說實話下面這一段代碼我看的不是很明白,只是看到了這里對8.0之后做了版本限制,直接使用默認檢查方式。

    ApplicationInfo appInfo;    try {      appInfo = mContext.getPackageManager().getApplicationInfoAsUser(              attrs.packageName,              0 /* flags */,              UserHandle.getUserId(callingUid));    } catch (PackageManager.NameNotFoundException e) {      appInfo = null;    }    if (appInfo == null || (type != TYPE_APPLICATION_OVERLAY && appInfo.targetSdkVersion >= O)) {            return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)          == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;    }

這段是要從PackageManager中獲取ApplicationInfo,如果獲取失敗會拋出NameNotFound異常。所以下面的判斷是在異常的時候使用默認權限處理方式。

最后還以一步檢查操作,關系不大就不看了。到這里checkPermission方法就結束了。

權限檢查的步驟已經結束,接著就是根據上述獲取到的結果進行條件篩選。

  if (res != WindowManagerGlobal.ADD_OKAY) {    return res;  }

首先在權限檢查的步驟獲取權限失敗,那么會直接返回,不會執行條件篩選的步驟。而真正的條件篩選步驟代碼也是很多,我這里直接粘過來然后說了。

      //111111111111111      if (!mDisplayReady) {        throw new IllegalStateException("Display has not been initialialized");      }      final DisplayContent displayContent = getDisplayContentOrCreate(displayId);      if (displayContent == null) {        Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "            + displayId + ". Aborting.");        return WindowManagerGlobal.ADD_INVALID_DISPLAY;      }      if (!displayContent.hasAccess(session.mUid)          && !mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) {        Slog.w(TAG_WM, "Attempted to add window to a display for which the application "            + "does not have access: " + displayId + ". Aborting.");        return WindowManagerGlobal.ADD_INVALID_DISPLAY;      }      if (mWindowMap.containsKey(client.asBinder())) {        Slog.w(TAG_WM, "Window " + client + " is already added");        return WindowManagerGlobal.ADD_DUPLICATE_ADD;      }      //22222222222222      if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {        parentWindow = windowForClientLocked(null, attrs.token, false);        if (parentWindow == null) {          Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "             + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;        }        if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW            && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {          Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "              + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;        }      }      //333333333333333      if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {        Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting.");        return WindowManagerGlobal.ADD_PERMISSION_DENIED;      }      //444444444444444      AppWindowToken atoken = null;      final boolean hasParent = parentWindow != null;      // Use existing parent window token for child windows since they go in the same token      // as there parent window so we can apply the same policy on them.      WindowToken token = displayContent.getWindowToken(          hasParent ? parentWindow.mAttrs.token : attrs.token);      // If this is a child window, we want to apply the same type checking rules as the      // parent window type.      final int rootType = hasParent ? parentWindow.mAttrs.type : type;      boolean addToastWindowRequiresToken = false;      if (token == null) {        if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {          Slog.w(TAG_WM, "Attempted to add application window with unknown token "             + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }        if (rootType == TYPE_INPUT_METHOD) {          Slog.w(TAG_WM, "Attempted to add input method window with unknown token "             + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }        if (rootType == TYPE_VOICE_INTERACTION) {          Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "             + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }        if (rootType == TYPE_WALLPAPER) {          Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "             + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }        if (rootType == TYPE_DREAM) {          Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "             + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }        if (rootType == TYPE_QS_DIALOG) {          Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "             + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }        if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {          Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "              + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }        if (type == TYPE_TOAST) {          // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.          if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,              parentWindow)) {            Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "                + attrs.token + ". Aborting.");            return WindowManagerGlobal.ADD_BAD_APP_TOKEN;          }        }        final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();        final boolean isRoundedCornerOverlay =            (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;        token = new WindowToken(this, binder, type, false, displayContent,            session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);      } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {        atoken = token.asAppWindowToken();        if (atoken == null) {          Slog.w(TAG_WM, "Attempted to add window with non-application token "             + token + ". Aborting.");          return WindowManagerGlobal.ADD_NOT_APP_TOKEN;        } else if (atoken.removed) {          Slog.w(TAG_WM, "Attempted to add window with exiting application token "             + token + ". Aborting.");          return WindowManagerGlobal.ADD_APP_EXITING;        } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {          Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"              + " starting window");          return WindowManagerGlobal.ADD_DUPLICATE_ADD;        }      } else if (rootType == TYPE_INPUT_METHOD) {        if (token.windowType != TYPE_INPUT_METHOD) {          Slog.w(TAG_WM, "Attempted to add input method window with bad token "              + attrs.token + ". Aborting.");           return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }      } else if (rootType == TYPE_VOICE_INTERACTION) {        if (token.windowType != TYPE_VOICE_INTERACTION) {          Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "              + attrs.token + ". Aborting.");           return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }      } else if (rootType == TYPE_WALLPAPER) {        if (token.windowType != TYPE_WALLPAPER) {          Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "              + attrs.token + ". Aborting.");           return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }      } else if (rootType == TYPE_DREAM) {        if (token.windowType != TYPE_DREAM) {          Slog.w(TAG_WM, "Attempted to add Dream window with bad token "              + attrs.token + ". Aborting.");           return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }      } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {        if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {          Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "              + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }      } else if (type == TYPE_TOAST) {        // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.        addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,            callingUid, parentWindow);        if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {          Slog.w(TAG_WM, "Attempted to add a toast window with bad token "              + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }      } else if (type == TYPE_QS_DIALOG) {        if (token.windowType != TYPE_QS_DIALOG) {          Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "              + attrs.token + ". Aborting.");          return WindowManagerGlobal.ADD_BAD_APP_TOKEN;        }      } else if (token.asAppWindowToken() != null) {        Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);        // It is not valid to use an app token with other system types; we will        // instead make a new token for it (as if null had been passed in for the token).        attrs.token = null;        token = new WindowToken(this, client.asBinder(), type, false, displayContent,            session.mCanAddInternalSystemWindow);      }      //5555555555555      final WindowState win = new WindowState(this, session, client, token, parentWindow,          appOp[0], seq, attrs, viewVisibility, session.mUid,          session.mCanAddInternalSystemWindow);      if (win.mDeathRecipient == null) {        // Client has apparently died, so there is no reason to        // continue.        Slog.w(TAG_WM, "Adding window client " + client.asBinder()            + " that is dead, aborting.");        return WindowManagerGlobal.ADD_APP_EXITING;      }      if (win.getDisplayContent() == null) {        Slog.w(TAG_WM, "Adding window to Display that has been removed.");        return WindowManagerGlobal.ADD_INVALID_DISPLAY;      }      final boolean hasStatusBarServicePermission =          mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)              == PackageManager.PERMISSION_GRANTED;      mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);      win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));      res = mPolicy.prepareAddWindowLw(win, attrs);      if (res != WindowManagerGlobal.ADD_OKAY) {        return res;      }      final boolean openInputChannels = (outInputChannel != null          && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);      if (openInputChannels) {        win.openInputChannel(outInputChannel);      }      //666666666666666      if (type == TYPE_TOAST) {        if (!getDefaultDisplayContentLocked().canAddToastWindowForUid(callingUid)) {          Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");          return WindowManagerGlobal.ADD_DUPLICATE_ADD;        }                if (addToastWindowRequiresToken            || (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0            || mCurrentFocus == null            || mCurrentFocus.mOwnerUid != callingUid) {          mH.sendMessageDelayed(              mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),              win.mAttrs.hideTimeoutMilliseconds);        }      }

這里講篩選部分大體分成這么幾個步驟:

  1. 系統以及初始化的一些判斷:就像最開始的四個判斷。
  2. 子窗口類型時候的對父窗口的相關篩選(父是否為空,以及父親的類型判斷)
  3. 一種特殊的私有類型條件篩選,該類型屬于系統類型
  4. 涉及證書(token)的窗口類型條件篩選。
  5. 狀態欄權限條件篩選
  6. 吐司類型的條件篩選

在代碼中對應的步驟有明確的標注,而具體的代碼大多只是一些判斷,所以在感覺沒有細說的必要了。

在條件篩選完成之后,剩下的類型都是符合添加的類型,從現在開始就開始對不同的type進行不同的添加。經過多到加工后,將OKAY返回。

如果能從添加窗口的步驟返回,就說明一定是OKAY的。那么我們可以一步步跳回層層調用的代碼,最終在ViewRootImpl中,對沒有添加成功的拋出異常。

      if (res < WindowManagerGlobal.ADD_OKAY) {          mAttachInfo.mRootView = null;          mAdded = false;          mFallbackEventHandler.setView(null);          unscheduleTraversals();          setAccessibilityFocus(null, null);          switch (res) {            case WindowManagerGlobal.ADD_BAD_APP_TOKEN:            case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:              throw new WindowManager.BadTokenException(                  "Unable to add window -- token " + attrs.token                  + " is not valid; is your activity running?");            case WindowManagerGlobal.ADD_NOT_APP_TOKEN:              throw new WindowManager.BadTokenException(                  "Unable to add window -- token " + attrs.token                  + " is not for an application");            case WindowManagerGlobal.ADD_APP_EXITING:              throw new WindowManager.BadTokenException(                  "Unable to add window -- app for token " + attrs.token                  + " is exiting");            case WindowManagerGlobal.ADD_DUPLICATE_ADD:              throw new WindowManager.BadTokenException(                  "Unable to add window -- window " + mWindow                  + " has already been added");            case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:              // Silently ignore -- we would have just removed it              // right away, anyway.              return;            case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:              throw new WindowManager.BadTokenException("Unable to add window "                  + mWindow + " -- another window of type "                  + mWindowAttributes.type + " already exists");            case WindowManagerGlobal.ADD_PERMISSION_DENIED:              throw new WindowManager.BadTokenException("Unable to add window "                  + mWindow + " -- permission denied for window type "                  + mWindowAttributes.type);            case WindowManagerGlobal.ADD_INVALID_DISPLAY:              throw new WindowManager.InvalidDisplayException("Unable to add window "                  + mWindow + " -- the specified display can not be found");            case WindowManagerGlobal.ADD_INVALID_TYPE:              throw new WindowManager.InvalidDisplayException("Unable to add window "                  + mWindow + " -- the specified window type "                  + mWindowAttributes.type + " is not valid");          }          throw new RuntimeException(              "Unable to add window -- unknown error code " + res);        }

對于OKAY的,在ViewRootImpl中會做一些其他的操作,反正我是沒看懂-。=、

四、小結

到這里WMS的添加懸浮窗口的流程差不多就過了一遍了??赡苡行┑胤秸f的不是很細,大家下來可以關注一下個別幾個點。整個過程有這么幾個需要強調的地方。

  • 函數循環嵌套,共同消費返回值。
  • 異常循環嵌套
  • 個別地方對M和O以上的系統進行了限制

如果在添加懸浮窗的時候使用了不同的type,可能會發生異常:本人拿了一個8.0的手機,分別對窗口type設置為OVERLAY和ERROR。因為ERROR類型是被棄用的,我發現使用ERROR會拋出異常,而OVERLAY不會。同樣的拿了一個6.0的手機添加ERROR類型就沒有異常拋出,肯定是上述的問題導致的,但是具體在哪一塊我還沒有找到,因為整個流程的出口太多了-。=。

此外在WindowManagerGlobal.addView方法中,有一個地方:

  if (parentWindow != null) {    parentWindow.adjustLayoutParamsForSubWindow(wparams);  } else {

這個方法是對于有子窗口類型的證書處理,網上查了一下該方法在四點幾、六點幾和8.0是不同的,也就是說對證書的處理方式變化了,這里本人還沒有細看,有興趣的盆友可以研究一下然后評論交流一番。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
在线观看精品自拍私拍| 日本不卡高字幕在线2019| 国产精品亚洲综合天堂夜夜| 亚洲最大激情中文字幕| 欧美裸体xxxx极品少妇| 欧美成年人视频| 91精品久久久久久久久久久久久| 日韩av电影国产| 欧美精品一区二区免费| 久久精品亚洲热| 国产精品久久久久免费a∨大胸| 国产精品视频久| 日韩精品在线电影| 欧美精品videos| 成人羞羞国产免费| 九九精品视频在线| 最好看的2019年中文视频| 国产精品一区二区电影| 色噜噜亚洲精品中文字幕| 国内精品久久影院| 欧美精品亚州精品| 欧美激情一区二区三区成人| 欧美亚洲在线播放| 国产精品吴梦梦| 欧美日韩精品在线| 国产精品激情自拍| 欧洲亚洲免费在线| 疯狂欧美牲乱大交777| 亚洲第一级黄色片| 国产成人97精品免费看片| 亚洲国产成人精品久久久国产成人一区| 日韩电影大片中文字幕| 亚洲国产精品成人一区二区| 日韩精品小视频| 欧美日韩人人澡狠狠躁视频| 91色p视频在线| 一本色道久久综合狠狠躁篇的优点| 国产精品第2页| 久久国产精品影视| xxav国产精品美女主播| 2019中文字幕在线观看| 91在线观看免费高清| 成人国产精品一区二区| 欧美黑人性视频| 911国产网站尤物在线观看| 日韩的一区二区| 日韩在线视频线视频免费网站| 97视频人免费观看| 午夜精品在线视频| 国产欧洲精品视频| 亚洲乱码av中文一区二区| 亚洲曰本av电影| 国产精品久久婷婷六月丁香| 久久久午夜视频| 日韩电影中文字幕一区| 亚洲无限乱码一二三四麻| 国产精品香蕉在线观看| 欧美激情一区二区三区成人| 欧美福利在线观看| 国产玖玖精品视频| 韩国精品美女www爽爽爽视频| 国内自拍欧美激情| 国产精品久久久久久久久久久新郎| 97精品一区二区三区| 国产精品福利在线观看| www.久久色.com| 久久人人爽亚洲精品天堂| 国产福利精品av综合导导航| 国产精品都在这里| 欧美国产在线电影| 亚洲成av人片在线观看香蕉| 欧美亚洲国产成人精品| 亚洲视频在线观看| 日韩高清电影免费观看完整| 欧美性猛交xxxx久久久| 久久久久久久久久亚洲| 热久久免费国产视频| 麻豆乱码国产一区二区三区| 国产精品爽爽爽| 欧美日韩国产成人高清视频| wwwwwwww亚洲| 最好看的2019的中文字幕视频| 欧美性生交大片免费| 综合136福利视频在线| 欧美黄色片免费观看| 国产一区二区三区丝袜| 国产精品久久久久久久一区探花| 亚洲美女喷白浆| 久久久久久伊人| 日韩在线视频线视频免费网站| 高清日韩电视剧大全免费播放在线观看| 国产精品久久久久福利| 7m精品福利视频导航| 91精品国产乱码久久久久久久久| 91黑丝高跟在线| 亚洲一区二区三区乱码aⅴ蜜桃女| 精品亚洲一区二区三区四区五区| 亚洲一区二区免费在线| 成人激情视频小说免费下载| 中文字幕日韩av| 国产最新精品视频| 一区二区三区四区视频| 欧美日韩在线免费| 亚洲成人aaa| 欧美有码在线观看| 色哟哟入口国产精品| 91久久久久久| 欧美色视频日本高清在线观看| 国产精品美女视频网站| 日韩精品免费电影| 国产精品天天狠天天看| 国产精品露脸av在线| 日韩在线视频免费观看高清中文| 精品在线观看国产| 国产日本欧美视频| 亚洲一区二区中文| 7m第一福利500精品视频| 中文字幕在线日韩| 国产精品自拍偷拍视频| 国产精品自拍偷拍视频| 中文字幕日韩av电影| 久久精品男人天堂| 亚洲伊人成综合成人网| 欧美黑人巨大精品一区二区| 国产激情综合五月久久| 久久精品亚洲94久久精品| 欧美精品videos另类日本| 国产精品欧美日韩一区二区| 久久久国产91| 中文字幕免费精品一区| 69精品小视频| 91精品在线播放| 欧美黑人巨大精品一区二区| 成年无码av片在线| 97视频在线观看成人| 全色精品综合影院| 亚洲精品国产品国语在线| 欧美日韩一区二区三区| 欧美性极品少妇精品网站| 91欧美视频网站| 欧美小视频在线| 精品亚洲国产成av人片传媒| 欧美丰满老妇厨房牲生活| 欧美性猛交xxxx乱大交蜜桃| 4438全国成人免费| 国产成人精品国内自产拍免费看| 欧美精品免费在线| 欧美在线视频网| 久久久久999| 精品露脸国产偷人在视频| 国产91精品久久久久久| 欧美激情一区二区三区在线视频观看| 欧美另类极品videosbestfree| 97激碰免费视频| 日韩免费av片在线观看| 亚洲国产欧美一区二区三区同亚洲| 这里精品视频免费| 欧美精品免费看| 亚洲综合在线小说| 一区二区三区精品99久久| 亚洲欧美精品一区二区| 亚洲一区二区三区香蕉| 午夜精品视频网站| 伦理中文字幕亚洲|