偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Android進(jìn)階之Dialog對(duì)應(yīng)的Context必須是Activity嗎?從源碼詳細(xì)分析

移動(dòng)開發(fā) Android
創(chuàng)建Dialog的時(shí)候知道在Dialog的構(gòu)造方法中需要一個(gè)上下文環(huán)境,而對(duì)這個(gè)“上下文”沒有具體的概念結(jié)果導(dǎo)致程序報(bào)錯(cuò),于是發(fā)現(xiàn)Dialog需要的上下文環(huán)境只能是activity。

[[419839]]

前言

創(chuàng)建Dialog的時(shí)候知道在Dialog的構(gòu)造方法中需要一個(gè)上下文環(huán)境,而對(duì)這個(gè)“上下文”沒有具體的概念結(jié)果導(dǎo)致程序報(bào)錯(cuò),

于是發(fā)現(xiàn)Dialog需要的上下文環(huán)境只能是activity。

所以接下來(lái)這篇文章將會(huì)從源碼的角度來(lái)徹底的理順這個(gè)問(wèn)題;

一、Dialog創(chuàng)建失敗

在Dialog的構(gòu)造方法中傳入一個(gè)Application的上下文環(huán)境??纯闯绦蚴欠駡?bào)錯(cuò):

  1. Dialog dialog = new Dialog(getApplication()); 
  2.      TextView textView = new TextView(this); 
  3.      textView.setText("使用Application創(chuàng)建Dialog"); 
  4.      dialog.setContentView(textView); 
  5.      dialog.show(); 

運(yùn)行程序,程序不出意外的崩潰了,我們來(lái)看下報(bào)錯(cuò)信息:

  1. Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application 
  2.     at android.view.ViewRootImpl.setView(ViewRootImpl.java:517) 
  3.     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:301) 
  4.     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:215) 
  5.     at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:140) 

這段錯(cuò)誤日志,有兩點(diǎn)我們需要注意一下

  • 程序報(bào)了一個(gè)BadTokenException異常;
  • 程序報(bào)錯(cuò)是在ViewRootImpl的setView方法中;
  • 我們一定很疑惑BadTokenException到底是個(gè)啥,在說(shuō)明這個(gè)之前我們首先需要了解Token,在了解了Token的概念之后,再結(jié)合ViewRootImpl的setView方法,就能理解BadTokenException這個(gè)到底是什么,怎么產(chǎn)生的;

二、Token分析

1、token詳解

Token直譯成中文是令牌的意思,android系統(tǒng)中將其作為一種安全機(jī)制,其本質(zhì)是一個(gè)Binder對(duì)象,在跨進(jìn)程的通行中充當(dāng)驗(yàn)證碼的作用。比如:在activity的啟動(dòng)過(guò)程及界面繪制的過(guò)程中會(huì)涉及到ActivityManagerService,應(yīng)用程序,WindowManagerService三個(gè)進(jìn)程間的通信,此時(shí)Token在這3個(gè)進(jìn)程中充當(dāng)一個(gè)身份驗(yàn)證的功能,ActivityManagerService與WindowManagerService通過(guò)應(yīng)用程序的activity傳過(guò)來(lái)的Token來(lái)分辨到底是控制應(yīng)用程序的哪個(gè)activity。具體來(lái)說(shuō)就是:

  • 在啟動(dòng)Activity的流程當(dāng)中,首先,ActivityManagerService會(huì)創(chuàng)建ActivityRecord由其本身來(lái)管理,同時(shí)會(huì)為這個(gè)ActivityRecord創(chuàng)建一個(gè)IApplication(本質(zhì)上就是一個(gè)Binder)。
  • ActivityManagerService將這個(gè)binder對(duì)象傳遞給WindowManagerService,讓W(xué)indowManagerService記錄下這個(gè)Binder。
  • 當(dāng)ActivityManagerService這邊完成數(shù)據(jù)結(jié)構(gòu)的添加之后,會(huì)返回給ActivityThread一個(gè)ActivityClientRecord數(shù)據(jù)結(jié)構(gòu),中間就包含了Token這個(gè)Binder對(duì)象。
  • ActivityThread這邊拿到這個(gè)Token的Binder對(duì)象之后,就需要讓W(xué)indowManagerService去在界面上添加一個(gè)對(duì)應(yīng)窗口,在添加窗口傳給WindowManagerService的數(shù)據(jù)中WindowManager.LayoutParams這里面就包含了Token。
  • 最終WindowManagerService在添加窗口的時(shí)候,就需要將這個(gè)Token的Binder和之前ActivityManagerService保存在里面的Binder做比較,驗(yàn)證通過(guò)說(shuō)明是合法的,否則,就會(huì)拋出BadTokenException這個(gè)異常。
  • 到這里,我們就知道BadTokenException是怎么回事了,然后接下來(lái)分析為什么使用Application上下文會(huì)報(bào)BadTokenException異常,而Activity上下文則不會(huì)
