Window梳理

Activity與Window相關(guān)概念

  • Activity負責生命周期
  • Window控制視圖顯示相關(guān)、事件處理相關(guān)
  • AMS統(tǒng)一調(diào)度所有應(yīng)用程序的Activity
  • WMS控制所有Window的顯示與隱藏

Window

“Window”表明它是和窗口相關(guān)的,“窗口”是一個抽象的概念,
1、從用戶的角度來講,它是一個“界面”;
2、從SurfaceFlinger的角度來看,它是一個Layer,承載著和界面有關(guān)的數(shù)據(jù)和屬性;
3、從WMS角度來看,它是一個WIndowState,用于管理和界面有關(guān)的狀態(tài)。
4,從WindowManager角度來講,它是一個容器存儲虛擬Window的數(shù)據(jù),被WindowManager管理

  • 表示一個窗口的概念,是所有View的直接管理者,任何視圖都通過Window呈現(xiàn)(點擊事件由Window->DecorView->View; Activity的setContentView底層通過Window完成)
  • Window是一個抽象類,具體實現(xiàn)有PhoneWindow。例如activity::attach與Dialog的構(gòu)造方法中
  • Window是抽象出來的概念,需要WindowManager來管理(添加、刪除、更新)。
  • WindowManager是外界訪問Window的入口
  • Window具體實現(xiàn)位于WindowManagerService中
  • 定義窗口樣式和行為的抽象基類,用于作為頂層的view加到WindowManager中,其實現(xiàn)類是PhoneWindow。
  • 每個Window都需要指定一個Type(應(yīng)用窗口、子窗口、系統(tǒng)窗口)。Activity對應(yīng)的窗口是應(yīng)用窗口;PopupWindow,ContextMenu,OptionMenu是常用的子窗口;像Toast和系統(tǒng)警告提示框(如ANR)就是系窗口,還有很多應(yīng)用的懸浮框也屬于系統(tǒng)窗口類型。

WindowManager

管理窗口(添加、刪除、更新),WindowManager只是面向開發(fā)者調(diào)用的一個中間類,其并不持有WindowManagerService的句柄,而是創(chuàng)建出ViewRootImpl與WindowManagerService通信,每個Windowd都有一個ViewRootImpl。

    //WindowManager的獲取   ContextImpl::getSystemService -> SystemServiceRegistry::getSystemService
    windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
   //WindowManager操作View
    public void addView(View view, ViewGroup.LayoutParams params);//顯示View 調(diào)用:Activity::makeVisible(),Dialog:show()
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);//更新View
    public void removeView(View view);//移除View
  • view:要顯示的View,常用的有Activity::PhoneWindow::DecorView、Dialog::PhoneWindow::DecorView、Toast::makeText,自己顯示W(wǎng)indow的View等等。
  • ViewGroup.LayoutParams:本質(zhì)WindowManager.LayoutParams,添加自己的Window就要配置這個參數(shù)
  • 1、配置type,應(yīng)用窗口:層級范圍是1~99、子窗口:層級范圍是1000~1999、系統(tǒng)窗口:層級范圍是2000~2999
  • 2、配置 flags,常用的有
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE//不獲取焦點
    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL//窗口之外事件可以傳遞到底層window
    WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED//顯示鎖屏Window
  • addView顯示View 調(diào)用:Activity::makeVisible(),Dialog:show(),自己顯示W(wǎng)indow的View。

WindowManagerImpl(Window里找)::addView-> WindowManagerGlobal::addView(window添加view的時候創(chuàng)建ViewRootImpl) -> ViewRootImpl::setView

    //Window
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);//設(shè)置WindowManager
    }

    //WindowManagerImpl
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
   @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);//驗證params是否是WindowManager.LayoutParams
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
   //WindowManagerGlobal
   public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
            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;
            }
        }
    }
//ViewRootImpl
final IWindowSession mWindowSession;//
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
         ...//
         requestLayout();
         ...//Bindler機制向Session通信內(nèi)部調(diào)用了WindowManagerService::addWindow
         res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
         ....
}
//執(zhí)行scheduleTraversals 開始走View流程
//Session  全局搜索IWindowSession可以找到Session類
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    @Override//調(diào)用WindowManagerService::addWindow
    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);
    }
}
//WindowManagerService
public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
     int res = mPolicy.checkAddPermission(attrs, appOp);//WindowManagerPolicy(PhoneWindowManager) window顯示策略
    ...
     mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
    ...
     res = mPolicy.prepareAddWindowLw(win, attrs);//調(diào)用prepareAddWindowLw()執(zhí)行Window的添加
     mWindowMap.put(client.asBinder(), win);//以Binder為Key將WindowState對象保存在mWindowMap中
     win.mToken.addWindow(win);//將WindowState保存在窗口的Token中
}
  • removeView 調(diào)用:ActivityThread::handleDestroyActivity,Dialog:dismiss()等等。

