Android?Framework?Window體系教程
前言
大家好,本篇文章來由是最近工作和面試需要,特定結(jié)合自身源碼以及大佬們的經(jīng)驗(yàn),寫的一篇關(guān)于Android framework層:WindowManager體系的講解。
本篇文章是Android framework的第一講《WindowManager體系-上》,重在講解Window在被添加到WindowManagerService前的流程。
Android Window體系
Android中的窗口體系可以分為下面三部分:
- 1.Window:可以簡單理解為一個(gè)窗口,這個(gè)窗口承載了需要繪制的View,他是一個(gè)抽象類,具體實(shí)現(xiàn)類是
PhoneWindow
。 - 2.WindowManager:也是一個(gè)接口類,繼承
ViewManager
,用來對(duì)Window進(jìn)行管理, 實(shí)現(xiàn)類:WindowManagerImpl
,其對(duì)Window的操作具體是通過WMS來實(shí)現(xiàn)的。理解為一個(gè)app層面的WMS和Window的中間人。 - 3.WindowManagerService(AMS):是系統(tǒng)服務(wù)中的一重要成員,WindowManager和WMS通過binder進(jìn)行通訊,真正對(duì)Window添加,更新,刪除等操作的執(zhí)行者。
三者之間關(guān)系:
前面分析了窗口體系中的類關(guān)系,下面我們從源碼角度和分析下:
為了大家不會(huì)迷路在源碼中,筆者會(huì)根據(jù)面試中可能被問到的幾個(gè)問題出發(fā),有目的性的介紹。
- 問題1:Window和Activity以及WindowManager什么時(shí)候建立的關(guān)系?
- 問題2:Window什么時(shí)候和View進(jìn)行關(guān)聯(lián)?
- 問題3:Window有哪些屬性?類型?層級(jí)關(guān)系?z-order?Window標(biāo)志?軟鍵盤模式都了解么?
- 問題4:View是如何一步一步添加到屏幕上的?更新?刪除呢?
那么就開始我們的源碼(mo)遨(yu)游吧。
1.Window和Activity以及WindowManager什么時(shí)候建立的關(guān)系?
前面幾篇文章我們分析過:Activity在啟動(dòng)的過程中,會(huì)調(diào)用它的attach方法進(jìn)行Window的創(chuàng)建。 那就直接定位到Activity的attach方法:
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 = new PhoneWindow(this, window, activityConfigCallback);//1 ... mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//2 }
先看attach注釋1處:創(chuàng)建了一個(gè)PhoneWindow,并傳入當(dāng)前Activity的this對(duì)象, PhoneWindow類,繼承了Window
public class PhoneWindow extends Window { public PhoneWindow(Context context, Window preservedWindow, ActivityConfigCallback activityConfigCallback) { //這里最終會(huì)調(diào)父類Window的構(gòu)造方法,并傳入Activity的實(shí)例context。 this(context); //只有Activity的Window才會(huì)使用Decor context,其他依賴使用的Context mUseDecorContext = true; ... } }
Window構(gòu)造方法:
public Window(Context context) { mContext = context; mFeatures = mLocalFeatures = getDefaultFeatures(context); }
PhoneWindow的構(gòu)造方法中傳入當(dāng)前Activity對(duì)象, 并最終在父類Window構(gòu)造方法中給mContext賦值,Window得到Activity的引用,由此就建立了Window和Activity的聯(lián)系。
下面繼續(xù)看attach注釋2:mWindow.setWindowManager
setWindowManager在PhoneWindow的父類Window中實(shí)現(xiàn):
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { //如果wm是空,則創(chuàng)建一個(gè)WindowManager服務(wù):注釋1 if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } //注釋2 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }
進(jìn)入setWindowManager的注釋1:
wm =(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
這里的mContext是ContextImpl類的對(duì)象:
@Override public Object getSystemService(String name) { return SystemServiceRegistry.getSystemService(this, name); } 進(jìn)入SystemServiceRegistry的getSystemService方法,name = Context.WINDOW_SERVICE public static Object getSystemService(ContextImpl ctx, String name) { ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);//1 return fetcher != null ? fetcher.getService(ctx) : null;//2 }
先看getSystemService方法的注釋1: SYSTEM_SERVICE_FETCHERS是一個(gè)HashMap的集合,在哪里初始化的?
我們注意到SystemServiceRegistry的static 方法,這個(gè)方法內(nèi)部注冊(cè)了很多服務(wù),我們可以在方法里面找到name = Context.WINDOW_SERVICE的服務(wù)
PS:static方法是在類加載的時(shí)候進(jìn)行初始化的,所以在整個(gè)類使用過程中只會(huì)執(zhí)行一次。
static { ... registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { @Override public WindowManager createService(ContextImpl ctx) { return new WindowManagerImpl(ctx); }}); }
來看下registerService方法:
private static <T> void registerService(String serviceName, Class<T> serviceClass, ServiceFetcher<T> serviceFetcher)
第三個(gè)參數(shù)是ServiceFetcher類型對(duì)象,實(shí)現(xiàn)的createService方法返回了一個(gè)WindowManagerImpl對(duì)象。
所以getSystemService方法的注釋1:fetcher = CachedServiceFetcher對(duì)象,且其createService方法返回了一個(gè)WindowManagerImpl對(duì)象
回到getSystemService注釋2處:fetcher.getService(ctx)最終會(huì)返回調(diào)用到createService方,返回WindowManagerImpl對(duì)象。
繼續(xù)回到Window的setWindowManager方法的注釋2處:
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this),
進(jìn)入WindowManagerImpl的createLocalWindowManager方法:
public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); }
這里也是創(chuàng)建了一個(gè)WindowManagerImpl,和前面getSystemService創(chuàng)建的WindowManagerImpl區(qū)別: 這里傳入了parentWindow,讓W(xué)indowManagerImpl持有了Window的引用,可以對(duì)此Window進(jìn)行操作了。
總結(jié)下Activity的attach方法:
- 1.new一個(gè)PhoneWindow對(duì)象并傳入當(dāng)前Activity引用,建立Window和Activity的一一對(duì)應(yīng)關(guān)系。此Window是Window類的子類PhoneWindow的實(shí)例。Activity在Window中是以mContext屬性存在。
- 2.調(diào)用PhoneWindow的setWindowManager方法,在這個(gè)方法中讓W(xué)indow和WindowManager建立了一一關(guān)系。此WindowManager是WindowManagerImpl的實(shí)例、
筆者畫了一張Window關(guān)系類圖:
2.Window什么時(shí)候和View進(jìn)行關(guān)聯(lián)?
我們來看Activity的setContentView方法
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID);//1 initWindowDecorActionBar();//2 }
注釋1中的getWindow前面分析了是:PhoneWindow對(duì)象 進(jìn)入PhoneWindow的setContentView方法:
public void setContentView(int layoutResID) { ... if (mContentParent == null) { installDecor();//注釋1 } mLayoutInflater.inflate(layoutResID, mContentParent);//注釋2 ... } private void installDecor() { ... if (mDecor == null) { mDecor = generateDecor(-1); } else { mDecor.setWindow(this); } } protected DecorView generateDecor(int featureId) { ... return new DecorView(context, featureId, this, getAttributes()); }
PhoneWindow:setContentView注釋1處:創(chuàng)建一個(gè)DecorView給當(dāng)前Window的mDecor。
PhoneWindow:setContentView注釋2處:解析當(dāng)前l(fā)ayoutResID為id的xml文件并傳遞給mContentParent。
這里的mContentParent其實(shí)就是當(dāng)前Window對(duì)應(yīng)的ContentView。
繼續(xù)回到Activity的setContentView方法注釋2處:initWindowDecorActionBar這里是初始化Window的ActionBar
private void initWindowDecorActionBar() { Window window = getWindow(); // Initializing the window decor can change window feature flags. // Make sure that we have the correct set before performing the test below. window.getDecorView(); if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) { return; } mActionBar = new WindowDecorActionBar(this); mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp); ... }
通過上面兩步驟:我們就給當(dāng)前Activity的Window創(chuàng)建了一個(gè)DecorView,這個(gè)DecorView就是當(dāng)前Window的rootView、并對(duì)rootView的ContentView設(shè)置了mContentParent 實(shí)現(xiàn)了將Window和DecorView綁定。
如果你夠細(xì)心,你會(huì)發(fā)現(xiàn)Activity所有的對(duì)View進(jìn)行的操作幾乎都是通過Window進(jìn)行的。而我們的Activity又是通過AMS來控制生命周期,所以AMS和View的通訊其實(shí)就是通過WindowManager這個(gè)介子進(jìn)行的、
比如:
//1.設(shè)置主題 public void setTheme(int resid) { super.setTheme(resid); mWindow.setTheme(resid); } //2.設(shè)置Window屬性等 public final boolean requestWindowFeature(int featureId) { return getWindow().requestFeature(featureId); } //3.獲取Layout解析器 public LayoutInflater getLayoutInflater() { return getWindow().getLayoutInflater(); }
3.Window有哪些屬性?類型?層級(jí)關(guān)系?z-order?Window標(biāo)志?
軟鍵盤模式都了解么
上面我們說過了Window、WindowManager和WMS之間的體系關(guān)系,WMS就好比老板,Window就好比員工,老板為了方便管理員工,就給員工定了很多規(guī)則.
這些規(guī)則就是Window的屬性:這些屬性都定義在WindowManager.LayoutParams類中。
應(yīng)用開發(fā)中比較常見的,主要有以下幾類:
- 1.Window窗口類型(Type)
- 2.Window窗口層級(jí)關(guān)系(Z-Order)
- 3.Window窗口標(biāo)志(Flag)
- 4.Window 軟鍵盤模式(SoftInputModel)
下面我們就分別來講解下這幾種屬性
Window窗口類型(Type)
Window窗口類型有很多種:比如:應(yīng)用程序窗口,PopupWindow,Toast,Dialog,輸入法窗口,系統(tǒng)錯(cuò)誤窗口等。總體來說主要分為以下三大類:
- 1.應(yīng)用程序窗口(Application Window)
- 2.子窗口(Sub Window)
- 3.系統(tǒng)窗口(System Window)
窗口類型在WindowManager的靜態(tài)內(nèi)部類LayoutParams中定義
應(yīng)用程序窗口
應(yīng)用程序窗口指我們應(yīng)用程序使用的(非Dialog)界面Window,如Activity就是一個(gè)典型的應(yīng)用窗口。
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { //應(yīng)用程序窗口的起始值 public static final int FIRST_APPLICATION_WINDOW = 1; //應(yīng)用程序窗口的基準(zhǔn)值 public static final int TYPE_BASE_APPLICATION = 1; //標(biāo)準(zhǔn)的應(yīng)用程序窗口:依附于Activity存在 public static final int TYPE_APPLICATION = 2; public static final int TYPE_APPLICATION_STARTING = 3; public static final int TYPE_DRAWN_APPLICATION = 4; //應(yīng)用程序窗口結(jié)束值 public static final int LAST_APPLICATION_WINDOW = 99; ... }
應(yīng)用程序窗口起始值為1,結(jié)束值為99,說明我們應(yīng)用程序窗口的類型為:1~99
. 這個(gè)值直接影響了窗口在屏幕中的層級(jí)關(guān)系,后面再講。
子窗口
子窗口看名字就知道其是一個(gè)窗口的子窗口,所以不能獨(dú)立存在,需要依附于父窗口存在,比如PopupWindow,Dialog等就屬于子窗口,子窗口的類型定義如下所示:
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { public static final int FIRST_SUB_WINDOW = 1000;//子窗口類型初始值 public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW; public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1; public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2; 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 TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5; public static final int LAST_SUB_WINDOW = 1999;//子窗口類型結(jié)束值 ... }
子窗口的Type值范圍為1000到1999
.
系統(tǒng)窗口
輸入法窗口,系統(tǒng)錯(cuò)誤提示窗口,Toast等都屬于系統(tǒng)窗口,系統(tǒng)窗口的類型定義如下所示:
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { public static final int FIRST_SYSTEM_WINDOW = 2000;//系統(tǒng)窗口類型初始值 public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//系統(tǒng)狀態(tài)欄窗口 public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//搜索條窗口 public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//通話窗口 public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//系統(tǒng)ALERT窗口 public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//鎖屏窗口 public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//TOAST窗口 ... public static final int LAST_SYSTEM_WINDOW = 2999;//系統(tǒng)窗口類型結(jié)束值 }
系統(tǒng)窗口的Type值范圍為2000到2999
。
對(duì)于不同類型的窗口添加過程會(huì)有所不同,但是對(duì)于WMS處理部分,添加的過程基本上是一樣的, WMS對(duì)于這三大類的窗口基本是“一視同仁”的。
Window窗口層級(jí)關(guān)系(Z-Order)
首先我們得有個(gè)概念就是窗口是疊加的,可以簡單理解為:幾頁紙按前后順序疊加在一塊。
我們可以將手機(jī)屏幕使用X,Y,Z軸來表示,X,Y表示屏幕的長和寬,而Z軸垂直于屏幕,從屏幕內(nèi)指向外。
確定窗口的層級(jí)關(guān)系其實(shí)就是確定窗口在Z軸上的位置,這個(gè)層級(jí)關(guān)系就是Z-Order,而Z-Order的排序依據(jù)就是前面分析的Window類型值。
類型值越大,越靠近用戶。通過上面分析可知:系統(tǒng)窗口>子窗口>應(yīng)用程序窗口。 這也就是系統(tǒng)窗口會(huì)覆蓋應(yīng)用窗口最直接的原因。
那么如果多個(gè)窗口都是應(yīng)用程序窗口如何顯示呢? WMS會(huì)結(jié)合實(shí)際情況,去顯示窗口合適的層級(jí)。
Window窗口標(biāo)志(Flag)
Window Flag用于控制窗口的一些事件:如是否接收觸屏事件,窗口顯示時(shí)是否允許鎖屏,窗口可見時(shí)屏幕常亮,隱藏窗口等。 這里我們列出幾個(gè)常用的標(biāo)志:
- 1.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
窗口可見時(shí),允許在此窗口鎖屏,一般需要結(jié)合FLAG_KEEP_SCREEN_ON和FLAG_SHOW_WHEN_LOCKED使用
- 2.FLAG_DIM_BEHIND
該窗口后面的畫面會(huì)變模糊,一般會(huì)有一個(gè)模糊值dimAmount。
- 3.FLAG_NOT_FOCUSABLE
窗口不能獲取焦點(diǎn),設(shè)置這個(gè)Flag的同時(shí)FLAG_NOT_TOUCH_MODAL也會(huì)被設(shè)置
- 4.FLAG_NOT_TOUCHABLE
該窗口獲取不到輸入事件,不可點(diǎn)擊狀態(tài)。
- 5.FLAG_NOT_TOUCH_MODAL
在該窗口區(qū)域外的觸摸事件傳遞給其他的Window,而自己只會(huì)處理窗口區(qū)域內(nèi)的觸摸事件
- 6.FLAG_KEEP_SCREEN_ON
只要窗口可見,屏幕就會(huì)一直亮著
- 7.FLAG_LAYOUT_NO_LIMITS
允許窗口超過屏幕之外
- 8.FLAG_FULLSCREEN
隱藏所有的屏幕裝飾窗口,比如在游戲、播放器中的全屏顯示
- 9.FLAG_SHOW_WHEN_LOCKED
窗口可以在鎖屏的窗口之上顯示
- 10.FLAG_IGNORE_CHEEK_PRESSES
當(dāng)用戶的臉貼近屏幕時(shí)(比如打電話),不會(huì)去響應(yīng)此事件
- 11.FLAG_TURN_SCREEN_ON
窗口顯示時(shí)將屏幕點(diǎn)亮
- 12.FLAG_DISMISS_KEYGUARD
窗口顯示時(shí),鍵盤會(huì)關(guān)閉
窗口標(biāo)志Flag的2種設(shè)置方式:
- 1.通過Window的addFlags方法
Window mWindow =getWindow(); mWindow.addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- 2.通過Window的setFlags方法
mWindow.setFlags(LayoutParams.FLAG_KEEP_SCREEN_ON,LayoutParams.FLAG_KEEP_SCREEN_ON);
addFlags方法內(nèi)部也是調(diào)用setFlags
- 3.給LayoutParams設(shè)置Flag,并通過WindowManager的addView方法進(jìn)行添加
WindowManager.LayoutParams wl = new WindowManager.LayoutParams(); wl.flags = LayoutParams.FLAG_KEEP_SCREEN_ON; Log.d("TAG","window:"+window.getWindowManager()); WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); TextView mTextView=new TextView(this); mTextView.setText("ttttttttdddddd"); Log.d("TAG","mWindow:"+mWindowManager); mWindowManager.addView(mTextView,wl);
Window 軟鍵盤模式(SoftInputModel)
我們的軟鍵盤也是窗口的一種,屬于系統(tǒng)窗口,層級(jí)較高,所以在一些場(chǎng)景下會(huì)覆蓋層級(jí)較低的應(yīng)用窗口。
比如我們的登錄場(chǎng)景,如果鍵盤彈出窗口處理不好,會(huì)覆蓋輸入登錄按鈕等,這樣用戶體驗(yàn)會(huì)非常糟糕。
于是WindowManager給我們提供了關(guān)于軟鍵盤模式的Window窗口處理方式:
- 1.SOFT_INPUT_STATE_UNSPECIFIED
沒有指定狀態(tài),系統(tǒng)會(huì)選擇一個(gè)合適的狀態(tài)或依賴于主題的設(shè)置
- 2.SOFT_INPUT_STATE_UNCHANGED
不會(huì)改變軟鍵盤狀態(tài)
- 3.SOFT_INPUT_STATE_HIDDEN
當(dāng)用戶進(jìn)入該窗口時(shí),軟鍵盤默認(rèn)隱藏
- 4.SOFT_INPUT_STATE_ALWAYS_HIDDEN
當(dāng)窗口獲取焦點(diǎn)時(shí),軟鍵盤總是被隱藏
- 5.SOFT_INPUT_ADJUST_RESIZE
當(dāng)軟鍵盤彈出時(shí),窗口會(huì)調(diào)整大小
- 6.SOFT_INPUT_ADJUST_PAN
當(dāng)軟鍵盤彈出時(shí),窗口不需要調(diào)整大小,要確保輸入焦點(diǎn)是可見的
軟鍵盤模式設(shè)置方式:getWindow().setSoftInputMode
到這里,我們已經(jīng)將Window的屬性:窗口類型(Type),窗口層級(jí)(Z-Order),窗口標(biāo)志(Flag),軟鍵盤模式(softInputModel)做了一個(gè)系統(tǒng)的講解
接下來我們來分析我們的View是如何顯示到屏幕上:
View是如何一步一步添加到屏幕上的?更新?刪除呢?
通過前面我們對(duì)WindowManager體系分析知道,我們屏幕中所有的View首先需要經(jīng)過WindowManager的處理,最后提交給WMS來處理。
我們先來看WindowManager的父類ViewManager:
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只提供了三個(gè)接口方法:分別對(duì)應(yīng)添加,更新,刪除
- 1.addView:添加一個(gè)View到WMS中,第二個(gè)參數(shù)為當(dāng)前View的參數(shù)。
- 2.updateViewLayout:更新當(dāng)前View,第二個(gè)參數(shù)為需要更新的view參數(shù)
- 3.removeView:刪除當(dāng)前View
而這幾個(gè)方法具體實(shí)現(xiàn)是在WindowManagerImpl類中(前面分析過) 直接來看WindowManagerImpl的三個(gè)對(duì)應(yīng)方法:
public final class WindowManagerImpl implements WindowManager{ private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); } @Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.updateViewLayout(view, params); } @Override public void removeView(View view) { mGlobal.removeView(view, false); } ... }
可以看到這三個(gè)方法都是委托給了WindowManagerGlobal進(jìn)行處理,這是設(shè)計(jì)模式中的橋接模式。
進(jìn)入WindowManagerGlobal中: 先分析addView:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... ViewRootImpl root; //將params強(qiáng)轉(zhuǎn)為WindowManager.LayoutParams類型 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; synchronized (mLock) { //創(chuàng)建一個(gè)ViewRootImpl對(duì)象 root = new ViewRootImpl(view.getContext(), display); //給View設(shè)置LayoutParams參數(shù) view.setLayoutParams(wparams); //存儲(chǔ)view到mViews列表 mViews.add(view); //存儲(chǔ)root到mRoots列表 mRoots.add(root); //存儲(chǔ)wparams到mParams列表 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; } } }
- 1.首先創(chuàng)建一個(gè)ViewRootImpl對(duì)象root。
- 2.然后將當(dāng)前view,當(dāng)前params以及1中創(chuàng)建的ViewRootImpl對(duì)象root分別存儲(chǔ)到對(duì)應(yīng)的List列表中,注意這三者的index是一一對(duì)應(yīng)的。
- 3.調(diào)用root的setView方法
ViewRootImpl身負(fù)了很多職責(zé):
- 1.管理View樹,且其是View的根
- 2.觸發(fā)三大繪制流程:測(cè)量,布局,繪制
- 3.輸入事件中轉(zhuǎn)站
- 4.管理Surface
- 5.負(fù)責(zé)與WMS通訊
我們來看ViewRootImpl的setView方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { ... requestLayout();//注釋1 ... res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel);//注釋2 ... }
來看setView的注釋1:requestLayout,屏幕繪制部分
我們?cè)谇懊嬉黄恼隆?a href="http://www.dbjr.com.cn/article/263619.htm" rel="external nofollow" target="_blank">"一文讀懂"系列:Android屏幕刷新機(jī)制》已經(jīng)對(duì)原理進(jìn)行解析過了,這里我們?cè)俅蟾耪f下: requestLayout內(nèi)部主要使用垂直同步信號(hào)VSync的方式,在收到GPU提供的VSync信號(hào)后,會(huì)觸發(fā)View的三大繪制layout,mesure,draw流程... 需要知道完整機(jī)制的可以調(diào)到原文查看
這里我們重點(diǎn)來看setView的注釋2:mWindowSession.addToDisplay
顧名思義:addToDisplay就是將Window添加到屏幕上,如何添加的呢?
mWindowSession是IWindowSession類型的,它是一個(gè)binder對(duì)象,用于進(jìn)程間通訊,IWindowSession是C端代理,在S端使用的是Session類實(shí)現(xiàn)。addToDisplay運(yùn)行在WMS進(jìn)程中。
mWindowSession = WindowManagerGlobal.getWindowSession(); public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); IWindowManager windowManager = getWindowManagerService();//1 sWindowSession = windowManager.openSession(//2 new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowSession; } }
注釋1處:返回一個(gè)WMS對(duì)象的代理類。在注釋2處調(diào)用WMS的代理類的openSession方法
定位到WMS的openSession方法:
@Override public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext) { if (client == null) throw new IllegalArgumentException("null client"); if (inputContext == null) throw new IllegalArgumentException("null inputContext"); Session session = new Session(this, callback, client, inputContext); return session; }
可以看到返回的就是一個(gè)Session對(duì)象。
所以之前的mWindowSession.addToDisplay方法調(diào)用的就是Session類的addToDisplay方法
此時(shí)已經(jīng)進(jìn)入了WMS進(jìn)程:
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outStableInsets, outOutsets, outInputChannel); }
這里的mService = WMS服務(wù),最終又回調(diào)到WMS中去了:
調(diào)用WMS的addWindow方法添加Window,在WMS眼里,一切View都是以Window形式存在的, 剩下的工作就交由WMS進(jìn)行處理了:在WMS中會(huì)為這個(gè)Window分配Surface,并確定顯示層級(jí), 可見負(fù)責(zé)顯示界面的是畫布Surface,而不是窗口本身,WMS將他管理的Surface交由SurfaceFlinger處理,SurfaceFlinger將這些Surface合并后放入到buffer中,屏幕會(huì)定時(shí)從buffer中獲取顯示數(shù)據(jù),顯示到屏幕上。
同理我們?cè)賮矸治鱿耉iew刪除流程:
定位到WindowManagerGlobal的removeView
public void removeView(View view, boolean immediate) { ... synchronized (mLock) { int index = findViewLocked(view, true);//1 View curView = mRoots.get(index).getView();//2 removeViewLocked(index, immediate);//3 if (curView == view) { return; } throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView); } } private int findViewLocked(View view, boolean required) { final int index = mViews.indexOf(view); if (required && index < 0) { throw new IllegalArgumentException("View=" + view + " not attached to window manager"); } return index; }
在注釋1處獲取view在mViews中的索引,在注釋2處通過索引獲取當(dāng)前mRoots列表中view。在注釋3處調(diào)用removeViewLocked,這個(gè)是重點(diǎn)移出方法。
private void removeViewLocked(int index, boolean immediate) { ... boolean deferred = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); } } }
看root.die方法:
boolean die(boolean immediate) { ... //如果是同步移除,則立即調(diào)用doDie方法 if (immediate && !mIsInTraversal) { doDie(); return false; } ... //異步移除,發(fā)出一個(gè)msg進(jìn)行移除 mHandler.sendEmptyMessage(MSG_DIE); return true; }
不管是同步還是異步,最終都會(huì)調(diào)用doDie方法進(jìn)行移除。 進(jìn)行doDie方法:
void doDie() { ... synchronized (this) { if (mAdded) { dispatchDetachedFromWindow();//1 } .. } WindowManagerGlobal.getInstance().doRemoveView(this);//4 }
doDie注釋1: 這個(gè)方法里面做一些對(duì)資源銷毀的操作:
void dispatchDetachedFromWindow() { if (mView != null && mView.mAttachInfo != null) { //這里可以看出我們可以在View銷毀前,在View的dispatchDetachedFromWindow做一些資源釋放操作 mView.dispatchDetachedFromWindow(); } //銷毀硬件渲染線程 destroyHardwareRenderer(); //將mView的root置為mull mView.assignParent(null); mView = null; mAttachInfo.mRootView = null; //釋放當(dāng)前Surface mSurface.release(); //核心移除View的方法。 try { mWindowSession.remove(mWindow); } catch (RemoteException e) { } //移除同步屏障以及View的繪制task unscheduleTraversals(); }
mWindowSession.remove(mWindow)
在addView流程分析過:mWindowSession是WMS進(jìn)程中的Session類
Session.java
public void remove(IWindow window) { mService.removeWindow(this, window); }
在 Session 中直接調(diào)用了 WindowManagerService 的 removeWindow(Session session, IWindow client) 方法。
WindowManagerService
public void removeWindow(Session session, IWindow client) { synchronized(mWindowMap) { // 得到 windowstate 對(duì)象 WindowState win = windowForClientLocked(session, client, false); if (win == null) { return; } // 進(jìn)行移除 window 操作 removeWindowLocked(win); } }
先得到 WindowState 對(duì)象,再調(diào)用 removeWindowLocked 去移除該 WindowState 。而具體的 removeWindowLocked 代碼我們?cè)谶@就不深入了,可以自行研究。
至此,整個(gè) Window 移除機(jī)制也分析完畢了。
總結(jié)
本篇文章基于Window模型以及源碼對(duì)Window在整個(gè)Android FrameWork體系中所涉及的知識(shí)點(diǎn)以及作用做了一個(gè)整體的歸納,篇幅問題,關(guān)于WMS中如何對(duì)Window的操作放在下篇進(jìn)行講解。
以上就是Android Framework Window體系教程的詳細(xì)內(nèi)容,更多關(guān)于Android Framework Window的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android使用flutter的ListView實(shí)現(xiàn)滾動(dòng)列表的示例代碼
現(xiàn)如今打開一個(gè) App,比如頭條、微博,都會(huì)有長列表,那么android使用flutter的ListView滾動(dòng)列表如何實(shí)現(xiàn),本文就來詳細(xì)的介紹一下,感興趣的同學(xué)可以來了解一下2018-12-12Android編程實(shí)現(xiàn)設(shè)置TabHost當(dāng)中字體的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)設(shè)置TabHost當(dāng)中字體的方法,涉及Android針對(duì)TabHost屬性操作的相關(guān)技巧,非常簡單實(shí)用,需要的朋友可以參考下2015-12-12Android開發(fā)Intent跳轉(zhuǎn)傳遞list集合實(shí)現(xiàn)示例
這篇文章主要為大家介紹了Android開發(fā)Intent跳轉(zhuǎn)傳遞list集合實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07Android 5.0 實(shí)現(xiàn)水波擴(kuò)散效果
這篇文章主要為大家詳細(xì)介紹了Android 5.0 實(shí)現(xiàn)水波擴(kuò)散效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01Android 如何實(shí)現(xiàn)彈窗順序&優(yōu)先級(jí)控制
這篇文章主要介紹了Android 如何實(shí)現(xiàn)彈窗順序&優(yōu)先級(jí)控制,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03Android垃圾回收機(jī)制解決內(nèi)存泄露問題
這篇文章主要介紹了Android垃圾回收機(jī)制解決內(nèi)存泄露問題的相關(guān)資料,需要的朋友可以參考下2016-02-02Android定時(shí)器實(shí)現(xiàn)的幾種方式整理及removeCallbacks失效問題解決
本文為大家詳細(xì)介紹下Android 定時(shí)器實(shí)現(xiàn)的幾種方式:Handler + Runnable、Timer的方式、Handle與線程的sleep(long )方法和removeCallbacks失效問題如何解決2013-06-06Android實(shí)現(xiàn)簡單的下拉阻尼效應(yīng)示例代碼
下面小編就為大家分享一篇Android實(shí)現(xiàn)簡單的下拉阻尼效應(yīng)示例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01