圖片

2、為什么非要一個(gè)Token

因?yàn)樵赪MS那邊需要根據(jù)這個(gè)Token來(lái)確定Window的位置(不是說(shuō)坐標(biāo)),如果沒有Token的話,就不知道這個(gè)窗口應(yīng)該放到哪個(gè)容器上了;

因?yàn)榉茿ctivity的Context它的WindowManger沒有ParentWindow,導(dǎo)致在WMS那邊找不到對(duì)應(yīng)的容器,也就是不知道要把Dialog的Window放置在何處。

還有一個(gè)原因是沒有SYSTEM_ALERT_WINDOW權(quán)限(當(dāng)然要加權(quán)限啦,DisplayArea.Tokens的子容器,級(jí)別比普通應(yīng)用的Window高,也就是會(huì)顯示在普通應(yīng)用Window的前面,如果不加權(quán)限控制的話,被濫用還得了)。

在獲得SYSTEM_ALERT_WINDOW權(quán)限并將Dialog的Window.type指定為SYSTEM_WINDOW之后能正常顯示,是因?yàn)閃MS會(huì)為SYSTEM_WINDOW類型的窗口專門創(chuàng)建一個(gè)WindowToken(這下就有容器了),并放置在DisplayArea.Tokens里面(這下知道放在哪里了);

三、創(chuàng)建dialog流程分析

1、activity的界面最后是通過(guò)ViewRootImpl的setView方法連接WindowManagerService,從而讓W(xué)indowManagerService將界面繪制到手機(jī)屏幕上。而從上面的異常日志中其實(shí)也可以看出,Dialog的界面也是通過(guò)ViewRootImpl的setView連接WindowManagerService,從而完成界面的繪制的。

我們首先來(lái)看Dialog的構(gòu)造方法。不管一個(gè)參數(shù)的構(gòu)造方法。兩個(gè)參數(shù)的構(gòu)造方法,最終都會(huì)調(diào)用到3個(gè)參數(shù)的構(gòu)造方法:

  1. Dialog(@NonNull Context context, @StyleRes int themeResId, boolean  
  2. createContextThemeWrapper) { 
  3.         ...... 
  4.         //1.創(chuàng)建一個(gè)WindowManagerImpl對(duì)象 
  5.         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 
  6.         //2.創(chuàng)建一個(gè)PhoneWindow對(duì)象 
  7.         final Window w = new PhoneWindow(mContext); 
  8.         mWindow = w; 
  9.         //3.使dialog能夠響應(yīng)用戶的事件 
  10.         w.setCallback(this); 
  11.         w.setOnWindowDismissedCallback(this); 
  12.         //4.為window對(duì)象設(shè)置WindowManager 
  13.         w.setWindowManager(mWindowManager, nullnull); 
  14.         w.setGravity(Gravity.CENTER); 
  15.         mListenersHandler = new ListenersHandler(this); 
  16.     } 

