本文是Android視圖層源碼分析系列第一篇文章。主要來理清Window
的地位以及做用。java
Android中全部的視圖(
View
)都是經過Window
來呈現的,無論是Activity
、Dialog
仍是Toast
,它們的視圖實際上都是附加在Window
上的,所以Window
實際是View
的直接管理者。本文就從源碼來分析一下Window
,理清Window
是如何組織視圖(View
)以及Activity
的PhoneWindow
的工做原理。本文不會去討論Window
的詳細使用。android
分析以前,咱們先找一個切入點,如下面這段代碼爲例:git
WindowTestActivity.javagithub
// example 1
val simpleTv = getSimpleTextView()
windowManager.addView(simpleTv, getSimpleWindowLayoutParams())
//example 2
window.addContentView(getSimpleTextView(), ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
複製代碼
即咱們直接經過windowManager.addView
和window.addContentView()
來添加了一個View
。這兩個方法都是Activity
直接提供的方法,也是咱們惟一與Window
交互的幾個方法之一,那:bash
View
究竟添加到了哪裏呢?window
和windowManager
有什麼關係呢?下面咱們將從源碼一點一點弄清這些問題。先來看一下windowManager.addView(contentView, layoutParams)
,爲了下面方便敘述,咱們把被add
的view
叫作contentView
。微信
WindowManager
是一個接口,在看windowManager.addView()
以前咱們先來看一下Activity的WindowManager
的實例是誰。app
追蹤Activity
的源碼發現WindowManager
實際上是經過Window
來獲取的(它實際上是Window
的成員變量)源碼分析
mWindowManager = mWindow.getWindowManager();
複製代碼
那Window
的WindowManager
是在什麼地方賦值的呢?實際上是在Activity attch
時:佈局
//Activity.java
final void attach(...){
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken,..);
}
複製代碼
mWindow.setWindowManager()
內部實際上是構造了一個WindowManagerImpl
:ui
public void setWindowManager(WindowManager wm, IBinder appToken...) {
...
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
複製代碼
即 Activity
的PhoneWindow
的WindowManager
實例是WindowManagerImpl
而且在Activity.attach
方法中也能夠看出Activity
的Window
的實例時PhoneWindow
(PhoneWindow
實際上是Window
的惟一實現類,是針對於app客戶端(相對於Android系統)
的一個Window
實體)。
WindowManagerImpl
其實只是一個簡單的裝飾類,全部操做直接轉發到了WindowManagerGlobal
, 所以windowManager.addView()
源碼的追蹤能夠直接看WindowManagerGlobal.addView()
:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
}
複製代碼
parentWindow
這個參數其實就是Activity
的window(PhoneWindow)
。而WindowManagerGlobal.addView()
作的主要事情是:
(root)ViewRootImpl
contentView
相關對象放入到mViews/mRoots/mParams
集合中。(若是contentView
被移除,那麼這3個集合相關對象也會被移除)root.setView(contentView..)
會經過IPC調用到WindowManagerService
來在window
中顯示contentView
。因此windowManager.addView()
作的事情是:
爲contentView
建立一個ViewRootImpl
對象,並把contentView
相關對象放入到mViews/mRoots/mParams
集合中維護起來,而後調用ViewRootImpl.setView(..)
方法來顯示contentView
因此到這裏能夠用下面這張圖總結一下Activity/Window/WindowManager
之間的關係:
通過上面的分析咱們還知道 : 經過windowManager.addView(contentView)
來顯示視圖實際上是和Activity
的Window
有着密切的聯繫的(顯示一個視圖必需要有Window
)。那Activity
的視圖是怎麼顯示的呢?
咱們繼續看一下(其實Activity
的視圖也是經過windowManager.addView(contentView)
的方式來顯示的):
追蹤Activity.setContentView(..)
源碼能夠看到:
getWindow().setContentView(contentView);
複製代碼
即咱們的contentView
實際上是設置給了Window(PhoneWindow)
:
PhoneWindow.java
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
...
mContentParent.addView(view, params);
}
複製代碼
即咱們Activity
的根佈局View實際上是添加到了PhoneWindow的mContentParent成員變量中
中。那mContentParent
是什麼呢?看一下PhoneWindow.installDecor()
,這個方法也很長,所以只截取最重要的部分看一下:
private void installDecor() {
mDecor = generateDecor(-1); // decor 的實例時DecorView,它繼承自FrameLayout
...
mDecor.setWindow(this); //DecorView 綁定一個window
...
mContentParent = generateLayout(mDecor); // mContentParent 會被add到 decor view中
...
}
複製代碼
根據上面的註釋,咱們能夠先這樣理解PhoneWindow/DecorView/mContentParent
的關係:
PhoneWindow
裏存在一個DecorView(mDecor)
成員變量,能夠把它理解爲一個FrameLayout
,它包含一個mContentParent
的子View, mContentParent
是Activity
的根佈局contentView
的父View
那mContentParent
是一個什麼樣的佈局/View
呢?繼續看一下generateLayout(mDecor)
:
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource; //mContentParent的佈局文件
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
....各類 if else
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
}
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //會把這個佈局文件inflate出的view,添加到DecorView中
//經過 findViewById 來獲取 ContentParent。 這個id其實就來自 layoutResource 所指向的佈局文件
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
}
複製代碼
上面我作了一些註釋,能夠理解爲mContentParent
就是DecorView
的子View。layoutResource
根據當前Activity
的Theme
的設置,會對應到許多不一樣的佈局文件,R.layout.screen_toolbar
是給Activity
設置默認 Theme
是所對應的佈局文件:
<com.android.internal.widget.ActionBarOverlayLayout
android:id="@+id/decor_content_parent"
...>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.android.internal.widget.ActionBarContainer
android:id="@+id/action_bar_container"
...
android:gravity="top">
<Toolbar
android:id="@+id/action_bar"
... />
<com.android.internal.widget.ActionBarContextView
android:id="@+id/action_context_bar"
..../>
</com.android.internal.widget.ActionBarContainer>
</com.android.internal.widget.ActionBarOverlayLayout>
複製代碼
看一個具體的Android Layout Inspectot
分析:
ContentFrameLayout
是support v7
的類,能夠把它理解爲FrameLayout
。
PhoneWindow
的視圖層級能夠用下圖表示
通過上面的分析: PhoneWindow
的視圖層級其實就是DecorView
的視圖層級。DecorView
就是Activity
視圖的根View
。
因此Activity
的視圖顯示的過程其實就是DecorView
的視圖顯示的過程。那DecorView
如何顯示呢?
它的顯示原理也是使用windowManager.addView()
:
在Activity Resume
時,DecorView
會添加到WindowManager
中:
ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
final Activity a = r.activity;
...
wm.addView(decor, l);
}
複製代碼
即在Activity Resume
時DecorView
添加到WindowManager
中,進而經過WindowManagerService
來顯示成功。因此Activity.onResume()
用戶才能夠看到Activity
的視圖。
總結一下到目前爲止所分析的點:
Window
。WindowManager
屬於Window
,負責管理Window
中View
的顯示。在Window
中顯示View
咱們應使用它的接口Window
能夠有多個子View,每一個子View
都對應一個ViewRootImpl
。ViewRootImpl
會經過IPC來與WindowManagerService
交互,來實現View
的顯示它們之間的關係以下圖:
下一篇文章將繼續分析ViewRootImpl.setView(..)
所引發的WindowManagerService
的操做,即Window
是怎麼在屏幕上展現內容的。
歡迎關注個人Android進階計劃看更多幹貨
歡迎關注個人微信公衆號:susion隨心
參考文章: