參考資料
凱子哥帶你學(xué)Framework· Activity界面顯示全解析-上
凱子哥帶你學(xué)Framework Activity界面顯示全解析-下
重要:?。?! Android 屏幕刷新機制
目錄
- 1)簡介
- 2)Window的內(nèi)部機制
- 2.1)Window的添加過程
- 2.2)Window的刪除過程
- 2.3)Window的更新過程
- 3)Window的創(chuàng)建過程
- 3.1)Activity的Window創(chuàng)建過程
- 3.2)Dialog的Window創(chuàng)建過程
- 3.3)Toast的Window創(chuàng)建過程
1)簡介
- Window是一個抽象類,具體實現(xiàn)為PhoneWindow
- 只需要WindowManager即可創(chuàng)建一個Window
- WindowManager是個接口,是外界訪問Window的入口,具體實現(xiàn)位于WindowManagerService(WMS),WindowManager和WindowManagerService的交互是一個IPC過程
- Android所有視圖都是附加在Window上,通過Window來呈現(xiàn),因此Window是View的直接管理者。如事件分發(fā)就是通過Window傳遞給DecorView。
- 站在系統(tǒng)角度,Window代表一塊顯示區(qū)域,系統(tǒng)并不關(guān)心Window內(nèi)的繪制內(nèi)容
//WindowManager可以通過下面兩種方式進行獲取
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager mWindowManager = (WindowManager) getWindowManger();
//WindowManager添加一個Window
mWindowManager.addView(mButton, mLayoutParams);
2)Window的內(nèi)部機制
Window是以View的形式存在,View的繪制流程從ViewRoot開始,ViewRoot對應(yīng)ViewRootImpl類,它是連接WindowManager和DecorView的紐帶。
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
->ViewManager.addView
->WindowManager.addView(也是接口,繼承ViewManager)
->WindowManagerImpl.addView(實現(xiàn)了WindowManager接口)
->WindowManagerGlobal.addView(接受WindowManagerImpl的委托,并構(gòu)建ViewRootImpl)
->ViewRootImpl.setView(ViewRooImpl是WindowManagerGlobal的addView方法中新建的對象)
->requestLayout(setView中的一個方法調(diào)用,調(diào)用performTraversals繪制整個view樹)
->WindowSession.addToDisplay(在setView方法中,它是一個Binder對象,用于與WindowManagerService進行IPC通信)
->Session(WindowSession的具體實現(xiàn))
->WindowManagerService(實現(xiàn)Window的添加)
ViewRootImpl負責(zé)管理視圖樹和與WMS交互,它是WindowManager和DecorView的紐帶,與WMS交互是通過WindowSession。而且ViewRootImpl也負責(zé)UI界面的布局與渲染,負責(zé)把一些事件分發(fā)至Activity,以便Activity可以截獲事件。大多數(shù)情況下,它管理Activity頂層視圖DecorView,它相當(dāng)于MVC模型中的Controller。
3)Window的創(chuàng)建過程
3.1)Activity的Window創(chuàng)建過程
| Activity | 四大組件之一, 是存放View對象的容器,也是我們界面的載體,可以用來展示一個界面。它有一個SetContentView()方法 ,可以將我們定義的布局設(shè)置到界面上 |
| View | 就是一個個視圖的對象 |
| Window | 抽象類,是一個頂層的窗口,它的唯一實例是PhoneWindow它提供標準的用戶界面策略,如背景、標題、區(qū)域,默認按鍵處理等 |
Activity就像是一扇貼著窗花的窗口,Window就想上窗口上面的玻璃,而View對象就像一個個貼在玻璃上的窗花。
- Activity最終會由ActivityThread中的performLaunchActivity來完成啟動,此方法內(nèi)會通過類加載器創(chuàng)建Activity的實例對象,并調(diào)用attach關(guān)聯(lián)所依賴的上下文。
- 在attach方法中,系統(tǒng)會創(chuàng)建Activity所屬的Window對象,并獲取了WindowManager對象。
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);/設(shè)置回調(diào)函數(shù),使得Activity可以處理一些事件,如dispatchTouchEvent()
- setContentView()調(diào)用了Window的setContentView(),將界面繪制交給了Window對象,也就是View通過Activity添加到了Window上面。
- DecorView是PhoneWindow的內(nèi)部類,繼承自FrameLayout,是最底層的界面
- 如果沒有DecorView,則創(chuàng)建它
- 將View添加到DecorView的mContentParent對象中
- 回調(diào)Activity的onContentChanged()通知Activity視圖已改變
- 經(jīng)過上面步驟DecorView已初始化,且Activity的布局也被添加到了DecorView的mContentParent對象中。但這時DecorView還沒有被WindowManager添加到Window中,
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
在Activity的onResume()中,WindowManager會執(zhí)行addView(mDecorView,getWindow().getAttributes())。至此Activity的視圖才能被用戶看見。

3.2)Dialog的Window創(chuàng)建過程
AlertDialog和Activity一樣,內(nèi)部有一個Window,我們構(gòu)造AlertDialog.Builder,通過Builder設(shè)置Dialog各種屬性,,這些參數(shù)會被放在一個名為P(AlertController類型)的變量中,在調(diào)用AlertDialog.Builder.create方法的時候,內(nèi)部首先會new一個 AlertDialog,AlertDialog的父類Dialog的構(gòu)造函數(shù)中會new一個PhoneWindow賦值給AlertDialog中的Window,并且為它設(shè)置了回調(diào)。AlertDialog創(chuàng)建之后執(zhí)行apply方法,將P中的參數(shù)設(shè)置賦值給Dialog,后我們調(diào)用Dialog.show方法展示窗口,內(nèi)部調(diào)用dispatchOnCreate,最終會走到setContentView,到此Dialog的Window和Dialog視圖關(guān)聯(lián)到了一起,最后執(zhí)行mWindowManager.addView方法,通過WindowManager將DecorView添加到Window之中,此時Dialog顯示在了我們面前。
- 在Activity中使用Dialog的時候,為什么有時候會報錯“Unable to add window -- token is not valid; is your activity running?”?
答:
一般發(fā)生在Activity進入后臺,Dialog沒有主動Dismiss掉,然后從后臺再次進入App的時候。
Window分為三種,子窗口,應(yīng)用窗口和系統(tǒng)窗口,子窗口必須依附于一個上下文,就是Activity,因為它需要Activity的appToken,
子窗口的window,比如Dialog,想要顯示必須保證appToken與Activity保持一致,當(dāng)Activity銷毀,再次回來的時候,Dialog試圖重新創(chuàng)建,調(diào)用ViewRootImpl的setView()的時候會出問題,所以當(dāng)Activity不可見的時候,主動Dismiss掉Dialog,否則會因為appToken為空crash。 - 在子線程中為什么不能顯示Toast?
Toast源碼是與NotificationManagerService進行IPC通信,
show()的時候,通過handler來接收,子線程中沒有handler,所以無法顯示??梢越o子線程添加Looper
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
- 為什么不能在setContentView()之后設(shè)置某些Window屬性標志?
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();//初始化window屬性
}
在Activity.setContentView()之后,Window的一些特征位將被鎖定