這段代碼可以看出dialog的創(chuàng)建實(shí)質(zhì)上和activity界面的創(chuàng)建沒什么兩樣,都需要完成一個(gè)應(yīng)用窗口Window的創(chuàng)建,和一個(gè)應(yīng)用窗口視圖對(duì)象管理者WindowManagerImpl的創(chuàng)建。

然后Dialog同樣有一個(gè)setContentView方法:

  1. public void setContentView(@LayoutRes int layoutResID) { 
  2.         mWindow.setContentView(layoutResID); 
  3.     } 
  4. 依然是調(diào)用PhoneWindow的setContentView方法。再接著我們來(lái)看下dialog的show方法: 
  5. public void show() { 
  6.         ...... 
  7.         //1.得到通過(guò)setView方法封裝好的DecorView  
  8.         mDecor = mWindow.getDecorView(); 
  9.         ...... 
  10.        //2.得到創(chuàng)建PhoneWindow時(shí)已經(jīng)初始化的成員變量WindowManager.LayoutParams 
  11.         WindowManager.LayoutParams l = mWindow.getAttributes(); 
  12.         if ((l.softInputMode 
  13.                 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { 
  14.             WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); 
  15.             nl.copyFrom(l); 
  16.             nl.softInputMode |= 
  17.                     WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; 
  18.             l = nl; 
  19.         } 
  20.         try { 
  21.             //3.通過(guò)WindowManagerImpl添加DecorView到屏幕 
  22.             mWindowManager.addView(mDecor, l); 
  23.             mShowing = true
  24.             sendShowMessage(); 
  25.         } finally { 
  26.         } 
  27.     } 

這段代碼和activity的makeVisable方法類似,這里也不多說(shuō)了,注釋已經(jīng)大概的寫清楚了。然后調(diào)用WindowManagerImpl的addView方法:

  1. @Override 
  2.     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { 
  3.         applyDefaultToken(params); 
  4.         mGlobal.addView(view, params, mDisplay, mParentWindow); 
  5.     } 
  6. 接著調(diào)用了WindowManagerGlobal的addView方法: 
  7. public void addView(View view, ViewGroup.LayoutParams params, 
  8.             Display display, Window parentWindow) { 
  9.         ...... 
  10.         //1.將傳進(jìn)來(lái)的ViewGroup.LayoutParams類型的params轉(zhuǎn)成  
  11. WindowManager.LayoutParams類型的wparams  
  12.         final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)  
  13. params; 
  14.        //2.如果WindowManagerImpl是在activity的方法中被創(chuàng)建則不為空 
  15.         if (parentWindow != null) { 
  16.             parentWindow.adjustLayoutParamsForSubWindow(wparams); 
  17.         } else { 
  18.         ...... 
  19.         } 
  20.         ViewRootImpl root; 
  21.         View panelParentView = null
  22.         synchronized (mLock) { 
  23.         ...... 
  24.             root = new ViewRootImpl(view.getContext(), display); 
  25.             view.setLayoutParams(wparams); 
  26.             //3.將視圖對(duì)象view,ViewRootImpl以及wparams分別存入相應(yīng)集合的對(duì)應(yīng)位置 
  27.             mViews.add(view); 
  28.             mRoots.add(root); 
  29.             mParams.add(wparams); 
  30.         } 
  31.         // do this last because it fires off messages to start doing things 
  32.         try { 
  33.             //4.通過(guò)ViewRootImpl聯(lián)系WindowManagerService將view繪制到屏幕上 
  34.             root.setView(view, wparams, panelParentView); 
  35.         } catch (RuntimeException e) { 
  36.             // BadTokenException or InvalidDisplayException, clean up. 
  37.             synchronized (mLock) { 
  38.                 final int index = findViewLocked(viewfalse); 
  39.                 if (index >= 0) { 
  40.                     removeViewLocked(indextrue); 
  41.                 } 
  42.             } 
  43.             throw e; 
  44.         } 
  45.     } 
  1. //2.如果WindowManagerImpl是在activity的方法中被創(chuàng)建則不為空    
  2.   if (parentWindow != null) { 
  3.            parentWindow.adjustLayoutParamsForSubWindow(wparams); 
  4.        } else { 
  5.        ...... 
  6.        } 