WindowManagerImpl::removeViewImmediate-> WindowManagerGlobal::removeView()-> ViewRootImpl::die

//ActivityThread
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason) {
     ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance, reason);
    ...
    wm.removeViewImmediate(v);
}
//WindowManagerGlobal
public void removeView(View view, boolean immediate) {
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
    }
}
private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();
        if (view != null) {
            InputMethodManager imm = InputMethodManager.getInstance();
            if (imm != null) {
                imm.windowDismissed(mViews.get(index).getWindowToken());
            }
        }
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }
  //ViewRootImpl
 boolean die(boolean immediate) {
        if (immediate && !mIsInTraversal) {
            doDie();
            return false;
        }
        if (!mIsDrawing) {
            destroyHardwareRenderer();
        } 
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }

    void doDie() {
        checkThread();
        synchronized (this) {
            mRemoved = true;
            if (mAdded) {
                dispatchDetachedFromWindow();//執(zhí)行view.onDetachedFromWindow、 mSurface.release()、 mWindowSession.remove(mWindow);等等釋放操作
            }
            if (mAdded && !mFirst) {
                destroyHardwareRenderer();
                if (mView != null) {
                    int viewVisibility = mView.getVisibility();
                    boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
                    if (mWindowAttributesChanged || viewVisibilityChanged) {
                        try {
                            if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
                                    & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                                mWindowSession.finishDrawing(mWindow);//結(jié)束繪制window
                            }
                        } catch (RemoteException e) {
                        }
                    }
                    mSurface.release();
                }
            }
            mAdded = false;
        }
        WindowManagerGlobal.getInstance().doRemoveView(this);
    }
  • updateViewLayout 同理
//WindowManagerGlobal
   public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        view.setLayoutParams(wparams);
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }
//ViewRootImpl
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
        synchronized (this) {
            ...
            scheduleTraversals();
        }
    }
  • WindowManagerGlobal 管理DecorView,mParams,mDyingViews,ViewRootImpl 等集合。
  • ViewRootImpl 非常重要 負責view measure layout draw event 統(tǒng)一管理。
  • 可以看出Window只是虛構(gòu)出來的架子,而ViewRootImpl 和 WMS才是控制Window關(guān)鍵先生。

WindowManagerService

簡稱Wms,WindowManagerService管理窗口的創(chuàng)建、更新和刪除,顯示順序等,是WindowManager這個管理接品的真正的實現(xiàn)類。它運行在System_server進程,作為服務(wù)端,客戶端(應(yīng)用程序)通過IPC調(diào)用和它進行交互。

Token

這里提到的Token主是指窗口令牌(Window Token),是一種特殊的Binder令牌,Wms用它唯一標識系統(tǒng)中的一個窗口。

