筆記:window與windowManager

window與windowManager


  • window在日常開發(fā)中如懸浮窗,它的實(shí)現(xiàn)是phoneWindow

  • window的創(chuàng)建通過WindowManager,WindowManager是外界訪問window的入口。

windowManager獲?。?/p>

      WindowManager      wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

window通過setWindowManager()方法關(guān)聯(lián)WindowManager


  • window的具體實(shí)現(xiàn)位于WindowManagerService,WindowManager和WindowManagerService的交互是一個IPC過程

  • Android所有的視圖都是通過Window來呈現(xiàn)的,不管是Activity,Dialog,還是Toast,它們的視圖實(shí)際上都是附加在Window上,因此,Window實(shí)際是View的實(shí)際管理者,

  • Activity設(shè)置視圖的方法setContentView()底層也是通過Window來實(shí)現(xiàn)的

  • Window有3種類型
  1. 應(yīng)用類window 對應(yīng)一個Activity (1~99)
  2. 子window 不能單獨(dú)存在,需要依附在特定的父window之中,比如Dialog (1000~1999)
  3. 系統(tǒng)window 需要聲明權(quán)限才能創(chuàng)建的window,比如toast,系統(tǒng)狀態(tài)欄 (2000~2999)

  • Window是分層的,每個Window都有對應(yīng)的z-ordered,層級大的會覆蓋在層級小的上面,和html的z-index概念完全一致*

層級分別是上面的范圍,對應(yīng)著WindowManager.LayoutParams.type參數(shù),想要覆蓋在最上面,設(shè)置層級范圍最大即可,即系統(tǒng)層級,一般選用:

TYPE_SYSTEM_OVERLAY

TYPE_SYSTEM_ERROR

mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR
同時聲明權(quán)限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

  • WindowManage僅提供了三個方法,供開發(fā)者繼承使用:
public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

  • WindowManager實(shí)現(xiàn)類是WindowManagerImpl

WindowManagerImpl并沒有直接實(shí)現(xiàn)Window的三大操作,而是全部交給WindowManagerGlobal來處理,WindowManagerGlobal以工廠的形式向外提供實(shí)例-----典型的橋接模式


Activity Window創(chuàng)建流程

  • Activity的Window的創(chuàng)建在attach()方法里完成的,在attach()方法里,系統(tǒng)會創(chuàng)建Activity所屬的Window對象并為其設(shè)置回調(diào)接口,Window對象的創(chuàng)建是通過PolicyManager的makeNewWindow方法實(shí)現(xiàn)的

由于Activity實(shí)現(xiàn)了Window的CallBack接口,因此Window接受到外界的回調(diào)就會回調(diào)到Activity中的方法,如onAttachToWindow(),onDetachFromWindow(),dispatchTouchEvent()

PolicyManager 一個策略類,PolicyManager中實(shí)現(xiàn)的幾個工廠方法全部在策略接口IPolicy中聲明了,其中makeNewWindow()方法中完成new PhoneWindow()。

(25版本中源碼Activity的attch方法中直接new PhoneWindow(),并沒有工廠模式)


  • Activity視圖怎么依附到Window上的

通過setContentView()來實(shí)現(xiàn)依附,setContent具體實(shí)現(xiàn):

  1. 如果沒有DecorView,就創(chuàng)建它
  2. 將View添加到DecorView的mContentParent中
  3. 回調(diào)Activity的onContentChanged()方法通知Activity的視圖已經(jīng)發(fā)生了改變

在ActivityThread中的handleResumeActivity方法中,首先會調(diào)用onResume方法,然后再調(diào)用Activity的makeVivible()方法,正是在makeVisible()方法中,DecorView真正完成了顯示和添加這兩個過程到這里Activity才被用戶看到。

//Activity.java
    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

Dialog Window創(chuàng)建流程


  • Dialog使用的是PhoneWindow,同樣低版本使用了PlicyManager的makeNewWindow方法來完成,高版本直接new PhoneWindow();參考源碼

  • 創(chuàng)建步驟
  1. 創(chuàng)建Window
  2. 初始化DecorView,并將Dialog視圖添加到DecorView中

setContentView()

  1. 將DecorView添加到Window中并顯示
    在Dialog的show()方法中,通過WindowManager將DecorView添加到Window中,

     mWindowManager.addView(mDecor, l);
     mShowing = true;
    

  • 普通Dialog的Context必須使用Activity的Context,如果使用ApplicationContext,會報錯。普通Dialog需要依附一個Window

Toast的Window創(chuàng)建過程


  • Toast 內(nèi)部有兩類IPC通信過程
  1. Toast訪問NotificationManagerService
  2. NotificationManagerService回調(diào)TN里的接口

  • TN是一個Binder類,用于NMS跨進(jìn)程調(diào)用TN的方法,如hide(),show()

所有TN里的hide,show()等方法都運(yùn)行在Binder線程池中,所以需要Handle切換到當(dāng)前線程中去。

注意是切換到當(dāng)前線程中。在沒有Looper的線程中,Toast無法正確運(yùn)行。


  • Toast的show()方法
    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }

enqueueToast首先將Toast封裝成ToastRecord對象,并將其添加到mToastQueue隊(duì)列中,mToastQueue其實(shí)是一個ArrayList,對于非系統(tǒng)應(yīng)用來說,該list長度最多為50個ToastRecord,這么做是為了防止DOS(拒絕服務(wù)攻擊),也就是如果大量循環(huán)彈toast那么其他應(yīng)用就無法彈了。


  • Toast 原理也就是Binder通信,Toast調(diào)用Show方法,內(nèi)部會將該toast與TN傳遞給運(yùn)行于系統(tǒng)進(jìn)程的NMS中,由系統(tǒng)進(jìn)程控制TN的show方法,系統(tǒng)需要統(tǒng)一管理toast,NMS會將該toast放入一個ArrayList執(zhí)行隊(duì)列中,while循環(huán)輪到該條toast時,取出該toast對于的TN對象,再通過Binder通信,執(zhí)行遠(yuǎn)程TN對象的show方法,show方法會通過Handle讓show過程脫離Binder線程池,運(yùn)行于Looper線程中,show中實(shí)際通過WindowManage的addView方法,將View添加到window上。這樣就完成了一次Toast顯示過程,hide過程一樣。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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