2、這里會(huì)首先判斷一個(gè)類型為Window的parentWindow 是否為空,如果不為空會(huì)通過(guò)Window的adjustLayoutParamsForSubWindow方法調(diào)整一個(gè)類型為WindowManager.LayoutParams的變量wparams的一些屬性值。應(yīng)用程序請(qǐng)求WindowManagerService服務(wù)時(shí)會(huì)傳入一個(gè)Token,其實(shí)那個(gè)Token就會(huì)通過(guò)Window的adjustLayoutParamsForSubWindow方法存放在wparams的token變量中,也就是說(shuō)如果沒有調(diào)用Window的adjustLayoutParamsForSubWindow方法就會(huì)導(dǎo)致wparams的token變量為空。然后我們接下來(lái)看一下wparams的token變量是如何賦值的:

  1. void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) { 
  2.         CharSequence curTitle = wp.getTitle(); 
  3.         if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 
  4.             wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 
  5.         ...... 
  6.         } else { 
  7.             if (wp.token == null) { 
  8.                 wp.token = mContainer == null ? mAppToken : mContainer.mAppToken; 
  9.             } 
  10.         ...... 
  11.         } 
  12.         if (wp.packageName == null) { 
  13.             wp.packageName = mContext.getPackageName(); 
  14.         } 
  15.         if (mHardwareAccelerated) { 
  16.             wp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 
  17.         } 

這里我們可以看到這段代碼首先會(huì)做一個(gè)判斷如果wp.type的值有沒有位于WindowManager.LayoutParams.FIRST_SUB_WINDOW與WindowManager.LayoutParams.LAST_SUB_WINDOW之間,如果沒有則會(huì)給wp.token賦值。wp.type代表窗口類型,有3種級(jí)別,分別為系統(tǒng)級(jí),應(yīng)用級(jí)以及子窗口級(jí)。而這里是判斷是否為子窗口級(jí)級(jí)別。而Dialog的WindowManager.LayoutParams.type默認(rèn)是應(yīng)用級(jí)的,因此會(huì)走else分支,給wp.token賦值mAppToken。至于mAppToken是什么,我們待會(huì)再來(lái)分析。