創(chuàng)建Window視圖配置參數(shù)type

  • 應(yīng)用窗口:層級范圍是1~99

  • 子窗口:層級范圍是1000~1999

  • 系統(tǒng)窗口:層級范圍是2000~2999

  • 各級別type值在WindowManager中的定義分別為:

  • 應(yīng)用窗口(1~99)

   //第一個應(yīng)用窗口
   public static final int FIRST_APPLICATION_WINDOW = 1;
   //所有程序窗口的base窗口,其他應(yīng)用程序窗口都顯示在它上面
   public static final int TYPE_BASE_APPLICATION   = 1;
   //所有Activity的窗口,只能配合Activity在當前APP使用
   public static final int TYPE_APPLICATION        = 2;
   //目標應(yīng)用窗口未啟動之前的那個窗口
   public static final int TYPE_APPLICATION_STARTING = 3;
   //最后一個應(yīng)用窗口
   public static final int LAST_APPLICATION_WINDOW = 99;
  • 子窗口(1000~1999)
  //第一個子窗口
  public static final int FIRST_SUB_WINDOW        = 1000;
  // 面板窗口,顯示于宿主窗口的上層,只能配合Activity在當前APP使用
  public static final int TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW;
  // 媒體窗口(例如視頻),顯示于宿主窗口下層
  public static final int TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1;
  // 應(yīng)用程序窗口的子面板,只能配合Activity在當前APP使用(PopupWindow默認就是這個Type)
  public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
  //對話框窗口,只能配合Activity在當前APP使用
  public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
  //
  public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4;
  //最后一個子窗口
 public static final int LAST_SUB_WINDOW         = 1999;
  • 系統(tǒng)窗口(2000~2999)
        //系統(tǒng)窗口,非應(yīng)用程序創(chuàng)建
        public static final int FIRST_SYSTEM_WINDOW     = 2000;
        //狀態(tài)欄,只能有一個狀態(tài)欄,位于屏幕頂端,其他窗口都位于它下方
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
        //搜索欄,只能有一個搜索欄,位于屏幕上方
        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1; 
        //
        //電話窗口,它用于電話交互(特別是呼入),置于所有應(yīng)用程序之上,狀態(tài)欄之下,屬于懸浮窗(并且給一個Activity的話按下HOME鍵會出現(xiàn)看不到桌面上的圖標異常情況)
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
        //
        //系統(tǒng)警告提示窗口,出現(xiàn)在應(yīng)用程序窗口之上,屬于懸浮窗, 但是會被禁止
        public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
        //
        //信息窗口,用于顯示Toast, 不屬于懸浮窗, 但有懸浮窗的功能, 缺點是在Android2.3上無法接收點擊事件
        public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
        //
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
        //鎖屏窗口
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
        //系統(tǒng)頂層窗口,顯示在其他一切內(nèi)容之上,此窗口不能獲得輸入焦點,否則影響鎖屏
        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
        //電話優(yōu)先,當鎖屏時顯示,此窗口不能獲得輸入焦點,否則影響鎖屏
        public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
        //系統(tǒng)對話框窗口
        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
        //鎖屏時顯示的對話框
        public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;
        //系統(tǒng)內(nèi)部錯誤提示,顯示在任何窗口之上
        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;
        //內(nèi)部輸入法窗口,顯示于普通UI之上,應(yīng)用程序可重新布局以免被此窗口覆蓋
        public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
        //內(nèi)部輸入法對話框,顯示于當前輸入法窗口之上
        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
        //墻紙窗口
        public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
        //狀態(tài)欄的滑動面板
        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
        //安全系統(tǒng)覆蓋窗口,這些窗戶必須不帶輸入焦點,否則會干擾鍵盤
        public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
        //最后一個系統(tǒng)窗口
        public static final int LAST_SYSTEM_WINDOW      = 2999;

2.窗口flags顯示屬性在WindowManager中也有定義:

        //窗口特征標記
        public int flags;
        //當該window對用戶可見的時候,允許鎖屏
        public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;
        //窗口后面的所有內(nèi)容都變暗
        public static final int FLAG_DIM_BEHIND        = 0x00000002;
        //Flag:窗口后面的所有內(nèi)容都變模糊
        public static final int FLAG_BLUR_BEHIND        = 0x00000004;
        //窗口不能獲得焦點
        public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
        //窗口不接受觸摸屏事件
        public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
        //即使在該window在可獲得焦點情況下,允許該窗口之外的點擊事件傳遞到當前窗口后面的的窗口去
        public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;
        //當手機處于睡眠狀態(tài)時,如果屏幕被按下,那么該window將第一個收到觸摸事件
        public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;
        //當該window對用戶可見時,屏幕出于常亮狀態(tài)
        public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;
        //:讓window占滿整個手機屏幕,不留任何邊界
        public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;
        //允許窗口超出整個手機屏幕
        public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;
        //window全屏顯示
        public static final int FLAG_FULLSCREEN      = 0x00000400;
        //恢復(fù)window非全屏顯示
        public static final int FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800;
        //開啟窗口抖動
        public static final int FLAG_DITHER             = 0x00001000;
        //安全內(nèi)容窗口,該窗口顯示時不允許截屏
        public static final int FLAG_SECURE             = 0x00002000;
        //鎖屏時顯示該窗口
        public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
        //系統(tǒng)的墻紙顯示在該窗口之后
        public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
        //當window被顯示的時候,系統(tǒng)將把它當做一個用戶活動事件,以點亮手機屏幕
        public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
        //該窗口顯示,消失鍵盤
        public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
        //當該window在可以接受觸摸屏情況下,讓因在該window之外,而發(fā)送到后面的window的觸摸屏可以支持split touch
        public static final int FLAG_SPLIT_TOUCH = 0x00800000;
        //對該window進行硬件加速,該flag必須在Activity或Dialog的Content View之前進行設(shè)置
        public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000;
        //讓window占滿整個手機屏幕,不留任何邊界
        public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000;
        //透明狀態(tài)欄
        public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
        //透明導(dǎo)航欄
        public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容