在Android中Window表示一个窗口,像我们日常使用的Activity就会有一个Window实例,用它来管理Activity中的视图呈现。Window其实是一个抽象类,它的具体实现类是PhoneWindow,每一个视图View的呈现都需要Window来实现,也可以说View是Window的实体存在
既然Window涉及到视图View的呈现,那么里面必然会有View的绘制行为,我们知道View绘制是通过ViewRootImpl来完成的,所以Window也必然会有对应的ViewRootImpl。Window管理着View的添加、删除、更新等操作,而ViewRootImpl则专门用来绘制Window管理的View
View在Window上的添加、删除、更新操作是通过WindowManager来实现,WindowManager是外界访问Window的入口,但是对Window的操作最终又会在WindowManagerService中实现,因为WindowManagerService在Android中是一个独立的进程,所以WindowManager和WindowManagerService的交互是一个IPC过程。下面先来分析一下Window的添加过程
Window的添加过程
Window添加视图的过程通过WindowManager的addView来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl,来看它的接口实现
@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并没有真正实现Window的三大操作,而是交给了WindowManagerGlobal来实现
//WindowManagerGlobal的addView方法
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//检查参数的合法性
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
****************省略代码*********************
//创建一个ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//保存相关参数
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//利用ViewRootImpl的setView方法来完成view的绘制
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
mViews存储的是所有添加到Window的View,mRoots存储的是所有View对应的Viewrootimpl,mParams存储的是View的布局参数,而mDyingViews则存储那些正在被删除的View
通过Viewrootimpl的setView方法进入View的绘制阶段,来看看
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
...
try {
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
在setView方法中,通过requestLayout完成View的异步刷新,接着通过WindowSession来最终来完成Window的添加任务
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//首先判断线程
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
绘制View首先会判断线程是否正确,这个就是我们平时在子线程中更新UI的时候会抛出的异常的原因了,然后执行scheduleTraversals
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//异步执行
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
异步执行mTraversalRunnable接口
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
//执行
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals会执行真正的View绘制,如下
private void performTraversals() {
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
performDraw();
}
......
}
从上面得知,Window在添加一个视图之前会先对View进行绘制,在完成绘制以后将通过WindowSession来实现跨进程的窗口添加工作,mWindowSession的一个Binder对象,真正的实现是Session,这也是Window添加过程中的一个IPC调用
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
Session内部将调用WindowManagerService的addWindow方法,如此一来,Window的添加任务就交给了WindowManagerService了,具体它在里面是怎么实现的就不做深入
通过上面我们知道了Window的添加过程,至于它的删除和更新操作由于篇幅有限就不作分析了。我们知道,Android中有视图的组件有Activity、Dialog、Toast等,可以说有视图的地方就有Window,下面来分析一下Window在Activity中是如何创建的
Activity中Window的创建和添加
Activity的启动过程比较复杂,下面直接从ActivityThread的handleLaunchActivity开始分析Activity的创建
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// Initialize before creating the activity
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
...
}
在初始化了WindowManagerGlobal后,开始调用performLaunchActivity来创建Activity的实例
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//利用类加载器创建activity实例对象
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
}
try {
...
appContext.setOuterContext(activity);
//调用attach方法
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()) {
//调用oncreate方法
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
//调用oncreate方法
mInstrumentation.callActivityOnCreate(activity, r.state);
}
.....
if (!r.activity.mFinished) {
//调用onstart方法
activity.performStart();
r.stopped = false;
}
//调用 activity的 OnRestoreInstanceState方法进行数据恢复
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
...
return activity;
}
通过ClassLoader来创建一个Activity对象,后面是依次执行Activity的相关生命周期方法,在onCreate方法执行之前执行attach方法,来看看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,
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);
...
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);
}
在attach方法中,创建了Window对象并设置窗口的相关回调,还设置了WindowManager,我们在Activity中通过getWindow和getWindowManager返回的实例就是在这里创建的了,到这里Window就已经创建完成。接下来将执行Activity的onCreate方法,我们知道Activity的视图也是在onCreate方法中通过setContentView来提供,来看setContentView方法
public void setContentView(View v) {
//确保DecorView的创建
ensureSubDecor();
// 获取放置内容的ViewGroup
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
// 将内容view添加上去
contentParent.addView(v);
//通知activity视图已经发生改变
mOriginalWindowCallback.onContentChanged();
}
首先确保DecorView的创建,DecorView是Activity视图的根视图,其中Activity视图的标题和内容视图都添加到DecorView中,它们的关系如下
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
*******省略代码*******
}
}
如果DecorView没有初始化,就调用createSubDecor创建之
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
a.recycle();
throw new IllegalStateException(
"You need to use a Theme.AppCompat theme (or descendant) with this activity.");
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
}
mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
a.recycle();
// Now let's make sure that the Window has installed its decor by retrieving it
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
if (!mWindowNoTitle) {
if (mIsFloating) {
// If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_dialog_title_material, null);
// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
/**
* This needs some explanation. As we can not use the android:theme attribute
* pre-L, we emulate it by manually creating a LayoutInflater using a
* ContextThemeWrapper pointing to actionBarTheme.
*/
TypedValue outValue = new TypedValue();
mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
Context themedContext;
if (outValue.resourceId != 0) {
themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
} else {
themedContext = mContext;
}
// Now inflate the view using the themed context and set it as the content view
subDecor = (ViewGroup) LayoutInflater.from(themedContext)
.inflate(R.layout.abc_screen_toolbar, null);
mDecorContentParent = (DecorContentParent) subDecor
.findViewById(R.id.decor_content_parent);
mDecorContentParent.setWindowCallback(getWindowCallback());
/**
* Propagate features to DecorContentParent
*/
if (mOverlayActionBar) {
mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
}
if (mFeatureProgress) {
mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
}
if (mFeatureIndeterminateProgress) {
mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
}
}
} else {
if (mOverlayActionMode) {
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_screen_simple_overlay_action_mode, null);
} else {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}
if (Build.VERSION.SDK_INT >= 21) {
// If we're running on L or above, we can rely on ViewCompat's
// setOnApplyWindowInsetsListener
ViewCompat.setOnApplyWindowInsetsListener(subDecor,
new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
final int top = insets.getSystemWindowInsetTop();
final int newTop = updateStatusGuard(top);
if (top != newTop) {
insets = insets.replaceSystemWindowInsets(
insets.getSystemWindowInsetLeft(),
newTop,
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
}
// Now apply the insets on our view
return ViewCompat.onApplyWindowInsets(v, insets);
}
});
} else {
// Else, we need to use our own FitWindowsViewGroup handling
((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
new FitWindowsViewGroup.OnFitSystemWindowsListener() {
@Override
public void onFitSystemWindows(Rect insets) {
insets.top = updateStatusGuard(insets.top);
}
});
}
}
if (subDecor == null) {
throw new IllegalArgumentException(
"AppCompat does not support the current theme features: { "
+ "windowActionBar: " + mHasActionBar
+ ", windowActionBarOverlay: "+ mOverlayActionBar
+ ", android:windowIsFloating: " + mIsFloating
+ ", windowActionModeOverlay: " + mOverlayActionMode
+ ", windowNoTitle: " + mWindowNoTitle
+ " }");
}
if (mDecorContentParent == null) {
mTitleView = (TextView) subDecor.findViewById(R.id.title);
}
// Make the decor optionally fit system windows, like the window's decor
ViewUtils.makeOptionalFitsSystemWindows(subDecor);
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content);
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to
// migrate them to our content view
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
// Change our content FrameLayout to use the android.R.id.content id.
// Useful for fragments.
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content);
// The decorContent may have a foreground drawable set (windowContentOverlay).
// Remove this as we handle it ourselves
if (windowContentView instanceof FrameLayout) {
((FrameLayout) windowContentView).setForeground(null);
}
}
// Now set the Window's content view with the decor
mWindow.setContentView(subDecor);
contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
@Override
public void onAttachedFromWindow() {}
@Override
public void onDetachedFromWindow() {
dismissPopups();
}
});
return subDecor;
}
这里代码比较多,其实就是根据不同的Activity主题加载不同的布局来创建DecorView。因为布局的加载设置是一个异步的过程,从上面的setContentView方法中可以得知,创建好Activity的视图之后,会回调onContentChanged方法,此时代表可以显示视图给前台看了,然后调用makeVisible方法
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
如果Window还没有添加的话,则添加Window,此Window的视图是DecorView,然后设置DecorView为可见,这就完成了Activity的Winow的创建和添加