3、看WindowManagerGlobal的addView方法的,會(huì)調(diào)用ViewRootImpl的setView方法,我們來(lái)看一下,ViewRootImpl是如何連接WindowManagerService傳遞token的:

  1. public void setView(View view, WindowManager.LayoutParams attrs, View  
  2. panelParentView) { 
  3.         synchronized (this) { 
  4.             if (mView == null) { 
  5.                 mView = view
  6.                 try { 
  7.                     ...... 
  8.                     //1.通過(guò)binder對(duì)象mWindowSession調(diào)用WindowManagerService的接口請(qǐng)求 
  9.                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, 
  10.                             getHostVisibility(), mDisplay.getDisplayId(), 
  11.                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 
  12.                             mAttachInfo.mOutsets, mInputChannel); 
  13.                 } catch (RemoteException e) { 
  14.                     ...... 
  15.                     throw new RuntimeException("Adding window failed", e); 
  16.                 } finally { 
  17.                     if (restore) { 
  18.                         attrs.restore(); 
  19.                     } 
  20.                 } 
  21.                     ...... 
  22.                 if (res < WindowManagerGlobal.ADD_OKAY) { 
  23.                     ...... 
  24.                     switch (res) { 
  25.                         case WindowManagerGlobal.ADD_BAD_APP_TOKEN: 
  26.                         case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN: 
  27.                             throw new WindowManager.BadTokenException( 
  28.                                     "Unable to add window -- token " + attrs.token 
  29.                                     + " is not valid; is your activity running?"); 
  30.                         //2.如果請(qǐng)求失敗(token驗(yàn)證失?。﹦t拋出BadTokenException異常 
  31.                         case WindowManagerGlobal.ADD_NOT_APP_TOKEN: 
  32.                             throw new WindowManager.BadTokenException( 
  33.                                     "Unable to add window -- token " + attrs.token 
  34.                                     + " is not for an application"); 
  35.                         case WindowManagerGlobal.ADD_APP_EXITING: 
  36.                             throw new WindowManager.BadTokenException( 
  37.                                     "Unable to add window -- app for token " +  
  38. attrs.token 
  39.                                     + " is exiting"); 
  40.                         case WindowManagerGlobal.ADD_DUPLICATE_ADD: 
  41.                             throw new WindowManager.BadTokenException( 
  42.                                     "Unable to add window -- window " + mWindow 
  43.                                     + " has already been added"); 
  44.                         case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED: 
  45.                             // Silently ignore -- we would have just removed it 
  46.                             // right away, anyway. 
  47.                             return
  48.                         case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON: 
  49.                             throw new WindowManager.BadTokenException( 
  50.                                     "Unable to add window " + mWindow + 
  51.                                     " -- another window of this type already  
  52. exists"); 
  53.                         case WindowManagerGlobal.ADD_PERMISSION_DENIED: 
  54.                             throw new WindowManager.BadTokenException( 
  55.                                     "Unable to add window " + mWindow + 
  56.                                     " -- permission denied for this window type"); 
  57.                         case WindowManagerGlobal.ADD_INVALID_DISPLAY: 
  58.                             throw new WindowManager.InvalidDisplayException( 
  59.                                     "Unable to add window " + mWindow + 
  60.                                     " -- the specified display can not be found"); 
  61.                         case WindowManagerGlobal.ADD_INVALID_TYPE: 
  62.                             throw new WindowManager.InvalidDisplayException( 
  63.                                     "Unable to add window " + mWindow 
  64.                                     + " -- the specified window type is not valid"); 
  65.                     } 
  66.                     throw new RuntimeException( 
  67.                             "Unable to add window -- unknown error code " + res); 
  68.                 } 
  69.         ...... 
  70.             } 
  71.         } 
  72.     } 

這段代碼有兩處需要注意:

  • 會(huì)通過(guò)一個(gè)mWindowSession的binder對(duì)象請(qǐng)求WindowManagerService服務(wù),傳遞一個(gè)類型為WindowManager.LayoutParams的變量mWindowAttributes到WindowManagerService,mWindowAttributes里面裝有代表當(dāng)前activity的token對(duì)象。然后通過(guò)WindowManagerService服務(wù)創(chuàng)建屏幕視圖。
  • 會(huì)根據(jù)請(qǐng)求WindowManagerService服務(wù)的返回結(jié)果判斷是否請(qǐng)求成功,如果請(qǐng)求失敗會(huì)拋出異常,注釋的地方就是在文章開頭示例拋出的異常。此時(shí)attrs.token為空。如果創(chuàng)建dialog的上下文環(huán)境改為activity的為什么就不為空呢?

四、分析創(chuàng)建Dialog的上下文Activity為何與眾不同

1、上文的分析中可以看出attrs.token的賦值在Window的adjustLayoutParamsForSubWindow方法中。而Dialog默認(rèn)的WindowManager.LayoutParams.type是應(yīng)用級(jí)別的,因此,如果能進(jìn)入這個(gè)方法內(nèi),attrs.token肯定能被賦值?,F(xiàn)在只有一種情況,如果不是activity的上下文環(huán)境就沒有進(jìn)入到這個(gè)方法內(nèi)。這時(shí)我們?cè)倏碬indowManagerGlobal的addView方法的:

  1. public void addView(View view, ViewGroup.LayoutParams params, 
  2.             Display display, Window parentWindow) { 
  3.         ...... 
  4.        //2.如果WindowManagerImpl是在activity的方法中被創(chuàng)建則不為空 
  5.         if (parentWindow != null) { 
  6.             parentWindow.adjustLayoutParamsForSubWindow(wparams); 
  7.         } else { 
  8.         ...... 
  9.         } 
  10.         ...... 
  11.     } 

