轉(zhuǎn)載請注明 Android_YangKe,謝謝!
老實說自己做 Android 有一段時間了,但發(fā)現(xiàn) Android 技能提升上有了點小瓶頸,總感覺自己什么都會,又都感覺自己什么都不會,于是就有了此片文章。
下面我們將通過 Window 慢慢引出三者之間的關(guān)系,同時適當(dāng)?shù)脑创a輔助分析。說到源碼相信很多人都是心中都一萬個 mmb,勞資這么差的英文,動不動成千上萬行的代碼,腦袋瞬間短路好不好... 微笑-微笑 。
Activity,View 是什么我相信不用解釋,而 Window 在使用頻率上相對來說就低了些,那么 Window 是什么?在 Android 體系中扮演著什么樣的角色?下面是 Google 工程師對 Window 類的一些介紹,我們來看下。
//Window.java
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
大致意思:Window 是一個基礎(chǔ)類,是頂級視圖的承載。提供了標(biāo)準(zhǔn)的 UI 策略,如背景,標(biāo)題。同時 Window 只有一個實現(xiàn)類PhoneWindow。官方的解釋還是比較給力,很詳細(xì)的介紹了該對象,現(xiàn)在我們對 Window 有了一個初步認(rèn)識。下面我們將結(jié)合具體案例進(jìn)行探索。
Activity 中 onCreate 函數(shù)相信大家再熟悉不過了,如我們將里面的setContentView函數(shù)注釋掉時,會發(fā)現(xiàn)原來我們的炫酷的頁面只留下一個標(biāo)題加空白頁面(前提我們沒有修改默認(rèn)的主題),這么看來此行代碼很關(guān)鍵。
//Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
一般來說我們的 Activity 都是繼承自 AppCompatActivity,但最終的頂層父類是 Activity。通過上面源碼我們可以發(fā)現(xiàn) Activity 只是 View 宿主,并沒有真正的實現(xiàn)setContentView而是通過 getWindow 操作此函數(shù),我們來看下 getWindow。
//Activity.java
public Window getWindow() {
return mWindow;
}
final void attach(...) {
attachBaseContext(context);
mWindow = new PhoneWindow(this, window,activityConfigCallback);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
}
很簡單 getWindow 函數(shù)返回了一個實例PhoneWindow。由于代碼中沒有對 getWindow 判空,我們可以推測出 attach 函數(shù)一定在 setContentView 之前執(zhí)行,也就是 onCreate 函數(shù)之前,不然一定會報NullPointerException。
扯得有點多,言歸正傳。這里我們看到了 Window 的身影,從上文我們了解到 Window 是頂級視圖的承載,而PhoneWindow又是 Window 的唯一子類,我們嘗試著去 PhoneWindow中探索 setContentView。
//PhoneWindow.java
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
// 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.
ViewGroup mContentParent;
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
...
}
一大串代碼(當(dāng)然我省略了一部分代碼)果不其然,真正的操作的就是在 Window 的 setContentView函數(shù)中完成的,首先。
- mDecor:Window 上最頂層的視圖,如果按照從上到下的順序,分別是狀態(tài)欄,標(biāo)題欄,具體 View 控件。由于其繼承了 FrameLayout 也就是說 mDecor 也是一個 ViewGroup。
- mContentParent:表示視圖區(qū)域,里面可以是 mDecor,也可以是 mDecor 中的具體 View,但不代表具體視圖。
了解了這兩個對象的作用,我們繼續(xù)看代碼。removeAllViews函數(shù)顧名思義,也就是從當(dāng)前 Window 中移除所有 View,這里就不進(jìn)入源碼進(jìn)行分析了(代碼水很深,隨時保留精氣神)?,F(xiàn)在就剩下installDecor了,此函數(shù)貌似很關(guān)鍵,我們跟進(jìn)來看下。
//PhoneWindow.java
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
首先映入眼簾的是generateDecor,同時此函數(shù)的返回值賦予了 mDecor。隨后系統(tǒng)調(diào)用了generateLayout。generateLayout從字面意義上來看是初始化布局,莫非跟布局 View 有關(guān),我們來看一下。
//PhoneWindow.java
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 (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
}
...
}
這里我們拋去 if 的各種判斷直接看這幾個常量:FLAG_FULLSCREEN、 FEATURE_NO_TITLE、 FLAG_FULLSCREEN等等,是不是似曾相識呢?相信大家在 onCreate 函數(shù)setContentView前都有過重設(shè)當(dāng)前 Activity 樣式的經(jīng)歷,嚴(yán)格來說應(yīng)該是 Window (Activity 全屏、去除標(biāo)題欄)。也就是說屏幕上頁面的尺寸,樣式都是通過 Window 來控制的!看來此函數(shù)很重要,打起精神,我們再來分析一波。
//PhoneWindow.java
protected ViewGroup generateLayout(DecorView decor) {
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
WindowManager.LayoutParams params = getAttributes();
if (!hasSoftInputMode()) {
params.softInputMode = a.getInt(R.styleable.Window_windowSoftInputMode, params.softInputMode);
}
if (params.windowAnimations == 0) {
params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
}
...
if (getContainer() == null) {
final Drawable background;
if (mBackgroundResource != 0) {
background = getContext().getDrawable(mBackgroundResource);
} else {
background = mBackgroundDrawable;
}
mDecor.setWindowBackground(background);
final Drawable frame;
if (mFrameResource != 0) {
frame = getContext().getDrawable(mFrameResource);
} else {
frame = null;
}
mDecor.setWindowFrame(frame);
...
if (mTitle != null) {setTitle(mTitle);}
if (mTitleColor == 0) {mTitleColor = mTextColor;}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
首先我們看下官方給的注釋:
- Remaining setup -- of background and title -- that only applies to top-level windows.
將自己置身于情境之中,大概意思:設(shè)置標(biāo)題,背景顏色,將視圖應(yīng)用在頂層 View 上,誰呢?DecorView。
其次:
獲取 Window 的一些屬性,并根據(jù)情況對輸入法模式、載入動畫等等進(jìn)行配置。
最后:
對 Window 承載的視圖進(jìn)行背景設(shè)置,邊距設(shè)置,標(biāo)題欄顏色設(shè)置,標(biāo)題欄設(shè)置等等。
到這里相信你對 Window 的作用,以及其在 Android 中扮演著怎樣的角色有了一定的認(rèn)知。下面我們來看一張圖片,輔助我們理解 Activity、Window、View 之間的關(guān)系:(圖片源自互聯(lián)網(wǎng),出處不明)

最外層是 Activity、其次是 PhoneWindow、最后是我們常操作的 View 控件。通過這張圖相信你對 Activity、Window、View 有了進(jìn)一步的認(rèn)識,下面我們再來重新認(rèn)識下三者。
View:說到 View 相信大家都很熟悉,像什么 TextView、Button、ImageView 等等我相信你可以一口氣說一大堆。View 是具體視圖的最小展示單元。在頁面上可以是一張圖片,一段文本,一個網(wǎng)頁。簡而言之:View 就是具體視圖(常常與用戶打交道)。
Window:我們真正意義上所說的頁面,是 Activity 任命的大使,主要用于管理 View,設(shè)置窗口(視圖)樣式、尺寸、輸入法模式等。我們也可以通過WindowManager對 View 進(jìn)行添加和移除操作等。簡而言之:Window 主要用于控制顯示窗口的尺寸、樣式、View 的移除添加(常常與研發(fā)人員打交道)。
Activity:頁面的載體。維護(hù)應(yīng)用程序的生命周期、提供用戶處理事件的 API、進(jìn)程間通信等。簡而言之:用于管理系統(tǒng)組件相關(guān)事物。
整理:
Activity-> PhoneWindow-> DecorView。
總結(jié):
Activity 作為四大組件主要與系統(tǒng)進(jìn)行交互,用于組件間通信,生命周期管理、進(jìn)程通信等。Window 作為視圖載體,主要用于管理視圖的尺寸、樣式、輸入法模式、View 的移除添加等,需要依托于 Activiity。View 就比較簡單了,不同的 View 用于展示不同的視圖,例:文本組件,圖片組件,甚至網(wǎng)頁等,需要依托于 Window。也就是說三者相輔相成,誰離開都將無存在意義。
尾聲:
為什么 View 不直接與 Activity 關(guān)聯(lián)呢?反而又設(shè)計出一個 Window 對象?
其實這里有些面向?qū)ο蟮母拍睿簿褪钦f對于龐大的功能我們需要進(jìn)行拆分,讓其盡量獨立,各司其職,同時在功能互不影響的情況下,相輔相成。
完~
喜歡有幫助的話: 雙擊、評論、轉(zhuǎn)發(fā),動一動你的小手讓更多的人知道!