什么是Activity 、View 、 Window?
Activity:是Android 四大組件之一, 是存放View對象的容器,也是我們界面的載體,可以用來展示一個界面。它有一個SetContentView()方法 ,可以將我們定義的布局設置到界面上。
View:就是一個個視圖的對象,實現(xiàn)了KeyEvent.Callback和Drawable.Callback。
Window:是一個抽象類,是一個頂層的窗口,它的唯一實例是PhoneWindow它提供標準的用戶界面策略,如背景、標題、區(qū)域,默認按鍵處理等。
分析下三者之間的關系吧
iew包含很多,TextView 、Imageview 、Listview 、 Button..就是一個一個展示不同圖形的對象。我們可以把view通過xml布局,或者通過new View(),然后通過addview方法或動態(tài)或靜態(tài)添加到Activity的布局上。我們都知道我們定義了layout布局,通過SetContentView就可以設置到Activity上,而Activity中的SetContentView()方法,又調(diào)用了Window的SetContentView方法,也就是View通過Activity最終添加到了Window上面。
那我們今天就看一下這個方法到底如何把layout布局加載進去,到底加載到哪里去了?
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
面注釋寫的很清楚,通過一個layout資源給Activity設置內(nèi)容,資源將被添加到Activity最頂層的View上也就是調(diào)用了方法體中的getWindow().set方法,首先這里getWindow() 拿到的是一個Window的子類,PhoneWindow的實例,那么這個Window對象是在哪里 賦值的呢,我們在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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
在第11行初始化mWindow對象,這個對象是window 接口的實現(xiàn)類 PhoneWindow 的實例。
那我們看一下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) {
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);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
這里我們只看下第6和第11行,首先判斷mContentParent是不是 null,我們先搞明白mContentParent是什么東西?
// 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.
private ViewGroup mContentParent;
通過查看源代碼 我們這里知道m(xù)ContentParent是一個ViewGroup對象 ,上面的注釋我們可以明白這個Window中的內(nèi)容就放置在mContentParent上面, 這個mContentParent或者是一個DecorView對象或者是一個DecorView的子類。
OK,搞明白了mContentParent是一個ViewGroup對象 ,那我們繼續(xù)往下看
如果是installDecor()不用想我們也知道這個方法肯定是初始化了mContentParent,一起看下是不是我們想的那樣吧。
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);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
//.....
//...... 省略若干代碼
}
}
這里先判斷 mDecor是不是null,如果是,初始化mDecor,然后判斷mContentParent是不是null,如果是,通過mDecor去初始化 mContentParent對象。 對吧,跟我們想的一樣是去初始化。
OK ,這里創(chuàng)建出了mContentParent對象,我們接著看PhoneWindow的SetContentView方法的第11行,這里先進行了判斷,具體判斷 我們先不關心,我們繼續(xù)往下執(zhí)行在看第12行或者17行,我們就清楚了我們在Activity中設置的layoutid 在這里加載到了mContentParent 上面。也就是所有的所有的View 對象都是加載到了mContentParent對象上面,而我們前面知道m(xù)ContentParent是根據(jù)DecorView而來的,這樣我們就清楚了Activity與Window以及View的關系,這里用圖2 表示一下他們的關系。

對著這張圖,打個比喻來幫助理解。
Activity就像是一扇貼著窗花的窗口,Window就想上窗口上面的玻璃,而View對象就像一個個貼在玻璃上的窗花。
最后的Activity與Window View的關聯(lián)在畫一個圖3:

總結(jié)起來
說就是 Activity會調(diào)用PhoneWindow的setContentView()將layout布局添加到DecorView上,而此時的DecorView就是那個最底層的View。然后通過LayoutInflater.infalte()方法加載布局生成View對象并通過addView()方法添加到Window上,(一層一層的疊加到Window上)所以,Activity其實不是顯示視圖,Window才是真正的顯示視圖。
注:一個Activity構(gòu)造的時候只能初始化一個Window(PhoneWindow),另外這個PhoneWindow有一個View容器 mContentParent,這個
View容器是一個ViewGroup,是最初始的跟視圖,然后通過addView方法將View一個個層疊到mContentParent上,這些層疊的View最終放在Window這個載體上面。
一、簡述如何將Activity展現(xiàn)在手機上
Tips:
Activity本身是沒辦法處理顯示什么控件(view)的,是通過PhoneWindow進行顯示的
換句話說:activity就是在造PhoneWindow,顯示的那些view都交給了PhoneWindow處理顯示
1、在Activity創(chuàng)建時調(diào)用attach方法:
2、attach方法中會調(diào)用PolicyManager.makeNewWindow()
實際工作的是IPolicy接口的makeNewWindow方法
①、其中創(chuàng)建了一個window(可以比喻為一個房子上造了一個窗戶):mWindow = PolicyManager.makeNewWindow(this);
②、在window這個類中,才調(diào)用了setContentView(),這是最終的調(diào)用
在Activity的setContentView方法中,實際上是調(diào)用:getWindow().setContentView(view, params);
這里的getWindow()就是獲取到一個Window對象
Tips:
啥attch優(yōu)先于onCreate調(diào)用,就是由于在attch方法中,會創(chuàng)建window,有了window才能調(diào)用
setContentView
3、在IPolicy的實現(xiàn)類中創(chuàng)建了PhoneWindow:
①、由mWindow = PolicyManager.makeNewWindow(this);,
②、這里的makeNewWindow(this);方法中,返回的是:return sPolicy.makeNewWindow(context);
③、這個sPolicy實際是一個接口,其實現(xiàn)類是Policy,其中只是創(chuàng)建了一個PhoneWindow
4、在PhoneWindow的setContentView中向ViewGroup(root)中添加了需要顯示的內(nèi)容
①、PhoneWindow是繼承Window的
②、setContentView這個方法中,需要先判斷一個mContentParent是否為空,因為在默認進來的時候,什么都沒創(chuàng)建呢
此時需要創(chuàng)建:installDecor(),DecorView是最根上的顯示的
可以通過adt中的的tools中有個hierarchyviewer.bat的工具,可以查看手機的結(jié)構(gòu)
③、DecorView:是繼承與FrameLayout的,作為parent存在,最初顯示的
④、下次再加載的時候,mContentParent就不為空了,會將其中的所有的view移除掉,然后在通過布局填充器加載布局
二、三者關系:
1、在Activity中調(diào)用attach,創(chuàng)建了一個Window
2、創(chuàng)建的window是其子類PhoneWindow,在attach中創(chuàng)建PhoneWindow
3、在Activity中調(diào)用setContentView(R.layout.xxx)
4、其中實際上是調(diào)用的getWindow().setContentView()
5、調(diào)用PhoneWindow中的setContentView方法
6、創(chuàng)建ParentView:作為ViewGroup的子類,實際是創(chuàng)建的DecorView(作為FramLayout的子類)
7、將指定的R.layout.xxx進行填充
通過布局填充器進行填充【其中的parent指的就是DecorView】
8、調(diào)用到ViewGroup
9、調(diào)用ViewGroup的removeAllView(),先將所有的view移除掉
10、添加新的view:addView()
Tips:
①、Activity就是在造“窗戶”,即創(chuàng)建PhoneWindow
②、PhoneWindow才是進行顯示view的操作,主要就是setContentView()
