Android 中的 Window,是一個比較抽象的概念,總有一種說不清道不明的感覺胧卤,但是又非常重要枝誊。
Activity 是四大組件之一侧啼,可以說是我們學(xué)習(xí) Android 接觸的第一個知識點皮壁,它也承擔(dān)著 UI 和交互的控制邏輯,是我們開發(fā)應(yīng)用經(jīng)常用到的虑瀑;View 也是我們經(jīng)常用到的,從 XML 布局編寫到 Activity & Fragment 中 UI 展示變化等痛侍,我們也很熟悉主届;相比之下君丁,Window 我們在平時的開發(fā)中用到的不多橡庞,相對來說比較陌生印蔗。這篇文章就從熟悉的 Activity 入手毙死,理解 Window。
一. Window 的創(chuàng)建 & 添加
Window 和 Activity 有著緊密的聯(lián)系喻鳄,一個 Activity 擁有至少一個 Window 對象扼倘,在創(chuàng)建 Activity 的時候,也會創(chuàng)建一個 Window 對象除呵,并且 Activity 會實現(xiàn) Window 的一些回調(diào)接口。
在上篇文章 深入理解 Activity 的生命周期 中颜曾,我們從源碼的角度分析了 Activity 的生命周期纠拔,在 Activity 對象創(chuàng)建完成之后,調(diào)用的 Activity 第一個方法并不是 onCreate
相關(guān)的方法泛豪,而是 attach
方法稠诲,在 attach
方法中我們創(chuàng)建了 Activity 對應(yīng)的 Window 對象,代碼如下
public class Activity extends ContextThemeWrapper implements ...... {
private Window mWindow;
private WindowManager mWindowManager;
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*/);
// 代碼 1
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
// 代碼 2
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());
}
}
// 代碼 3
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);
}
}
在代碼 1 處創(chuàng)建了 Window 對象诡曙,Window 是一個抽象類臀叙,其具體的實現(xiàn)類是 PhoneWindow 類
-
在代碼 2 處設(shè)置了 Window 的 Callback 回調(diào),用戶的觸摸 & 鍵盤等輸入事件就是通過此接口回調(diào)到 Activity 中的价卤,
Window#Callback
回調(diào)源碼如下public abstract class Window { /** * API from a Window back to its caller. This allows the client to * intercept key dispatching, panels and menus, etc. */ public interface Callback { /** * Called to process key events. At the very least your * implementation must call * {@link android.view.Window#superDispatchKeyEvent} to do the * standard key processing. * * @param event The key event. * * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event); /** * Called to process a key shortcut event. * At the very least your implementation must call * {@link android.view.Window#superDispatchKeyShortcutEvent} to do the * standard key shortcut processing. * * @param event The key shortcut event. * @return True if this event was consumed. */ public boolean dispatchKeyShortcutEvent(KeyEvent event); /** * Called to process touch screen events. At the very least your * implementation must call * {@link android.view.Window#superDispatchTouchEvent} to do the * standard touch screen processing. * * @param event The touch screen event. * * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent event); // ...... } }
-
在代碼 3 處為 Window 對象設(shè)置了 WindowManager 對象劝萤, WindowManager 是一個 Interface,其實現(xiàn)類是 WindowManagerImpl 類慎璧,我們來看下床嫌,最后通過 WindowManagerImpl 的 createLocalWindowManager 方法創(chuàng)建了一個新的 WindowManagerImpl 方法,并且將 Window 自己傳入 WindowManagerImpl 中胸私,在 WindowManagerImpl 的 mParentWindow 持有 Window 的對象
public abstract class Window { /** * Set the window manager for use by this Window to, for example, * display panels. This is <em>not</em> used for displaying the * Window itself -- that must be done by the client. * * @param wm The window manager for adding new windows. */ public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { mAppToken = appToken; mAppName = appName; mHardwareAccelerated = hardwareAccelerated || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false); if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); } }
public final class WindowManagerImpl implements WindowManager { private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); private final Context mContext; private final Window mParentWindow; private IBinder mDefaultToken; public WindowManagerImpl(Context context) { this(context, null); } private WindowManagerImpl(Context context, Window parentWindow) { mContext = context; mParentWindow = parentWindow; } public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); } public WindowManagerImpl createPresentationWindowManager(Context displayContext) { return new WindowManagerImpl(displayContext, mParentWindow); } // ...... @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); } @Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.updateViewLayout(view, params); } // ...... @Override public void removeView(View view) { mGlobal.removeView(view, false); } @Override public void removeViewImmediate(View view) { mGlobal.removeView(view, true); } // ...... }
-
在 WindowManagerImpl 類中持有一個 WindowManagerGlobal 對象厌处,WindowManagerGlobal 在整個應(yīng)用進(jìn)程中是以單例存在的,也就是說在整個應(yīng)用進(jìn)程中岁疼,所有的 Window 和 WindowManagerImpl 對象都對應(yīng)著這個 WindowManagerGlobal 對象阔涉,其源碼如下,在其中有幾個變量非常重要
-
ArrayList<View> mViews
:存儲所有 Window 所對應(yīng)的 View -
ArrayList<ViewRootImpl>
:存儲所有 Window 所對應(yīng)的 ViewRootImpl -
ArrayList<WindowManager.LayoutParams>
:存儲所有 Window 所對應(yīng)的 WindowManager.LayoutParams -
ArraySet<View>
:存儲所有即將被移除的 View
public final class WindowManagerGlobal { // WindowManagerGlobal 單例對象 private static WindowManagerGlobal sDefaultWindowManager; // IWindowManager & IWindowSession 用于和 WindowManagerService 通過 Binder 通信 private static IWindowManager sWindowManagerService; private static IWindowSession sWindowSession; private final Object mLock = new Object(); private final ArrayList<View> mViews = new ArrayList<View>(); private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); private final ArraySet<View> mDyingViews = new ArraySet<View>(); // 私有構(gòu)造方法 private WindowManagerGlobal() { } // 初始化方法 public static void initialize() { getWindowManagerService(); } public static WindowManagerGlobal getInstance() { synchronized (WindowManagerGlobal.class) { if (sDefaultWindowManager == null) { sDefaultWindowManager = new WindowManagerGlobal(); } return sDefaultWindowManager; } } // 創(chuàng)建 IWindowManager 對象 public static IWindowManager getWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try { if (sWindowManagerService != null) { ValueAnimator.setDurationScale( sWindowManagerService.getCurrentAnimatorScale()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowManagerService; } } // 非常重要的方法,向 Window 中添加 View洒敏,在這里 Window 和 View 產(chǎn)生了關(guān)聯(lián) public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { // ...... } // 更新 Window 中的 View public void updateViewLayout(View view, ViewGroup.LayoutParams params) { // ...... } // 從 Window 中移除 View public void removeView(View view, boolean immediate) { // ...... } }
-
關(guān)于 Activity龄恋、Window、WindowManager凶伙、WindowManagerImpl郭毕、WindowManagerGlobal 這幾個類的關(guān)系可以用如下圖來表示可能更清晰一些
上面我們介紹了 Activity 中的 Window 是如何創(chuàng)建并添加,Activity 和 Window 之間的關(guān)系就算是清楚了函荣。接著我們分析 View 是如何添加到 Window 上面的显押。
二. 創(chuàng)建 PhoneView & DecorView & mContentParent
還是從熟悉的知識入手,我們在創(chuàng)建好一個 Activity 類傻挂,在 Activity 的 onCreate
方法中都會調(diào)用 setContentView
方法為 Activity 設(shè)置 View 的
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
setContentView
的源碼如下乘碑,其實是調(diào)用了 Window 的 setContentView
方法
public class Activity extends ContextThemeWrapper implements ... {
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
}
Window 是一個抽象類,其具體實現(xiàn)類是 PhoneWindow 類
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.
ViewGroup mContentParent;
@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
// 代碼 1
// 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 {
// 代碼 2
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
// 代碼 3
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
- 在代碼1 處金拒,首先會判斷
mContentParent
是否為空兽肤,若為空會調(diào)用installDecor()
方法。mContentParent
是 PhoneWindow 中非常重要的一個屬性绪抛,由其英文注釋可知:mContentParent 是 Window 放置 View 內(nèi)容的容器资铡,可能是 mDecor 本身,或者是 mDecor 的一個子 View幢码。 - 在代碼2 處笤休,會通過 mLayoutInflater 將傳入的 layoutResID 解析并放入 mContentParent 內(nèi)
- 在代碼3 處,得到設(shè)置的 Callback 回調(diào)對象症副,并調(diào)用
onContentChanged()
店雅,通知 View 內(nèi)容發(fā)生變化這兒其實就是在
Activity#attach
方法中設(shè)置的 Callback 回調(diào)對象
接下來我們看一下 installDecor()
方法,看看是如何創(chuàng)建 mDecor
和 mContentParent
對象的
private void installDecor() {
mForceDecorInstall = false;
// 代碼 1
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
// 代碼 2
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
// ......
}
}
- 在代碼 1 處通過
generateDecor(-1)
方法生成一個mDecor
的實例對象 - 在代碼 2 處通過
generateLayout(mDecor)
方法生成mContentParent
實例對象
generateDecor(int featureId)
方法源碼如下贞铣,最后一行生成了一個 DecorView 的實例對象闹啦,DecorView
就是一個 FrameLayout 的子類,這里不做過多的分析
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
}
接下來看一下 generateLayout(mDecor)
源碼咕娄,通過 mDecor
生成一個 mContentParent
對象
protected ViewGroup generateLayout(DecorView decor) {
// ...... 應(yīng)用當(dāng)前的 Theme
// 根據(jù)主題設(shè)置去選擇 layoutResource
// 這個 layoutResource 就是 DecorView 的子 View 的布局
// Inflate the window decor.
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 {
// 比較常見的是這個 layout 布局
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();
// 通過 DecorView 的 onResourcesLoaded 方法將 layoutResource 設(shè)置為當(dāng)前 DecorView 的內(nèi)容
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 通過 android.R.id.content id 得到 contentParent, 它是 setContentView 的父視圖
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks(contentParent);
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
// 設(shè)置背景和標(biāo)題
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);
mDecor.setElevation(mElevation);
mDecor.setClipToOutline(mClipToOutline);
if (mTitle != null) {
setTitle(mTitle);
}
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
上面代碼就是根據(jù)當(dāng)前的 Theme 設(shè)置 Window 的樣式亥揖,選擇合適的 mContentParent 的 layout 布局,并將 mContentParent 加載到 DecorView 中圣勒。比較常見的 mContentParent 的 layout 布局是 R.layout.screen_title_icons
,其代碼位于 /frameworks/base/core/res/res/layout/screen_title.xml
摧扇,內(nèi)容如下圣贸,其中有個 android:id/title
的 title 和 android:id/content
容器,我們通過 setContentView 設(shè)置的 View 的 parentView 就是這個 android:id/content
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- Popout bar for action modes -->
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
在 generateLayout(DecorView decor)
方法中得到了 DecorView 的直接子 View 的 layout 布局 id扛稽,然后通過 DecorView#onResourcesLoaded
方法將其設(shè)置為 DecorView 的直接子 View吁峻,我們來分析下 onResourcesLoaded
源碼
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
ViewGroup mContentRoot;
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
mStackId = getStackId();
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
}
mDecorCaptionView = createDecorCaptionView(inflater);
// 通過 LayoutInflater 加載 layoutResource 布局,通過 root 引用
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// 將 root View 設(shè)置為 DecorView 的第 0 個 View
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
// 將 root 賦給 mContentRoot 引用
mContentRoot = (ViewGroup) root;
initializeElevation();
}
}
看到這里,Activity用含、PhoneWindow矮慕、DecorView、mContentParent 的關(guān)系基本上就搞清楚了啄骇,如下圖所示:
到這兒其實并沒有結(jié)束痴鳄,我們上面都是在 Activity 的 create 階段分析 PhoneWindow、DecorView缸夹、mContentParent 的創(chuàng)建痪寻,并沒有分析 DecorView 是如何添加到 PhoneWindow 上的,DecorView 添加到 PhoneView 的過程在 Activity 的 resume 過程中完成
三. 向 PhoneView 添加 DecorView
在 ActivityThread 的 handleLaunchActivity
中執(zhí)行完 create 階段的方法之后虽惭,立即開始調(diào)用 handleResumeActivity
方法橡类,開始執(zhí)行 resume 過程,handleResumeActivity
方法如下
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// 代碼 1芽唇,調(diào)用 performResumeActivity 間接地調(diào)用 Activity 的 onResume 方法
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(
TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ ", finished: " + a.mFinished);
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// 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 = ActivityManager.getService().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
// 代碼 2顾画,在添加 DecorView 到 Window 之前,將 DecorView 設(shè)置為不可見的
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
// 代碼 3匆笤,此處很重要研侣,將 WindowManager.LayoutParams.type 屬性設(shè)置為 TYPE_BASE_APPLICATION = 1,代表此 Window 是一個應(yīng)用級的窗口疚膊,在所有窗口的最低下
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;
// 代碼 4义辕,在這兒調(diào)用 WindowManager 的 addView 方法,將 DecorView 添加到 Window 中
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 the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r, false /* force */);
// ......
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManager.getService()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
在代碼 4 處寓盗,我們可以清楚的看到調(diào)用了 WindowManager 的 addView 方法灌砖,那我們接著分析,因為 WindowManager 是一個 Interface傀蚌,其實現(xiàn)類是 WindowManagerImpl基显,我們來到 WindowManagerImpl 方法中,如下:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
// ......
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
// ......
}
其實是調(diào)用了 WindowManagerGlobal 的 addView 方法善炫,
public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ......
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// ......
// 代碼 1撩幽,從 mViews 中找到此 DecorView 的 index 的索引
int index = findViewLocked(view, false);
if (index >= 0) {
// 若在 mDyingViews 中存在此 DecorView,則直接調(diào)用對應(yīng)的 ViewRootImpl.doDei() 方法
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// 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.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
// 代碼 2箩艺,新建一個 ViewRootImpl 對象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
// 代碼 3窜醉,將 DecorView、ViewRootImple艺谆、WindowManager.LayoutParams 添加到對應(yīng)的 List 中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// 調(diào)用 ViewRootImpl 的 setView 方法榨惰,開始執(zhí)行 DecorView 的繪制過程
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
private int findViewLocked(View view, boolean required) {
final int index = mViews.indexOf(view);
if (required && index < 0) {
throw new IllegalArgumentException("View=" + view + " not attached to window manager");
}
return index;
}
}