原文出處——>[Android應用程序窗口(Activity)的視圖對象(View)的創建過程分析](http://blog.csdn.net/luoshengyang/article/details/8245546)
從前文可知道,每一個Activity組件都有一個關聯的Window對象,用來描述一個應用程序窗口。每一個應用程序窗口內部又包含有一個View對象,用來描述應用程序窗口的視圖。應用程序窗口視圖是真正用來實現UI內容和布局的,也就是說,每一個Activity組件的UI內容和布局都是通過與其所關聯的一個Window對象的內部的一個View對象來實現的。在本文中,我們就詳細分析應用程序窗口視圖的創建過程。
在前面Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文中提到,應用程序窗口內部所包含的視圖對象的實際類型為DecorView。DecorView類繼承了View類,是作為容器(ViewGroup)來使用的,它的實現如圖1所示:
:-: 
圖1 DecorView類的實現
這個圖的具體描述可以參考Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文中的圖5,這里不再詳述。
從前面Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文還可以知道,每一個應用程序窗口的視圖對象都有一個關聯的ViewRoot對象,這些關聯關系是由窗口管理器來維護的,如圖2所示:
:-: 
圖2 應用程序窗口視圖與ViewRoot的關系圖
這個圖的具體描述可以參考Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文中的圖6,這里不再詳述。
簡單來說,ViewRoot相當于是MVC模型中的Controller,它有以下職責:
1. 負責為應用程序窗口視圖創建Surface。
2. 配合WindowManagerService來管理系統的應用程序窗口。
3. 負責管理、布局和渲染應用程序窗口視圖的UI。
那么,應用程序窗口的視圖對象及其所關聯的ViewRoot對象是什么時候開始創建的呢? 從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,Activity組件在啟動的時候,系統會為它創建窗口對象(Window),同時,系統也會為這個窗口對象創建視圖對象。另一方面,當Activity組件被激活的時候,系統如果發現與它的應用程序窗口視圖對象所關聯的ViewRoot對象還沒有創建,那么就會先創建這個ViewRoot對象,以便接下來可以將它的UI渲染出來。
從前面Android應用程序啟動過程源代碼分析一文可以知道,Activity組件在啟動的過程中,會調用ActivityThread類的成員函數handleLaunchActivity,用來創建以及首次激活Activity組件,因此,接下來我們就從這個函數開始,具體分析應用程序窗口的視圖對象及其所關聯的ViewRoot對象的創建過程,如圖3所示:

圖3 應用程序窗口視圖的創建過程
這個過程一共可以分為13個步驟,接下來我們就詳細分析每一個步驟。
**Step 1. ActivityThread.handleLaunchActivity**
~~~
public final class ActivityThread {
......
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
......
handleResumeActivity(r.token, false, r.isForward);
......
}
......
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。
函數首先調用ActivityThread類的成員函數performLaunchActivity來創建要啟動的Activity組件。在創建Activity組件的過程中,還會為該Activity組件創建窗口對象和視圖對象。Activity組件創建完成之后,就可以將它激活起來了,這是通過調用ActivityThread類的成員函數handleResumeActivity來執行的。
接下來,我們首先分析ActivityThread類的成員函數performLaunchActivity的實現,以便可以了解應用程序窗口視圖對象的創建過程,接著再回過頭來繼續分析ActivityThread類的成員函數handleResumeActivity的實現,以便可以了解與應用程序窗口視圖對象所關聯的ViewRoot對象的創建過程。
**Step 2. ActivityThread.performLaunchActivity**
這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java文件中。
這一步可以參考Android應用程序窗口(Activity)的運行上下文環境(Context)的創建過程分析一文的Step 1,它主要就是創建一個Activity組件實例,并且調用這個Activity組件實例的成員函數onCreate來讓其執行一些自定義的初始化工作。
**Step 3. Activity.onCreate**
這個函數定義在文件frameworks/base/core/java/android/app/Activity.java中。
這一步可以參考Android應用程序窗口(Activity)的運行上下文環境(Context)的創建過程分析一文的Step 10。我們在實現一個Activity組件的時候,也就是在實現一個Activity子類的時候,一般都會重寫成員函數onCreate,以便可以執行一些自定義的初始化工作,其中就包含初始化UI的工作。例如,在前面在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務一文中,我們實現了一個名稱為Hello的Activity組件,用來測試硬件服務,它的成員函數onCreate的樣子長得大概如下所示:
~~~
public class Hello extends Activity implements OnClickListener {
......
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
......
}
......
}
~~~
其中,調用從父類Activity繼承下來的成員函數setContentView就是用來創建應用程序窗口視圖對象的。
接下來,我們就繼續分析Activity類的成員函數setContentView的實現。
**Step 4. Activity.setContentView**
~~~
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
private Window mWindow;
......
public Window getWindow() {
return mWindow;
}
......
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/app/Activity.java中。
Activity類的成員函數setContentView首先調用另外一個成員函數getWindow來獲得成員變量mWindow所描述的一個窗口對象,接著再調用這個窗口對象的成員函數setContentView來執行創建應用程序窗口視圖對象的工作。
從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,Activity類的成員變量mWindow指向的是一個PhoneWindow對象,因此,接下來我們就繼續分析PhoneWindow類的成員函數setContentView的實現。
**Step 5. PhoneWindow.setContentView**
~~~
public class PhoneWindow extends Window implements MenuBuilder.Callback {
......
// 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;
......
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
......
}
~~~
這個函數定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
PhoneWindow類的成員變量mContentParent用來描述一個類型為DecorView的視圖對象,或者這個類型為DecorView的視圖對象的一個子視圖對象,用作UI容器。當它的值等于null的時候,就說明正在處理的應用程序窗口的視圖對象還沒有創建。在這種情況下,就會調用成員函數installDecor來創建應用程序窗口視圖對象。否則的話,就說明是要重新設置應用程序窗口的視圖。在重新設置之前,首先調用成員變量mContentParent所描述的一個ViewGroup對象來移除原來的UI內空。
由于我們是在Activity組件啟動的過程中創建應用程序窗口視圖的,因此,我們就假設此時PhoneWindow類的成員變量mContentParent的值等于null。接下來,函數就會調用成員函數installDecor來創建應用程序窗口視圖對象,接著再通過調用PhoneWindow類的成員變量mLayoutInflater所描述的一個LayoutInflater對象的成員函數inflate來將參數layoutResID所描述的一個UI布局設置到前面所創建的應用程序窗口視圖中去,最后還會調用一個Callback接口的成員函數onContentChanged來通知對應的Activity組件,它的視圖內容發生改變了。從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,Activity組件自己實現了這個Callback接口,并且將這個Callback接口設置到了與它所關聯的應用程序窗口對象的內部去,因此,前面實際調用的是Activity類的成員函數onContentChanged來發出一個視圖內容變化通知。
接下來,我們就繼續分析PhoneWindow類的成員函數installDecor的實現,以便可以繼續了解應用程序窗口視圖對象的創建過程。
**Step 6. PhoneWindow.installDecor**
~~~
public class PhoneWindow extends Window implements MenuBuilder.Callback {
......
// 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;
......
private TextView mTitleView;
......
private CharSequence mTitle = null;
......
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
......
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
View titleContainer = findViewById(com.android.internal.R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
if (mContentParent instanceof FrameLayout) {
((FrameLayout)mContentParent).setForeground(null);
}
} else {
mTitleView.setText(mTitle);
}
}
}
}
......
}
~~~
這個函數定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
由于我們是在Activity組件啟動的過程中創建應用程序窗口視圖的,因此,我們同時假設此時PhoneWindow類的成員變量mDecor的值等于null。這時候PhoneWindow類的成員函數installDecor就會調用另外一個成員函數generateDecor來創建一個DecorView對象,并且保存在PhoneWindow類的成員變量mDecor中。
PhoneWindow類的成員函數installDecor接著再調用另外一個成員函數generateLayout來根據當前應用程序窗口的Feature來加載對應的窗口布局文件。這些布局文件保存在frameworks/base/core/res/res/layout目錄下,它們必須包含有一個id值為“content”的布局控件。這個布局控件必須要從ViewGroup類繼承下來,用來作為窗口的UI容器。PhoneWindow類的成員函數generateLayout執行完成之后,就會這個id值為“content”的ViewGroup控件來給PhoneWindow類的成員函數installDecor,后者再將其保存在成員變量mContentParent中。
PhoneWindow類的成員函數installDecor還會檢查前面加載的窗口布局文件是否包含有一個id值為“title”的TextView控件。如果包含有的話,就會將它保存在PhoneWindow類的成員變量mTitleView中,用來描述當前應用程序窗口的標題欄。但是,如果當前應用程序窗口是沒有標題欄的,即它的Feature位FEATURE_NO_TITLE的值等于1,那么PhoneWindow類的成員函數installDecor就需要將前面得到的標題欄隱藏起來。注意,PhoneWindow類的成員變量mTitleView所描述的標題欄有可能是包含在一個id值為“title_container”的容器里面的,在這種情況下,就需要隱藏該標題欄容器。另一方面,如果當前應用程序窗口是設置有標題欄的,那么PhoneWindow類的成員函數installDecor就會設置它的標題欄文字。應用程序窗口的標題欄文字保存在PhoneWindow類的成員變量mTitle中,我們可以調用PhoneWindow類的成員函數setTitle來設置。
這一步執行完成之后,應用程序窗口視圖就創建完成了,回到前面的Step 1中,即ActivityThread類的成員函數handleLaunchActivity中,接下來就會調用ActivityThread類的另外一個成員函數handleResumeActivity來激活正在啟動的Activity組件。由于在是第一次激活該Activity組件,因此,在激活之前,還會為該Activity組件創建一個ViewRoot對象,并且與前面所創建的應用程序窗口視圖關聯起來,以便后面可以通過該ViewRoot對象來控制應用程序窗口視圖的UI展現。
接下來,我們就繼續分析ActivityThread類的成員函數handleResumeActivity的實現。
**Step 7. ActivityThread.handleResumeActivity**
~~~
public final class ActivityThread {
......
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
......
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
......
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
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;
......
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
......
}
......
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。
ActivityThread類的成員函數handleResumeActivity首先調用另外一個成員函數performResumeActivity來通知Activity組件,它要被激活了,即會導致Activity組件的成員函數onResume被調用。ActivityThread類的成員函數performResumeActivity的返回值是一個ActivityClientRecord對象r,這個ActivityClientRecord對象的成員變量activity描述的就是正在激活的Activity組件a。
ActivityThread類的成員函數handleResumeActivity接下來判斷正在激活的Activity組件接下來是否是可見的。如果是可見的,那么變量willBeVisible的值就會等于true。Activity類的成員變量mStartedActivity用來描述一個Activity組件是否正在啟動一個新的Activity組件,并且等待這個新的Activity組件的執行結果。如果是的話,那么這個Activity組件的成員變量mStartedActivity的值就會等于true,表示在新的Activity組件的執行結果返回來之前,當前Activity組件要保持不可見的狀態。因此,當Activity組件a的成員變量mStartedActivity的值等于true的時候,它接下來就是不可見的,否則的話,就是可見的。
雖然說在Activity組件a的成員變量mStartedActivity的值等于true的情況下,它接下來的狀態要保持不可見的,但是有可能它所啟動的Activity組件的UI不是全屏的。在這種情況下,Activity組件a的UI仍然是有部分可見的,這時候也要將變量willBeVisible的值設置為true。因此,如果前面得到變量willBeVisible的值等于false,那么ActivityThread類的成員函數handleResumeActivity接下來就會通過Binder進程間通信機制來調用ActivityManagerService服務的成員函數willActivityBeVisible來檢查位于Activity組件a上面的其它Activity組件(包含了Activity組件a正在等待其執行結果的Activity組件)是否是全屏的。如果不是,那么ActivityManagerService服務的成員函數willActivityBeVisible的返回值就會等于true,表示接下來需要顯示Activity組件a。
前面得到的ActivityClientRecord對象r的成員變量window用來描述當前正在激活的Activity組件a所關聯的應用程序窗口對象。當它的值等于null的時候,就表示當前正在激活的Activity組件a所關聯的應用程序窗口對象還沒有關聯一個ViewRoot對象。進一步地,如果這個正在激活的Activity組件a還活著,并且接下來是可見的,即ActivityClientRecord對象r的成員變量mFinished的值等于false,并且前面得到的變量willBeVisible的值等于true,那么這時候就說明需要為與Activity組件a所關聯的一個應用程序窗口視圖對象關聯的一個ViewRoot對象。
將一個Activity組件的應用程序窗口視圖對象與一個ViewRoot對象關聯是通過該Activity組件所使用的窗口管理器來執行的。從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,一個Activity組件所使用的本地窗口管理器保存它的成員變量mWindowManager中,這可以通過Activity類的成員函數getWindowManager來獲得。在接下來的Step 10中,我們再分析Activity類的成員函數getWindowManager的實現。
由于我們現在要給Activity組件a的應用程序窗口視圖對象關聯一個ViewRoot對象,因此,我們就需要首先獲得這個應用程序窗口視圖對象。從前面的Step 6可以知道,一個Activity組件的應用程序窗口視圖對象保存在與其所關聯的一個應用程序窗口對象的內部,因此,我們又要首先獲得這個應用程序窗口對象。與一個Activity組件所關聯的應用程序窗口對象可以通過調用該Activity組件的成員函數getWindow來獲得。一旦獲得了這個應用程序窗口對象(類型為PhoneWindow)之后,我們就可以調用它的成員函數getDecorView來獲得它內部的視圖對象。在接下來的Step 8和Step 9中,我們再分別分析Activity類的成員函數Activity類的成員函數getWindow和PhoneWindow類的成員函數getDecorView的實現。
在關聯應用程序窗口視圖對象和ViewRoot對象的時候,還需要第三個參數,即應用程序窗口的布局參數,這是一個類型為WindowManager.LayoutParams的對象,可以通過調用應用程序窗口的成員函數getAttributes來獲得。一切準備就緒之后,還要判斷最后一個條件是否成立,即當前正在激活的Activity組件a在本地進程中是否是可見的,即它的成員變量mVisibleFromClient的值是否等于true。如果是可見的,那么最后就可以調用前面所獲得的一個本地窗口管理器wm(類型為LocalWindowManager)的成員函數addView來執行關聯應用程序窗口視圖對象和ViewRoot對象的操作。
接下來,我們就分別分析Activity類的成員函數getWindow、PhoneWindow類的成員函數getDecorView、ctivity類的成員函數getWindowManager以及LocalWindowManager類的成員函數addView的實現。
**Step 8. Activity.getWindow**
~~~
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
private Window mWindow;
......
public Window getWindow() {
return mWindow;
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/app/Activity.java中。
從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,Activity類的成員變量mWindow指向的是一個類型為PhoneWindow的窗口對象,因此,Activity類的成員函數getWindow返回給調用者的是一個PhoneWindow對象。
這一步執完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數handleResumeActivity中,接下來就會繼續調用前面所獲得的一個PhoneWindow對象的成員函數getDecorView來獲得當前正在激活的Activity組件所關聯的一個應用程序窗口視圖對象。
**Step 9. PhoneWindow.getDecorView**
~~~
public class PhoneWindow extends Window implements MenuBuilder.Callback {
......
private DecorView mDecor;
......
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}
......
}
~~~
這個函數定義在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。
PhoneWindow類的成員函數getDecorView首先判斷成員變量mDecor的值是否等于null。如果是的話,那么就說明當前正在處理的應用程序窗口還沒有創建視圖對象。這時候就會調用另外一個成員函數installDecor來創建這個視圖對象。從前面的調用過程可以知道,當前正在處理的應用程序窗口已經創建過視圖對象,因此,這里的成員變量mDecor的值不等于null,PhoneWindow類的成員函數getDecorView直接將它返回給調用者。
這一步執完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數handleResumeActivity中,接下來就會繼續調用當前正在激活的Activity組件的成員函數getWindowManager來獲得一個本地窗口管理器。
**Step 10. Activity.getWindowManager**
~~~
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
private WindowManager mWindowManager;
......
public WindowManager getWindowManager() {
return mWindowManager;
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/app/Activity.java中。
從前面Android應用程序窗口(Activity)的運行上下文環境(Context)的創建過程分析一文可以知道,Activity類的成員變量mWindowManager指向的一是類型為LocalWindowManager的本地窗口管理器,Activity類的成員函數getWindowManager直接將它返回給調用者。
這一步執完成之后,返回到前面的Step 7中,即ActivityThread類的成員函數handleResumeActivity中,接下來就會繼續調用前面所獲得的一個LocalWindowManager對象的成員函數addView來為當前正在激活的Activity組件的應用程序窗口視圖對象關聯一個ViewRoot對象。
**Step 11. LocalWindowManager.addView**
~~~
public abstract class Window {
......
private class LocalWindowManager implements WindowManager {
......
public final void addView(View view, ViewGroup.LayoutParams params) {
......
mWindowManager.addView(view, params);
}
......
private final WindowManager mWindowManager;
......
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/view/Window.java中。
從前面Android應用程序窗口(Activity)的窗口對象(Window)的創建過程分析一文可以知道,LocalWindowManager類的成員變量mWindowManager指向的是一個WindowManagerImpl對象,因此,LocalWindowManager類的成員函數addView接下來調用WindowManagerImpl類的成員函數addView來給參數view所描述的一個應用程序窗口視圖對象關聯一個ViewRoot對象。
**Step 12. WindowManagerImpl.addView**
~~~
public class WindowManagerImpl implements WindowManager {
......
public void addView(View view, ViewGroup.LayoutParams params)
{
addView(view, params, false);
}
......
private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
{
......
final WindowManager.LayoutParams wparams
= (WindowManager.LayoutParams)params;
ViewRoot root;
View panelParentView = null;
synchronized (this) {
// Here's an odd/questionable case: if someone tries to add a
// view multiple times, then we simply bump up a nesting count
// and they need to remove the view the corresponding number of
// times to have it actually removed from the window manager.
// This is useful specifically for the notification manager,
// which can continually add/remove the same view as a
// notification gets updated.
int index = findViewLocked(view, false);
if (index >= 0) {
if (!nest) {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
root = mRoots[index];
root.mAddNesting++;
// Update layout parameters.
view.setLayoutParams(wparams);
root.setLayoutParams(wparams, true);
return;
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews != null ? mViews.length : 0;
for (int i=0; i<count; i++) {
if (mRoots[i].mWindow.asBinder() == wparams.token) {
panelParentView = mViews[i];
}
}
}
root = new ViewRoot(view.getContext());
root.mAddNesting = 1;
view.setLayoutParams(wparams);
if (mViews == null) {
index = 1;
mViews = new View[1];
mRoots = new ViewRoot[1];
mParams = new WindowManager.LayoutParams[1];
} else {
index = mViews.length + 1;
Object[] old = mViews;
mViews = new View[index];
System.arraycopy(old, 0, mViews, 0, index-1);
old = mRoots;
mRoots = new ViewRoot[index];
System.arraycopy(old, 0, mRoots, 0, index-1);
old = mParams;
mParams = new WindowManager.LayoutParams[index];
System.arraycopy(old, 0, mParams, 0, index-1);
}
index--;
mViews[index] = view;
mRoots[index] = root;
mParams[index] = wparams;
}
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
}
......
private View[] mViews;
private ViewRoot[] mRoots;
private WindowManager.LayoutParams[] mParams;
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/view/WindowManagerImpl.java中。
在WindowManagerImpl類中,兩個參數版本的成員函數addView是通過調用三個參數版本的成同函數addView來實現的,因此,我們接下來就主要分析三個參數版本的成員函數addView的實現。
在分析WindowManagerImpl類的三個參數版本的成員函數addView的實現之前,我們首先了解一下WindowManagerImpl類是如何關聯一個應用程序窗口視圖對象(View對象)和一個ViewRoot對象的。一個View對象在與一個ViewRoot對象關聯的同時,還會關聯一個WindowManager.LayoutParams對象,這個WindowManager.LayoutParams對象是用來描述應用程序窗口視圖的布局屬性的。
WindowManagerImpl類有三個成員變量mViews、mRoots和mParams,它們分別是類型為View、ViewRoot和WindowManager.LayoutParams的數組。這三個數組的大小是始終保持相等的。這樣, 有關聯關系的View對象、ViewRoot對象和WindowManager.LayoutParams對象就會分別保存在數組mViews、mRoots和mParams的相同位置上,也就是說,mViews[i]、mRoots[i]和mParams[i]所描述的View對象、ViewRoot對象和WindowManager.LayoutParams對象是具有關聯關系的。因此,WindowManagerImpl類的三個參數版本的成員函數addView在關聯一個View對象、一個ViewRoot對象和一個WindowManager.LayoutParams對象的時候,只要分別將它們放在數組mViews、mRoots和mParams的相同位置上就可以了。
理解了一個View對象、一個ViewRoot對象和一個WindowManager.LayoutParams對象是如何關聯之后,WindowManagerImpl類的三個參數版本的成員函數addView的實現就容易理解了。
參數view和參數params描述的就是要關聯的View對象和WindowManager.LayoutParams對象。成員函數addView首先調用另外一個成員函數findViewLocked來檢查參數view所描述的一個View對象是否已經存在于數組中mViews中了。如果已經存在的話,那么就說明該View對象已經關聯過ViewRoot對象以及WindowManager.LayoutParams對象了。在這種情況下,如果參數nest的值等于false,那么成員函數addView是不允許重復對參數view所描述的一個View對象進行重新關聯的。另一方面,如果參數nest的值等于true,那么成員函數addView只是重新修改參數view所描述的一個View對象及其所關聯的一個ViewRoot對象內部使用的一個WindowManager.LayoutParams對象,即更新為參數params所描述的一個WindowManager.LayoutParams對象,這是通過調用它們的成員函數setLayoutParams來實現的。
如果參數view所描述的一個View對象還沒有被關聯過一個ViewRoot對象,那么成員函數addView就會創建一個ViewRoot對象,并且將它與參數view和params分別描述的一個View對象和一個WindowManager.LayoutParams對象保存在數組mViews、mRoots和mParams的相同位置上。注意,如果數組mViews、mRoots和mParams尚未創建,那么成員函數addView就會首先分別為它們創建一個大小為1的數組,以便可以用來分別保存所要關聯的View對象、ViewRoot對象和WindowManager.LayoutParams對象。另一方面,如果數組mViews、mRoots和mParams已經創建,那么成員函數addView就需要分別將它們的大小增加1,以便可以在它們的末尾位置上分別保存所要關聯的View對象、ViewRoot對象和WindowManager.LayoutParams對象。
還有另外一個需要注意的地方是當參數view描述的是一個子應用程序窗口的視圖對象時,即WindowManager.LayoutParams對象wparams的成員變量type的值大于等于WindowManager.LayoutParams.FIRST_SUB_WINDOW并且小于等于WindowManager.LayoutParams.LAST_SUB_WINDOW時,那么成員函數addView還需要找到這個子視圖對象的父視圖對象panelParentView,這是通過遍歷數組mRoots來查找的。首先,WindowManager.LayoutParams對象wparams的成員變量token指向了一個類型為W的Binder本地對象的一個IBinder接口,用來描述參數view所描述的一個子應用程序窗口視圖對象所屬的父應用程序窗口視圖對象。其次,每一個ViewRoot對象都通過其成員變量mWindow來保存一個類型為W的Binder本地對象,因此,如果在數組mRoots中,存在一個ViewRoot對象,它的成員變量mWindow所描述的一個W對象的一個IBinder接口等于WindowManager.LayoutParams對象wparams的成員變量token所描述的一個IBinder接口時,那么就說明與該ViewRoot對象所關聯的View對象即為參數view的父應用程序窗口視圖對象。
成員函數addView為參數view所描述的一個View對象和參數params所描述的一個WindowManager.LayoutParams對象關聯好一個ViewRoot對象root之后,最后還會將這個View對view象和這個WindowManager.LayoutParams對象,以及變量panelParentView所描述的一個父應用程序窗視圖對象,保存在這個ViewRoot對象root的內部去,這是通過調用這個ViewRoot對象root的成員函數setView來實現的,因此,接下來我們就繼續分析ViewRoot類的成員函數setView的實現。
**Step 13. ViewRoot.setView**
~~~
public final class ViewRoot extends Handler implements ViewParent,
View.AttachInfo.Callbacks {
......
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
......
View mView;
......
final View.AttachInfo mAttachInfo;
......
boolean mAdded;
......
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mWindowAttributes.copyFrom(attrs);
......
mAttachInfo.mRootView = view;
.......
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
}
mAdded = true;
......
requestLayout();
......
try {
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
......
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
......
}
......
}
}
......
}
~~~
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java中。
參數view所描述的一個View對象會分別被保存在ViewRoot類的成員變量mView以及成員變量mAttachInfo所描述的一個AttachInfo的成員變量mRootView中,而參數attrs所描述的一個WindowManager.LayoutParams對象的內容會被拷貝到ViewRoot類的成員變量mWindowAttributes中去。
當參數panelParentView的值不等于null的時候,就表示參數view描述的是一個子應用程序窗口視圖對象。在這種情況下,參數panelParentView描述的就是一個父應用程序窗口視圖對象。這時候我們就需要獲得用來描述這個父應用程序窗口視圖對象的一個類型為W的Binder本地對象的IBinder接口,以便可以保存在ViewRoot類的成員變量mAttachInfo所描述的一個AttachInfo的成員變量mPanelParentWindowToken中去。這樣以后就可以知道ViewRoot類的成員變量mView所描述的一個子應用程序窗口視圖所屬的父應用程序窗口視圖是什么了。注意,通過調用參數panelParentView的所描述的一個View對象的成員函數getApplicationWindowToken即可以獲得一個對應的W對象的IBinder接口。
上述操作執行完成之后,ViewRoot類的成員函數setView就可以將成員變量mAdded的值設置為true了,表示當前正在處理的一個ViewRoot對象已經關聯好一個View對象了。接下來,ViewRoot類的成員函數setView還需要執行兩個操作:
1. 調用ViewRoot類的另外一個成員函數requestLayout來請求對應用程序窗口視圖的UI作第一次布局。
2. 調用ViewRoot類的靜態成員變量sWindowSession所描述的一個類型為Session的Binder代理對象的成員函數add來請求WindowManagerService增加一個WindowState對象,以便可以用來描述當前正在處理的一個ViewRoot所關聯的一個應用程序窗口。
至此,我們就分析完成Android應用程序窗口視圖對象的創建過程了。在接下來的一篇文章中,我們將會繼續分析Android應用程序窗口與WindowManagerService服務的連接過程,即Android應用程序窗口請求WindowManagerService為其增加一個WindowState對象的過程,而在接下來的兩篇文章中,我們還會分析用來渲染Android應用程序窗口的Surface的創建過程,以及Android應用程序窗口的渲染過程。通過這三個過程的分析,我們就可以看到上述第1點和第2點提到的兩個函數的執行過程,敬請期待!
- 前言
- Android組件設計思想
- Android源代碼開發和調試環境搭建
- Android源代碼下載和編譯
- Android源代碼情景分析法
- Android源代碼調試分析法
- 手把手教你為手機編譯ROM
- 在Ubuntu上下載、編譯和安裝Android最新源代碼
- 在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)
- 如何單獨編譯Android源代碼中的模塊
- 在Ubuntu上為Android系統編寫Linux內核驅動程序
- 在Ubuntu上為Android系統內置C可執行程序測試Linux內核驅動程序
- 在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序
- 在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口
- 在Ubuntu上為Android系統的Application Frameworks層增加硬件訪問服務
- 在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務
- Android源代碼倉庫及其管理工具Repo分析
- Android編譯系統簡要介紹和學習計劃
- Android編譯系統環境初始化過程分析
- Android源代碼編譯命令m/mm/mmm/make分析
- Android系統鏡像文件的打包過程分析
- 從CM刷機過程和原理分析Android系統結構
- Android系統架構概述
- Android系統整體架構
- android專用驅動
- Android硬件抽象層HAL
- Android應用程序組件
- Android應用程序框架
- Android用戶界面架構
- Android虛擬機之Dalvik虛擬機
- Android硬件抽象層
- Android硬件抽象層(HAL)概要介紹和學習計劃
- Android專用驅動
- Android Logger驅動系統
- Android日志系統驅動程序Logger源代碼分析
- Android應用程序框架層和系統運行庫層日志系統源代碼分析
- Android日志系統Logcat源代碼簡要分析
- Android Binder驅動系統
- Android進程間通信(IPC)機制Binder簡要介紹和學習計劃
- 淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路
- 淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路
- Android系統進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析
- Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
- Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
- Android Ashmem驅動系統
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)驅動程序源代碼分析
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)在進程間共享的原理分析
- Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析
- Android應用程序進程管理
- Android應用程序進程啟動過程的源代碼分析
- Android系統進程Zygote啟動過程的源代碼分析
- Android系統默認Home應用程序(Launcher)的啟動過程源代碼分析
- Android應用程序消息機制
- Android應用程序消息處理機制(Looper、Handler)分析
- Android應用程序線程消息循環模型分析
- Android應用程序輸入事件分發和處理機制
- Android應用程序鍵盤(Keyboard)消息處理機制分析
- Android應用程序UI架構
- Android系統的開機畫面顯示過程分析
- Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析
- SurfaceFlinger
- Android系統Surface機制的SurfaceFlinger服務
- SurfaceFlinger服務簡要介紹和學習計劃
- 啟動過程分析
- 對幀緩沖區(Frame Buffer)的管理分析
- 線程模型分析
- 渲染應用程序UI的過程分析
- Android應用程序與SurfaceFlinger服務的關系
- 概述和學習計劃
- 連接過程分析
- 共享UI元數據(SharedClient)的創建過程分析
- 創建Surface的過程分析
- 渲染Surface的過程分析
- Android應用程序窗口(Activity)
- 實現框架簡要介紹和學習計劃
- 運行上下文環境(Context)的創建過程分析
- 窗口對象(Window)的創建過程分析
- 視圖對象(View)的創建過程分析
- 與WindowManagerService服務的連接過程分析
- 繪圖表面(Surface)的創建過程分析
- 測量(Measure)、布局(Layout)和繪制(Draw)過程分析
- WindowManagerService
- WindowManagerService的簡要介紹和學習計劃
- 計算Activity窗口大小的過程分析
- 對窗口的組織方式分析
- 對輸入法窗口(Input Method Window)的管理分析
- 對壁紙窗口(Wallpaper Window)的管理分析
- 計算窗口Z軸位置的過程分析
- 顯示Activity組件的啟動窗口(Starting Window)的過程分析
- 切換Activity窗口(App Transition)的過程分析
- 顯示窗口動畫的原理分析
- Android控件TextView的實現原理分析
- Android視圖SurfaceView的實現原理分析
- Android應用程序UI硬件加速渲染
- 簡要介紹和學習計劃
- 環境初始化過程分析
- 預加載資源地圖集服務(Asset Atlas Service)分析
- Display List構建過程分析
- Display List渲染過程分析
- 動畫執行過程分析
- Android應用程序資源管理框架
- Android資源管理框架(Asset Manager)
- Asset Manager 簡要介紹和學習計劃
- 編譯和打包過程分析
- Asset Manager的創建過程分析
- 查找過程分析
- Dalvik虛擬機和ART虛擬機
- Dalvik虛擬機
- Dalvik虛擬機簡要介紹和學習計劃
- Dalvik虛擬機的啟動過程分析
- Dalvik虛擬機的運行過程分析
- Dalvik虛擬機JNI方法的注冊過程分析
- Dalvik虛擬機進程和線程的創建過程分析
- Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃
- Dalvik虛擬機Java堆創建過程分析
- Dalvik虛擬機為新創建對象分配內存的過程分析
- Dalvik虛擬機垃圾收集(GC)過程分析
- ART虛擬機
- Android ART運行時無縫替換Dalvik虛擬機的過程分析
- Android運行時ART簡要介紹和學習計劃
- Android運行時ART加載OAT文件的過程分析
- Android運行時ART加載類和方法的過程分析
- Android運行時ART執行類方法的過程分析
- ART運行時垃圾收集機制簡要介紹和學習計劃
- ART運行時Java堆創建過程分析
- ART運行時為新創建對象分配內存的過程分析
- ART運行時垃圾收集(GC)過程分析
- ART運行時Compacting GC簡要介紹和學習計劃
- ART運行時Compacting GC堆創建過程分析
- ART運行時Compacting GC為新創建對象分配內存的過程分析
- ART運行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析
- ART運行時Mark-Compact( MC)GC執行過程分析
- ART運行時Foreground GC和Background GC切換過程分析
- Android安全機制
- SEAndroid安全機制簡要介紹和學習計劃
- SEAndroid安全機制框架分析
- SEAndroid安全機制中的文件安全上下文關聯分析
- SEAndroid安全機制中的進程安全上下文關聯分析
- SEAndroid安全機制對Android屬性訪問的保護分析
- SEAndroid安全機制對Binder IPC的保護分析
- 從NDK在非Root手機上的調試原理探討Android的安全機制
- APK防反編譯
- Android視頻硬解穩定性問題探討和處理
- Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析
- Android應用程序安裝過程源代碼分析
- Android應用程序啟動過程源代碼分析
- 四大組件源代碼分析
- Activity
- Android應用程序的Activity啟動過程簡要介紹和學習計劃
- Android應用程序內部啟動Activity過程(startActivity)的源代碼分析
- 解開Android應用程序組件Activity的"singleTask"之謎
- Android應用程序在新的進程中啟動新的Activity的方法和過程分析
- Service
- Android應用程序綁定服務(bindService)的過程源代碼分析
- ContentProvider
- Android應用程序組件Content Provider簡要介紹和學習計劃
- Android應用程序組件Content Provider應用實例
- Android應用程序組件Content Provider的啟動過程源代碼分析
- Android應用程序組件Content Provider在應用程序之間共享數據的原理分析
- Android應用程序組件Content Provider的共享數據更新通知機制分析
- BroadcastReceiver
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃
- Android應用程序注冊廣播接收器(registerReceiver)的過程分析
- Android應用程序發送廣播(sendBroadcast)的過程分析