Android版本:7.0(API27)java
[TOC]android
從ActivityClientRecord中獲取ComponentNamewindows
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
複製代碼
建立activitybash
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
複製代碼
建立appContext和Application對象app
ContextImpl appContext = createBaseContextForActivity(r);
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
複製代碼
上述信息準備完成後就開始Activity的初始化和啓動:ide
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
複製代碼
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,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(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;
mWindow.setColorMode(info.colorMode);
}
複製代碼
這裏最重要就是PhoneWindow的建立和設置Callback回調,這個Callback的實現是由Activity實現的,經過這一回調能夠將窗口中發生的變化通知到Activity。同時,將PhoneWindow賦值給內部變量mWindow。oop
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setCallback(this);
複製代碼
public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
final void performCreate(Bundle icicle) {
performCreate(icicle, null);
}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
mActivityTransitionState.readState(icicle);
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
}
複製代碼
最終就是調用onCreate,onCreate中會調用Activity.setContentView()post
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製代碼
getWindow()得到的是mWindow,從前面的分析能夠知道mWindow是PhoneWindow,那最終又調用到PhoneWindow內部的setContentView方法。ui
到這裏咱們尚未看到Activity窗口建立的具體過程,只是看到了咱們在onCreate中調用的setContentView設置的layout在PhoneWindow中被使用,其實這個祕密都隱藏在PhoneWindow中。this
Android提供了com.view.Window類以一個更高的級別操做窗口,Window類中有三個最核心的組件:WindowManager.LayoutParams、控件樹以及Window.Callback。
簡單來講,Window類是一個模板,它大大簡化了一個符合使用習慣的控件樹的建立過程。使得使用者僅須要關注控件樹中其真正感興趣的部分,而且僅需少許的工做就可使這部分嵌套在一個漂亮而專業的窗口外觀之下,而不用關心這一窗口外觀的控件樹的構成。
目前Window的惟一實現是PhoneWindow。Window類中提供了用於修改LayoutParams的接口等通用功能,而PhoneWindow類則負責具體的外觀模板的實現。
相信你們對Activity.requestWindowFeature()和Activity.setContentView()兩個方法不會陌生,前者負責指定Activity窗口的特性,如是否擁有標題欄,是否存在一個進度條,程序圖標的位置等。換而言之,Activity.requestWindowFeature()決定了窗口的外觀;然後者則設置開發者所指望顯示的控件樹,將這個控件樹填充到Activity的窗口中。
public final boolean requestWindowFeature(int featureId) {
return getWindow().requestFeature(featureId);
}
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製代碼
Activity中的這兩個方法都是將請求直接轉發給了PhoneWindow。
[PhoneWindow.requestWindowFeature()]
public boolean requestFeature(int featureId) {
/*requestFeature調用必須在setContentView以前,由於setContentView調用後,整個窗口就以及肯定了,此時是不容許再修改窗口的外觀的*/
if (mContentParentExplicitlySet) {
throw new AndroidRuntimeException("requestFeature() must be called before adding content");
}
/*這部分會作一系列的檢查,由於PhoneWindow容許使用者設置對個feature,可是不一樣的feature之間可能存在互斥,因此須要進行檢查*/
........
return super.requestFeature(featureId);
}
複製代碼
再看Window.requestFeature
public boolean requestFeature(int featureId) {
final int flag = 1<<featureId;
mFeatures |= flag;
mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;
return (mFeatures&flag) != 0;
}
複製代碼
可見設置窗口特性仍是十分簡單的。請記住窗口的特性被保存在Window.mFeatures成員之中。requestFeature方法並無馬上建立外觀模板,可是mFeatures成員將會爲建立外觀模板提供依據。
[PhoneWindow.setContentView()]
public void setContentView(int layoutResID) {
// 1. 首先爲窗口準備外觀模板
if (mContentParent == null) {
/*mContentParent爲null時,代表外觀模板還沒有建立,此時會經過installDecor方法建立一個外觀模板。建立完成以後mContentParent便會被設置爲模板中的一個ViewGroup而且隨後它會做爲使用者提供的控件樹的父控件
*/
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 {
/* 2. 將開發者給定的layout實例化爲一顆控件樹,而後做爲子控件保存在mContentParent中。完成這個操做後,PhoneWindow便完成了整棵控件樹的建立*/
mLayoutInflater.inflate(layoutResID, mContentParent);
}
/*3.Callback接口被Window用來向使用者通知其內部所發生的變化。此時通知使用者Window的控件樹發生了改變。做爲Window的使用者,Activity類實現了這一接口,所以開發者能夠經過重寫Activity的這一方法從而對這些變化作出反應*/
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
/*4. 這個與咱們前面requestFeature中的分析是一致的*/
mContentParentExplicitlySet = true;
}
複製代碼
[PhoneWindow.installDecor()]
installDecor中的關鍵代碼以下
private void installDecor() {
//建立根控件DecorView
mDecor = generateDecor(-1);
.....
//建立外觀模板
mContentParent = generateLayout(mDecor);
.....
}
複製代碼
generateDecor方法中進行外觀模板的建立。外觀模板的建立是一個繁瑣的過程,由於它不只受前面所述窗口特性的影響,並且還須要考慮窗口的樣式設置、Android的版本等。
TypedArray a = getWindowStyle();
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
...............
................
if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {
if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
a.getValue(R.styleable.Window_windowFixedHeightMajor,
mFixedHeightMajor);
}
if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {
if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
a.getValue(R.styleable.Window_windowFixedHeightMinor,
mFixedHeightMinor);
}
if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {
requestFeature(FEATURE_CONTENT_TRANSITIONS);
}
if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
}
複製代碼
final Context context = getContext();
final int targetSdk = context.getApplicationInfo().targetSdkVersion;
final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
final boolean targetHcNeedsOptions = context.getResources().getBoolean(
R.bool.target_honeycomb_needs_options_menu);
final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
} else {
setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
}
if (!mForcedStatusBarColor) {
mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
}
if (!mForcedNavigationBarColor) {
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
0x00000000);
}
WindowManager.LayoutParams params = getAttributes();
......................
......................
// The rest are only done if this window is not embedded; otherwise,
// the values are inherited from our container.
if (getContainer() == null) {
if (mBackgroundDrawable == null) {
if (mBackgroundResource == 0) {
mBackgroundResource = a.getResourceId(
R.styleable.Window_windowBackground, 0);
}
if (mFrameResource == 0) {
mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
}
mBackgroundFallbackResource = a.getResourceId(
R.styleable.Window_windowBackgroundFallback, 0);
if (false) {
System.out.println("Background: "
+ Integer.toHexString(mBackgroundResource) + " Frame: "
+ Integer.toHexString(mFrameResource));
}
}
if (mLoadElevation) {
mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
}
mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
}
複製代碼
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;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
複製代碼
mLayoutInflater.inflate(layoutResID, mContentParent);
複製代碼
開發者所設置的layout又經過上面的方式唄添加到mContentParent中。這樣一來,整棵DecorView樹就被完成的建立出來了。 這個過程對比「Android Framework概述」一文,完成了以下圖所示關係的創建:
經過前面的分析,此時PhoneWindow和完整的控件樹DecorView都已經準備好了,如今咱們須要將DecorView經過WMS顯示出來(這一過程所須要使用的知識點請參考「Window與WindowMananger」一文)。 在「淺析Activity啓動過程」咱們說過,ActivityThread.handleResumeActivity完成Activity窗口的顯示: [ActivityThread.handleResumeActivity]
final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
.....
/*1. 調用onResume回調
*/
r = performResumeActivity(token, clearHide, reason);
.....
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
/*2. 設置decor爲INVISIBLE
*/
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 (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
/*3. 調用WindowManager的addview方法顯示decor,WindowManager的原理參考「Window與WindowMananger」一文
*/
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
}
.....
if (r.activity.mVisibleFromClient) {
/*4. 設置decor爲VISIBLE
*/
r.activity.makeVisible();
}
}
複製代碼
可見,當Activity.onResume被調用時,Activity的窗口其實還沒有顯示,也就是說Activity可見發生在onResume()以後。 第二步中DecorView被設置爲invisible(不知道是否是爲了防止更新閃爍的問題),以後多是要把它設置回來,就是在makevisible方法中
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
複製代碼
能夠看到若是當前DecorView還未添加到WindwManager的話,則從新添加,最後設置爲VISIBLE。 而咱們日常在activity中使用setVisibility,也就是在設置DecorView是VISIBLE仍是INVASIBLE。
public void setVisible(boolean visible) {
if (mVisibleFromClient != visible) {
mVisibleFromClient = visible;
if (mVisibleFromServer) {
if (visible) makeVisible();
else mDecor.setVisibility(View.INVISIBLE);
}
}
}
複製代碼
至此,Activity被啓動起來,視圖(DecorView)也被建立(Window)管理(WindowManager)起來了。