從這里看出如果Window類型的parentWindow為空,就不會(huì)進(jìn)入adjustLayoutParamsForSubWindow方法。從而可以得出結(jié)論如果不是activity的上下文環(huán)境WindowManagerGlobal的第四個(gè)參數(shù)parentWindow為空。緊接著我們?cè)賮?lái)分析為什么其他的上下文會(huì)導(dǎo)致parentWindow為空。

WindowManagerGlobal調(diào)用addView方法在WindowManagerImpl的addView方法中:

  1.  @Override 
  2.     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { 
  3.         applyDefaultToken(params); 
  4.         mGlobal.addView(view, params, mDisplay, mParentWindow); 
  5.     } 
  6. WindowManagerImpl的addView方法在Dialog的首位方法中調(diào)用: 
  7. public void show() { 
  8.         ...... 
  9.         try { 
  10.             mWindowManager.addView(mDecor, l); 
  11.             mShowing = true
  12.             sendShowMessage(); 
  13.         } finally { 
  14.         } 
  15.     } 

對(duì)比這兩個(gè)方法。可以看出WindowManagerImpl的addView方法調(diào)用WindowManagerGlobal的addView方法是多出來(lái)了兩個(gè)參數(shù)mDisplay, mParentWindow,我們只看后一個(gè),多了一個(gè)Window類型的mParentWindow,可以一mParentWindow并不是在Dialog的show方法中賦值的。那么它在哪賦值呢?在WindowManagerImpl類中搜索mParentWindow發(fā)現(xiàn)它在WindowManagerImpl的兩個(gè)參數(shù)的構(gòu)造方法中被賦值。從這里我們可以猜測(cè),如果是使用的activity上下文,那么在創(chuàng)建WindowManagerImpl實(shí)例的時(shí)候用的是兩個(gè)參數(shù)的構(gòu)造方法,而其他的上下文是用的一個(gè)參數(shù)的構(gòu)造方法?,F(xiàn)在問(wèn)題就集中到了WindowManagerImpl是如何被創(chuàng)建的了。

我們?cè)倩剡^(guò)頭來(lái)看Dialog的構(gòu)造方法中WindowManagerImpl是如何創(chuàng)建的:

  1. Dialog(@NonNull Context context, @StyleRes int themeResId, boolean  
  2. createContextThemeWrapper) { 
  3.         ...... 
  4.         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 
  5.         ...... 
  6.     } 
  7. 然后分別查看activity的getSystemService方法,和Application的getSystemService方法: 
  8. activity的getSystemService方法 
  9. @Override 
  10.    public Object getSystemService(@ServiceName @NonNull String name) { 
  11.        ...... 
  12.        if (WINDOW_SERVICE.equals(name)) { 
  13.            return mWindowManager; 
  14.        } else if (SEARCH_SERVICE.equals(name)) { 
  15.            ensureSearchManager(); 
  16.            return mSearchManager; 
  17.        } 
  18.        return super.getSystemService(name); 
  19.    } 

在這個(gè)方法中直接返回了activity的mWindowManager對(duì)象,activity的mWindowManager對(duì)象在activity的attach方法中:

  1. final void attach(Context context, ActivityThread aThread, 
  2.             Instrumentation instr, IBinder token, int ident, 
  3.             Application application, Intent intent, ActivityInfo info, 
  4.             CharSequence title, Activity parent, String id, 
  5.             NonConfigurationInstances lastNonConfigurationInstances, 
  6.             Configuration config, String referrer, IVoiceInteractor voiceInteractor) { 
  7.       ...... 
  8.       mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(), 
  9.                 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); 
  10.        ......   
  11.     } 

2、我們?cè)倏碬indow的setWindowManager方法:

  1. public void setWindowManager(WindowManager wm, IBinder appToken, String appName, 
  2.             boolean hardwareAccelerated) { 
  3.         //1.將ActivityManagerService傳過(guò)來(lái)的Token保存到mAppToken中 
  4.         mAppToken = appToken; 
  5.         //2.創(chuàng)建WindowManagerImpl 
  6.         mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); 
  7.     } 

