Window添加懸浮窗解析

  • WindowManager獲取
    window作為一種視圖抽象承載者,唯一的實(shí)現(xiàn)類是PhoneWindow,PhoneWindow中包含一個(gè)視圖結(jié)構(gòu)DecorView(FrameLayout 包含Title布局) 該View便是我們會(huì)addView時(shí)的視圖根布局 包含結(jié)構(gòu)如下圖:
    PhoneWindow 結(jié)構(gòu)

    添加Window時(shí)代碼如下:
    WindowManager windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
    WindowManager windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); 

上圖中第一種獲取的WindowManager是當(dāng)前Activity窗口管理類WindowManagerImp,這個(gè)窗口管理實(shí)現(xiàn)類會(huì)有多個(gè)每個(gè)Activirty都會(huì)包含一個(gè)該實(shí)例,Activity銷毀 該窗口管理器包含的視圖也將銷毀 。第二種是通過獲取全局應(yīng)用級(jí)別的WindowManagerImpl實(shí)現(xiàn)類 ,該窗口管理器包含的窗口個(gè)和Application的生命一樣長(zhǎng),而平時(shí)遇到的其他非Activity類型的Context獲取到的WindowManager都是代理到ContextImpl來具體獲取,所以非Activity獲取到的WindowManagerImpl均是同一個(gè)對(duì)象
下面是Activty獲取WindowManager的實(shí)現(xiàn):

Activity.class
   @Override
    public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {  
            return mWindowManager; // 返回當(dāng)前Acticvty的成員變量mWindowManager
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }

該mWindowManager是在ActivityThread采用反射加載Activity之后調(diào)用Activity.attach()方法時(shí),將當(dāng)前Activity的PhoneWindow與WindowManager關(guān)聯(lián)

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);
        ...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),   //關(guān)聯(lián)WindowManagerImpl   
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();// 將當(dāng)前WindowManagerImpl賦值
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);

        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
        enableAutofillCompatibilityIfNeeded();
    }

繼續(xù)看關(guān)聯(lián)WindowManager代碼:

   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); // 獲取WindowManagerService
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);  // 創(chuàng)建一個(gè)WindowManagerImpl
    }

對(duì)于使用Activity上下文獲取的WindowManager是專門管理當(dāng)前Activity得窗口實(shí)例 ,當(dāng)前界面銷毀時(shí) ,該管理器管理的界面也都要回收銷毀, 這個(gè)也就是Dialog和Popwindow這種子窗口的實(shí)現(xiàn)方式
而第二種方式是通過獲取Aplication的上下文從而調(diào)用ContextWrap的getSystemService,因?yàn)镃ontext的實(shí)現(xiàn)采用橋接設(shè)計(jì)模式將所有ContextWrap的實(shí)現(xiàn)交給ContextImpl來執(zhí)行 創(chuàng)建application的代碼:

ActivityThread.class  - > makeApplication 
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
 app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);

看出application的方法就是ContextImpl實(shí)現(xiàn)

ContextImpl.class

 @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

SystemServiceRegistry這個(gè)類是用來管理所有的系統(tǒng)Service的獲取類,內(nèi)部包含一個(gè)靜態(tài)代碼塊,實(shí)例化系統(tǒng)服務(wù)的客戶端調(diào)用的代理

SystemServiceRegistry.class
 static{
 ...
   registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
...
}

因此第二種獲取WindowManager才是作為與Applicatuon的全局唯一的WindowManagerImpl

  • Window與WindowManagerService交互
    每WinowManagerImpl實(shí)例內(nèi)部對(duì)View的操作也是通過獲取WindowManagerGlobal這個(gè)類的單例來實(shí)現(xiàn)實(shí)現(xiàn),WindowManagerGlobal內(nèi)部將ViewRootImpl、view 、 windowmanager.params、剛才銷毀的View存儲(chǔ)起來:
WindowManagerGlobal.class 

    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

WindowManagerGlobal(后面統(tǒng)稱wmg)通過Binder獲取wms,然后wms同樣通過Binder通信獲取 IWindowSession.stub 的實(shí)現(xiàn)類Session ,Session類的注釋:

Session 注釋

Session與windowManager協(xié)同管理每個(gè)app的窗口交互,每個(gè)app擁有一個(gè)Session和一個(gè)全局的WindowManagerGlobal ,
Session與application關(guān)系
在wmg的setView以及update等View的操作又統(tǒng)統(tǒng)交給ViewRootImpl來實(shí)現(xiàn),ViewRoorImpl在操作View展示以及跟新時(shí)調(diào)用wmg的靜態(tài)方法獲取IwindowSession.Stub實(shí)例,ViewRootImpl通過IWindowSession與WMS進(jìn)行交互,IWindowSession定義在IWindowSession.aidl文件中,而ViewRootImpl的內(nèi)部類 class W extends IWindow.Stub 作為在WMS操作后利用Binder通信的響應(yīng)類,主要用來通知ViewRootImpl來進(jìn)行View的調(diào)整等信息。

  • Window類型
  1. WindowManager.LayoutParams.TYPE_TOAST
    Toast類型的窗口 由NMS管理 Android4.4之前該類型的窗口系統(tǒng)為其添加兩個(gè)flags WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 表明這種類型窗口無法獲取焦點(diǎn)與交互行為 4.4 之后該類型的Toast取消了以上兩個(gè)flag 而且這種類型窗口不需要SYSTEM_ALERT_WINDOW權(quán)限的申明 直到6.0 google也沒有解決這個(gè)類似bug的漏洞,但是7.1 google 添加了一個(gè)限制 就是每一個(gè)Uid只能彈出一個(gè)該類型的窗口 不能重疊彈出 (由于國(guó)內(nèi)rom定制需要具體分析 ),也就是說當(dāng)你的target>25的時(shí)候也就是7.1的時(shí)候該類型的非系統(tǒng)app設(shè)置窗口類型為TYPE_TOAST會(huì)拋出BadToken異常,8.0 時(shí)google為了解決這些懸浮窗問題不用戶用戶使用TYPE_TOAST 、TYPE_PHONE、TYPE_SYSTEM_ALERT 如果設(shè)置為該類型會(huì)直接奔潰 必須修改為 TYPE_APPLICATION_OVERLAY

  2. WindowManager.LayoutParams.TYPE_PHONE (窗口類型和手機(jī)來電窗口級(jí)別一致)WindowManager.LayoutParams.TYPE_SYSTEM_ALERT(該窗口類型和與低電量系統(tǒng)提示窗口級(jí)別一致 )
    在6.0以前只需在Manifest中注冊(cè)SYSTEM_ALERT_WINDOW權(quán)限即可,6.0以后可以通過Setting.canDrawOverLays來判斷該權(quán)限授予情況 則需要條狀到設(shè)置頁面手動(dòng)授權(quán),6.0以前國(guó)內(nèi)廠商可能也需要?jiǎng)討B(tài)去授權(quán)改權(quán)限

  3. TYPE_APPLICATION_OVERLAY
    該窗口類型是Android8.0以后新增 為了統(tǒng)一之前版本對(duì)于用戶隨意使用懸浮窗導(dǎo)致混亂的情況 該窗口類型需要通過Setting.canDrawOverLays判斷是否授權(quán) 如果未授權(quán) 通過一下代碼跳轉(zhuǎn)到允許懸浮窗權(quán)限界面
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + this.getPackageName()));
    startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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