1.前言
我覺(jué)得要弄清楚事件分發(fā)前,還是要先大致了解一下幾個(gè)概念碗旅。
Window,WindowManager,WindowMangerService,ViewRoot,DecorView他們之間是如何協(xié)同工作的镜悉。
2.Activity和Window的關(guān)系
我們知道Activity是支持顯示UI的祟辟,那么它是否直接管理view樹(shù)或者ViewRoot呢?答案時(shí)否定的侣肄,Activity并沒(méi)有與這兩者產(chǎn)生直接的聯(lián)系旧困,因?yàn)檫@中間還有一個(gè)被稱為“Window”的對(duì)象。大家可以在Activity的源碼中找到如下代碼:
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {
...
private Window window;
Window的字面意思是"窗口"稼锅,這很好地解釋了它存在的意義吼具。Window是基類(lèi),根據(jù)不同的產(chǎn)品可以衍生出不同的子類(lèi)——具體則是由系統(tǒng)在Activity.attach中調(diào)用PolicyManager.makeNewWindow決定的矩距,目前版本的Android系統(tǒng)默認(rèn)生成的都是PhoneWindow拗盒。
3.Window和WindowManagerImpl的關(guān)系
在Android源碼中以“Window”開(kāi)頭的類(lèi)有不少,如Window,WindowManager,WindowManagerImpl等锥债,為什么需要這么多相似的類(lèi)呢陡蝇?
先來(lái)看Window,它是面向Activity的赞弥,表示"UI界面的外框"毅整;而“框里面”具體的東西包括布局和內(nèi)容等,是由具體的Window子類(lèi)绽左,如PhoneWindow來(lái)規(guī)劃的悼嫉。
Window的另一層含義是要與WindowManagerService進(jìn)行通信,但它并沒(méi)有直接在自身實(shí)現(xiàn)這一功能拼窥。原因是:一個(gè)應(yīng)用程序中很可能存在多個(gè)Window戏蔑。如果它們都單獨(dú)與WMS通信,那么既浪費(fèi)資源鲁纠,又會(huì)造成管理的混亂总棵。換句話說(shuō),它們需要統(tǒng)一的管理改含。于是就有了WindowManager情龄,它作為Window的成員變量mWindowManager存在。這個(gè)WindowManager是一個(gè)接口類(lèi),其真正的實(shí)現(xiàn)是WindowManagerImpl骤视,后者同時(shí)也是整個(gè)應(yīng)用程序中所有Window的管理者鞍爱。因而WindowManager與WindowManagerImpl的關(guān)系有點(diǎn)類(lèi)似于“地方與中央”:地方為實(shí)施中央的“政策”提供了一個(gè)接口,然后匯總到中央進(jìn)行管理专酗。
在Window的源碼中與mWindowMager有關(guān)的代碼有如下幾句:
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);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
public WindowManager getWindowManager() {
return mWindowManager;
}
然后我們?nèi)indowManagerImpl的代碼中去查看createLocalWindowManager方法的代碼睹逃,代碼如下:
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mDisplay, parentWindow);
}
從這幾處代碼,大家可以看到Window類(lèi)中的mWindowManger引用的其實(shí)是WindowManagerImpl的實(shí)例祷肯。
4.ViewRoot和WindowManagerImpl的關(guān)系
由于每個(gè)Activity都有自己的窗口“Window”沉填,每個(gè)Window都有一個(gè)WindowManagerImpl實(shí)例的引用。即每個(gè)Activity都對(duì)應(yīng)一個(gè)WindowManagerImpl佑笋。
在早期的版本中在WindowMangerImpl內(nèi)部翼闹,存在3個(gè)全局變量:
public final class WindowManagerImpl implements WindowManager {
private [View] [] mViews;//樹(shù)的根節(jié)點(diǎn)
private [ViewRoot][] mRoots;//ViewRoot
private [WindowManager][] mParams;//Window的屬性
由此也可以看出,一個(gè)進(jìn)程中不僅有一個(gè)ViewRoot允青;而Activity與ViewRoot則是一對(duì)一的關(guān)系橄碾。
自Android4.3開(kāi)始對(duì)此做了修改卵沉,WindowManagerImpl不再直接存儲(chǔ)上述三個(gè)數(shù)組變量颠锉,而是由一個(gè)稱為“WindowMangerGlobal”的類(lèi)統(tǒng)一管理。
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Display mDisplay;
private final Window mParentWindow;
public final class WindowManagerGlobal {
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>();
5.DecorView與ViewRootImpl
從setContentView說(shuō)起
一般地史汗,我們?cè)贏ctivity中琼掠,會(huì)在onCreate()方法中寫(xiě)下這樣一句:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
顯然,這是為activity設(shè)置一個(gè)我們定義好的main.xml布局停撞,我們跟蹤一下源碼瓷蛙,看看這個(gè)方法是怎樣做的,Activity#setContentView:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
以上代碼我們可以看出戈毒,Activity的setContentView,其實(shí)是調(diào)用的window的setContentView方法艰猬。上面我們了解到Window是基類(lèi),我們要找到它的實(shí)現(xiàn)類(lèi)埋市。
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) {
...
//創(chuàng)建Window對(duì)象
mWindow = PolicyManager.makeNewWindow(this);
...
}
我們只看關(guān)鍵部分冠桃,在Activity里面有一個(gè)attach方法,在這個(gè)方法中創(chuàng)建了Window對(duì)象道宅,下面看PolicyManager里面的makeNewWindow方法食听。
public final class PolicyManager {
......
private static final IPolicy sPolicy;
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
}
在這個(gè)代碼里面調(diào)用的是IPolicy里面的makeNewWindow方法,一看IPolicy就知道它是一個(gè)接口污茵,真正的實(shí)現(xiàn)是在它的實(shí)現(xiàn)類(lèi)Policy中完成的樱报,直接看代碼。
public class Policy implements IPolicy {
......
public Window makeNewWindow(Context context) {
//看到?jīng)]有泞当,是一個(gè)PhoneWindow對(duì)象
return new PhoneWindow(context);
}
......
}
這就看到了創(chuàng)建的是一個(gè)PhoneWindow對(duì)象迹蛤,也就是說(shuō)Activity里面的Window屬性引用的就是PhoneWindow對(duì)象,調(diào)用Window的方法實(shí)質(zhì)調(diào)用的就是PhoneWindow的方法,都在這里實(shí)現(xiàn)了盗飒。
總結(jié):
1.在啟動(dòng)Activity的過(guò)程中會(huì)調(diào)用一個(gè)attach函數(shù)
2.在attach函數(shù)中會(huì)執(zhí)行PolicyManager.makeNewWindow(this)
3.進(jìn)一步深入穷缤,真正執(zhí)行makeNewWindow的其實(shí)是IPolicy,它只是被 PolicyManager進(jìn)行了包裝箩兽。
4.IPolicy是一個(gè)接口津肛,所以makeNewWindow的執(zhí)行是由它的實(shí)現(xiàn)類(lèi)Policy來(lái)執(zhí)行的。
5.可以看到執(zhí)行創(chuàng)建了一個(gè)PhoneWindow對(duì)象并且返回汗贫,最終也是將這個(gè)對(duì)象賦值給mWindow身坐。
至此我們知道PhoneWindow是Window的實(shí)現(xiàn)類(lèi),那么我們?cè)赑honeWindow類(lèi)里面找到它的setContentView方法落包,看看它又實(shí)現(xiàn)了什么部蛇,PhoneWindow#setContentView:
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {//1
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);//2
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
首先判斷了mContentParent是否為null,如果為空則執(zhí)行installDecor()方法咐蝇,那么這個(gè)mContentParent又是什么呢涯鲁?我們看一下它的注釋:
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
它是一個(gè)ViewGroup類(lèi)型,結(jié)合2號(hào)代碼處有序,可以得知抹腿,這個(gè)mContentParent是我們?cè)O(shè)置的布局(即main.xml)的父布局。注釋還提到了旭寿,這個(gè)mContentParent是mDecor本身或者是mDecor的一個(gè)子元素
這里先梳理一下以上的內(nèi)容:Activity通過(guò)PhoneWindow的setContentView方法來(lái)設(shè)置布局警绩,而設(shè)置布局之前,會(huì)先判斷是否存在mContentParent盅称,而我們?cè)O(shè)置的布局文件則是mContentParent的子元素肩祥。
DecorView的創(chuàng)建
我們接著看下 installDecor();的源碼 PhoneWindow#installDecor:
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
首先,如果mDecor為空的時(shí)候會(huì)執(zhí)行g(shù)enerateDecor()代碼缩膝,調(diào)用PhoneWindow#generateDecor方法:
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
可以看出混狠,這里實(shí)例化了DecorView,在源碼中我們看到DecorView是PhoneWindow的內(nèi)部類(lèi),
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
它繼承的是FrameLayout疾层,由此可知它也是一個(gè)ViewGroup将饺。
那么,DecroView到底充當(dāng)了什么樣的角色呢云芦?
其實(shí)俯逾,DecorView是整個(gè)ViewTree的最頂層View,它是一個(gè)FrameLayout布局舅逸,代表了整個(gè)應(yīng)用的界面桌肴。在該布局下面,有標(biāo)題view和內(nèi)容view這兩個(gè)子元素琉历,而內(nèi)容view則是上面提到的mContentParent坠七。我們接著看generateLayout(mDecor)代碼水醋,PhoneWindow#generateLayout方法
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 從主題文件中獲取樣式信息
TypedArray a = getWindowStyle();
...
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
...
if (...) {
...
}
....
// Inflate the window decor.
// 加載窗口布局
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if (...) {
....
}
mDecor.startChanging();
//加載layoutResource
View in = mLayoutInflater.inflate(layoutResource, null);
//往DecorView中添加子View,即mContentParent
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
// 這里獲取的就是mContentParent
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks();
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
...
mDecor.finishChanging();
return contentParent;
}
由以上代碼可以看出彪置,該方法還是做了相當(dāng)多的工作的拄踪,首先根據(jù)設(shè)置的主題樣式來(lái)設(shè)置DecorView的風(fēng)格,比如說(shuō)有沒(méi)有titlebar之類(lèi)的拳魁,接著為DecorView添加子View惶桐,而這里的子View則是上面提到的mContentParent,如果上面設(shè)置了FEATURE_NO_ACTIONBAR潘懊,那么DecorView就只有mContentParent一個(gè)子View姚糊,
小結(jié):DecorView是頂級(jí)View,內(nèi)部有titlebar和contentParent兩個(gè)子元素授舟,contentParent的id是content救恨,而我們?cè)O(shè)置的main.xml布局則是contentParent里面的一個(gè)子元素。
在DecorView創(chuàng)建完畢后释树,讓我們回到PhoneWindow#setContentView方法肠槽,直接看代碼: mLayoutInflater.inflate(layoutResID, mContentParent);這里加載了我們?cè)O(shè)置的main.xml布局文件,并且設(shè)置mContentParent為main.xml的父布局奢啥,至于它怎么加載的秸仙,這里就不展開(kāi)來(lái)說(shuō)了。
到目前為止扫尺,通過(guò)setContentView方法筋栋,創(chuàng)建了DecorView和加載了我們提供的布局,但是這時(shí)正驻,我們的View還是不可見(jiàn)的,因?yàn)槲覀儍H僅是加載了布局抢腐,并沒(méi)有對(duì)View進(jìn)行任何的測(cè)量姑曙、布局、繪制工作迈倍。在View進(jìn)行測(cè)量流程之前伤靠,還要進(jìn)行一個(gè)步驟,那就是把DecorView添加至window中啼染,然后經(jīng)過(guò)一系列過(guò)程觸發(fā)ViewRootImpl#performTraversals方法宴合,在該方法內(nèi)部會(huì)正式開(kāi)始測(cè)量、布局迹鹅、繪制這三大流程卦洽。至于該一系列過(guò)程是怎樣的,因?yàn)樯婕暗搅撕芏鄼C(jī)制斜棚,這里簡(jiǎn)單說(shuō)明一下:
將DecorView添加至Window
每一個(gè)Activity組件都有一個(gè)關(guān)聯(lián)的Window對(duì)象阀蒂,用來(lái)描述一個(gè)應(yīng)用程序窗口该窗。每一個(gè)應(yīng)用程序窗口內(nèi)部又包含有一個(gè)View對(duì)象,用來(lái)描述應(yīng)用程序窗口的視圖蚤霞。上文分析了創(chuàng)建DecorView的過(guò)程酗失,現(xiàn)在則要把DecorView添加到Window對(duì)象中。而要了解這個(gè)過(guò)程昧绣,我們首先要簡(jiǎn)單先了解一下Activity的創(chuàng)建過(guò)程:
首先规肴,在ActivityThread#handleLaunchActivity中啟動(dòng)Activity,在這里面會(huì)調(diào)用到Activity#onCreate方法夜畴,從而完成上面所述的DecorView創(chuàng)建動(dòng)作奏纪,當(dāng)onCreate()方法執(zhí)行完畢,在handleLaunchActivity方法會(huì)繼續(xù)調(diào)用到ActivityThread#handleResumeActivity方法斩启,我們看看這個(gè)方法的源碼:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
...
// TODO Push resumeArgs into the activity for consideration
// 這里會(huì)調(diào)用到Activity的onResume()方法
ActivityClientRecord r = performResumeActivity(token, clearHide);
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);// 調(diào)用addView方法
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
...
}
}
在該方法內(nèi)部序调,獲取該activity所關(guān)聯(lián)的window對(duì)象,DecorView對(duì)象兔簇,以及windowManager對(duì)象发绢,而WindowManager是抽象類(lèi),它的實(shí)現(xiàn)類(lèi)是WindowManagerImpl垄琐,所以后面調(diào)用的是WindowManagerImpl#addView方法边酒,我們看看源碼:
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, mDisplay, mParentWindow);
}
接著調(diào)用了mGlobal的成員函數(shù),而mGlobal則是WindowManagerGlobal的一個(gè)實(shí)例狸窘,那么我們接著看WindowManagerGlobal#addView方法:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
...
root = new ViewRootImpl(view.getContext(), display);//1
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);//2
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
先看1號(hào)代碼處墩朦,實(shí)例化了ViewRootImpl類(lèi),接著翻擒,在2號(hào)代碼處氓涣,調(diào)用ViewRootImpl#setView方法,并把DecorView作為參數(shù)傳遞進(jìn)去陋气,在這個(gè)方法內(nèi)部劳吠,會(huì)通過(guò)跨進(jìn)程的方式向WMS(WindowManagerService)發(fā)起一個(gè)調(diào)用,從而將DecorView最終添加到Window上巩趁,在這個(gè)過(guò)程中痒玩,ViewRootImpl、DecorView和WMS會(huì)彼此關(guān)聯(lián)议慰,最終通過(guò)WMS調(diào)用ViewRootImpl#performTraverals方法開(kāi)始View的測(cè)量蠢古、布局、繪制流程
總結(jié):
Window别凹,它是"UI界面的外框",而里面具體內(nèi)容是有其子類(lèi)草讶,如PhoneWindow來(lái)規(guī)劃的。
WindowManager番川,是統(tǒng)一管理Window而誕生的到涂。其實(shí)現(xiàn)是WindowManagerImpl
WindowManagerImpl脊框,直接或間接的存儲(chǔ)DecorView,ViewRoot践啄,WindowManager;
DecorView,是整個(gè)ViewTree的最頂層View浇雹,它是一個(gè)FrameLayout布局,代表了整個(gè)應(yīng)用的界面,我們setContentView添加的視圖是被mContentParent"包裹"著添加到DecorView 中的屿讽。
ViewRoot是GUI管理系統(tǒng)與GUI呈現(xiàn)系統(tǒng)之間的橋梁昭灵,根據(jù)ViewRoot的定義,我們發(fā)現(xiàn)它并不是一個(gè)View類(lèi)型伐谈,而是一個(gè)Handler烂完。
它的主要作用如下:
- 向DecorView分發(fā)收到的用戶發(fā)起的event事件,如按鍵诵棵,觸屏抠蚣,軌跡球等事件;
-
與WindowManagerService交互履澳,完成整個(gè)Activity的GUI的繪制嘶窄。