這段代碼兩個(gè)地方需要注意,一是前ActivityManagerService傳過(guò)來(lái)的Token賦值給Winow的mAppToken,這個(gè)token最后會(huì)保存到attr.token,具體操作在Window的adjustLayoutParamsForSubWindow方法中。二是調(diào)用WindowManagerImpl的createLocalWindowManager方法創(chuàng)建WindowManagerImpl:

  1. public WindowManagerImpl createLocalWindowManager(Window parentWindow) { 
  2.         return new WindowManagerImpl(mDisplay, parentWindow); 
  3.     } 

到這里就可以看出如果創(chuàng)建Dialog的上下文是activity,則會(huì)調(diào)用WindowManagerImpl兩個(gè)參數(shù)的構(gòu)造方法,從而導(dǎo)致parentWindow不為空。

3、Application的getSystemService方法:

由于Application是Context的子類,所以Application的getSystemService最終會(huì)調(diào)到ContextImpl的getSystemService方法

  1. @Override 
  2.     public Object getSystemService(String name) { 
  3.         return SystemServiceRegistry.getSystemService(this, name); 
  4.     } 
  5. 直接調(diào)用了SystemServiceRegistry的getSystemService方法,這個(gè)方法又會(huì)得到匿名內(nèi)部類CachedServiceFetcher<WindowManager>的createService方法的返回值。 
  6.  @Override 
  7.             public WindowManager createService(ContextImpl ctx) { 
  8.                 return new WindowManagerImpl(ctx.getDisplay()); 
  9.             }}); 

從這個(gè)方法中可以看出上下文為Application時(shí),調(diào)用的是WindowManagerImpl的一個(gè)參數(shù)的構(gòu)造方法,從而parentWindow為空;

總結(jié)

  • 創(chuàng)建dialog時(shí),如果傳入構(gòu)造方法不是一個(gè)activity類型的上下文,則導(dǎo)致WindowManagerImpl類型為Window的變量mParentWindow,從而導(dǎo)致WindowManagerGlobal的addView不會(huì)調(diào)用Window的adjustLayoutParamsForSubWindow方法,從而不會(huì)給attr.token賦值,導(dǎo)致在WindowManagerService服務(wù)中的身份驗(yàn)證失敗,拋出BadTokenException異常;
  • Show一個(gè)普通的Dialog需要的并不是Activity本身,而是一個(gè)容器的token,我們平時(shí)會(huì)傳Activity,只不過(guò)是Activity剛好對(duì)應(yīng)WMS那邊的一個(gè)WindowState的容器而已;

本文轉(zhuǎn)載自微信公眾號(hào)「Android開發(fā)編程」

 

責(zé)任編輯:姜華 來(lái)源: Android開發(fā)編程
相關(guān)推薦

2021-08-28 07:48:04

AndroidActivityRecActivitySta

2021-08-19 06:58:49

Android場(chǎng)景FirstActivi

2009-06-18 14:00:51

2009-09-25 14:23:39

2009-09-28 10:39:01

Hibernate基礎(chǔ)

2009-03-24 08:30:54

AndroidGoogle移動(dòng)os

2009-03-24 09:17:58

驅(qū)動(dòng)GSMAndroid

2013-12-04 10:21:38

AndroidAndroidMani

2010-04-26 18:17:19

Oracle存儲(chǔ)過(guò)程

2010-02-06 15:19:35

2009-12-03 17:41:40

PHP應(yīng)用發(fā)展

2021-10-25 19:52:52

IntentFilte

2009-09-09 09:48:43

Linq延遲加載

2009-09-14 16:21:34

LINQ To XML

2009-10-10 13:52:57

VB Update方法

2009-09-14 13:50:35

LINQ編程模型

2009-09-08 15:56:50

Linq使用Group

2010-01-06 13:50:37

.NET Framew

2009-11-20 13:11:44

Oracle XML數(shù)

2010-01-06 16:12:52

分析Json stri
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)