Android 中所有的视图都是通过 Window 来呈现的,不管是 Activity、Dialog 还是 Toast,它们的视图实际上都是附加在 Window 上的,因此 Window 实际是 View 的直接管理者。
Window 和 WindowManager
使用示例: 将一个 Button 添加到屏幕坐标为 (100,300) 的位置。
Button button = new Button(this);
button.setText("button");
WindowManager.LayoutParams layoutParams =
new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.START | Gravity.TOP;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
layoutParams.format = PixelFormat.TRANSPARENT;
layoutParams.x = 100;
layoutParams.y = 300;
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
windowManager.addView(button, layoutParams);
复制代码
WindowManager.LayoutParams 的 Api 文档:https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html
Flag
表示 Window 的属性,控制 Window 的显示特征。
常用的几个 FLAG:
- FLAG_NOT_FOCUSABLE:
不会接收事件的焦点,此标记会同时启用 FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的 Window。
- FLAG_NOT_TOUCH_MODAL
在此模式下,系统会将当前 Window 区域以外的单击事件传递给底层的 Window,当前 Window 区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标记,否则其他 Window 将无法收到单击事件。
- FLAG_SHOW_WHEN_LOCKED
开启此模式可以让 Window 显示在锁屏的界面。
Type
表示 Window 的类型。
- 应用 Window
对应着一个 Activity
- 子 Window
不能单独存在,需要附属在特定的父 Window 中。例如:Dialog
- 系统 Window
需要声明权限才能创建,例如:Toast 和系统状态栏
z-ordered
Window 是分层的,每个 Window 都有对应的 z-ordered。层级大的在上面。
- 应用 Window 的层级为 1~99
- 子 Window 的层级为 100~1999
- 系统 Window 的层级为 2000~2999
WindowManager
继承自 ViewManager
public interface WindowManager extends ViewManager
复制代码
android.view.ViewManager
public interface ViewManager
{
/**
* Assign the passed LayoutParams to the passed View and add the view to the window.
* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
* secondary {@link Display} and the specified display can't be found
* (see {@link android.app.Presentation}).
* @param view The view to be added to this window.
* @param params The LayoutParams to assign to view.
*/
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
复制代码
Window 的内部机制
Window 是个抽象类,每一个 Window 都对应一个 View 和一个 ViewRootImpl,window 并不是实际存在的,是以 View 的形式存在。
通过 WindowManager 来访问 Window。
WindowManagerImp 内部通过 WindowManagerGlobal 来添加,更新,移除 View。这是典型的桥接模式。
WindowManagerGlobal 的列表集合
/** 存储 View */
private final ArrayList<View> mViews = new ArrayList<View>();
/** 存储 ViewRootImpl */
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
/** 存储 LayoutParams */
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
/** 存储正在被删除的 View */
private final ArraySet<View> mDyingViews = new ArraySet<View>();
复制代码
添加
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");
}
/* 检查是否是子 window,是的话就调整下参数 */
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
// 如果没有父类window,按应用程序的硬件加速设置这个 View 的硬件加速
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
/* 观察系统属性的变化 */
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
/* 检查是否有正在的删除的 View */
if (index >= 0) {
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);
}
}
}
// 将对象存储到列表中
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 更新界面并完成 Window 的添加过程。
内部通过 requestLayout 绘制界面
*/
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
复制代码
ViewRootImpl#setView 最终通过 IWindowSession 这个 Binder 对象来完成添加过程。实现类是 Session。这个过程是一个 IPC 调用。
android.view.ViewRootImpl.java # setView 方法片段
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
复制代码
在 mWindowSession 内部会通过 WindowManagerService 来实现 Window 的添加。
IWindowSession.Stub#addToDisplay
public int addToDisplay(IWindow window,
int seq,
WindowManager.LayoutParams attrs,
int viewVisibility,
int displayId,
Rect outContentInsets,
InputChannel outInputChannel) {
return mService.addWindow(this,window,seq,attrs,viewVisibility,displayId, outContentInsets,outInputChannel);
}
复制代码
这样,Window 的添加请求最终交给了 WindowManagerService 去处理。
Window 的删除过程
和添加一样,先通过 WindowManagerImpl, 再通过 WindowManagerGlobal 来实现的。
android.view.WindowManagerGlobal#removeView
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
// 查找待删除的 View 的索引。
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
// 删除
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
复制代码
android.view.WindowManagerGlobal#removeViewLocked
private void removeViewLocked(int index, boolean immediate) {
// 通过 ViewRootImpl 来完成删除操作
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred =
// 执行 die,完成具体的删除操作
root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
复制代码
android.view.ViewRootImpl#die
// immediate 为true代表同步删除,false 代表异步执行。一般不需要同步以免发生错误。
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
// 异步删除时只是发送了一个 DIE 消息,Handler 执行 doDie()
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
复制代码
die 方法中如果是异步删除,就发送一个 MSG, ViewRootImpl 中的 Handler 会执行 doDie()。如果是同步,直接执行 diDie()。
android.view.ViewRootImpl#doDie
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
// 真正删除 View 的操作在这个方法中
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
// If layout params have been changed, first give them
// to the window manager to make sure it has the correct
// animation info.
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);
}
} catch (RemoteException e) {
}
}
mSurface.release();
}
}
mAdded = false;
}
// 刷新数据,包括 mRoots、mParams、以及 mDyingViews. 需要将当前 Window 所关联的这三类对象从列表中删除。
WindowManagerGlobal.getInstance().doRemoveView(this);
}
复制代码
主要的删除操作由这个方法完成: android.view.ViewRootImpl#dispatchDetachedFromWindow
void dispatchDetachedFromWindow() {
// 垃圾回收相关的工作。移除回调,清除数据和消息
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
// 当 View 从 Window 中移除时,这个方法就会被调用。可以在这个
// 方法中做一些资源回收的工作,比如终止动画、停止线程。
mView.dispatchDetachedFromWindow();
}
mAccessibilityInteractionConnectionManager.ensureNoConnection();
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager);
mAccessibilityManager.removeHighTextContrastStateChangeListener(
mHighContrastTextManager);
removeSendWindowContentChangedCallback();
destroyHardwareRenderer();
setAccessibilityFocus(null, null);
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
mSurface.release();
if (mInputQueueCallback != null && mInputQueue != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueue.dispose();
mInputQueueCallback = null;
mInputQueue = null;
}
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
}
// 通过 Session 的 remove 方法删除。这同样是是一个 IPC 过程,最终调用 WindowManagerService 的 RemoveWindow
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
// Dispose the input channel after removing the window so the Window Manager
// doesn't interpret the input channel being closed as an abnormal termination.
if (mInputChannel != null) {
mInputChannel.dispose();
mInputChannel = null;
}
mDisplayManager.unregisterDisplayListener(mDisplayListener);
unscheduleTraversals();
}
复制代码
Window 的更新过程
android.view.WindowManagerGlobal#updateViewLayout
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
// 检查异常
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
// 给View设置新的params
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
// 将老的 params 删除,添加新的 params。
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
// 更新 ViewRootImpl 中的 LayoutParams
root.setLayoutParams(wparams, false);
}
}
复制代码
在 ViewRootImpl 中会通过 scheduleTraversals 方法对 View 重新布局。
还会通过 WindowSession 来更新 Window 的视图,这个过程最终是由 WindowMangerService 的 relayoutWindow 来具体实现的,同样是个 IPC 过程。
Window 的创建过程
View 不能单独存在,必须依附在 Window 这个抽象的概念上。
有 View 的地方就有 Window。
Activity、Dialog、Toast。
Activity 的创建过程
Activity 的启动过程很复杂,最终会由 ActivityThread 中的 performLaunchActivity() 来完成整个启动过程。
android.app.ActivityThread#performLaunchActivity()
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
// 调用 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);
}
复制代码
在 Acrivity 的 attach
方法中,系统会创建 Activity 所属的 Window 对象并为其设置回调接口。Window 对象的创建是通过 PolicyManager 的 makeNewWindow
方法实现的。(新版本直接创建了 PhoneWindow)
由于 Activity 实现了 Window 的 callback 接口,因此当 Window 接收到外界状态的改变时就会回调 Activity 的方法。
mWindow = new PhoneWindow(this, window);
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);
}
复制代码
Activity 的视图是怎么加载在 Widnow 上的?
直接看 Activity 的 setContentView
的实现。
android.app.Activity#setContentView
public void setContentView(@LayoutRes int layoutResID) {
// 交给了 window 去实现
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
复制代码
Window 的具体实现是 PhoneWindow 类。
com.android.internal.policy.PhoneWidnow#setContentView
@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
// before this happens.
if (mContentParent == null) {
// 安装 Decor
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 {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
复制代码
- 如果没有 DecorView 就创建它。
DecorView 是一个 FrameLayout,是 Activity 中的顶级 View,一般来说内部包含标题栏和内部栏,这个会随着主题而变化。内容栏的 id 是 android.R.id.content
。
com.android.internal.policy.PhoneWindow#installDecor
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 直接创建,此时还是一个空白的 FrameLayout
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
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);
if (decorContentParent != null) {
mDecorContentParent = decorContentParent;
// 设置一堆东西
mDecorContentParent.setUiOptions(mUiOptions);
// ......
} else {
mTitleView = (TextView) findViewById(R.id.title);
if (mTitleView != null) {
// 设置title,省略部分代码
}
}
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
// Only inflate or create a new TransitionManager if the caller hasn't
// already set a custom one.
// 省略部分代码
}
}
复制代码
generateLayout()
初始化 mContentParent。
com.android.internal.policy.PhoneWindow#generateLayout
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
// 省略部分代码...
// 从这里开始。Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// 省略一堆判断...
mDecor.startChanging();
// 加载布局
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 这个 id 所对应的 ViewGroup 就是 mContentParent。
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
// 省略设置背景标题等...
mDecor.finishChanging();
return contentParent;
}
复制代码
上述方法中的 ID_ANDROID_CONTENT
的定义如下。
android.view.Window#ID_ANDROID_CONTENT
/**
* The ID that the main layout in the XML layout file should have.
*/
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
复制代码
- 将 View 添加到 DecoreView 的 mContentParent 中。
mLayoutInflater.inflate(layoutResID, mContentParent);
复制代码
将 Activity 的视图直接添加到 DecorView 的 mContentParent 中。因此设置布局的方法叫 setContentView
- 回调 Activity 的 onContentChanged 方法通知 Activity 视图改变
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
复制代码
Activity 实现了 Window 的 Callback 接口。Activity 的布局文件被添加到 DecorView 的 mContentParent 中,于是需要通知 Activity,使其可以做相应的处理。
Activity 中的 onContentChanged 是空实现。
public void onContentChanged() {
}
复制代码
显示
经过上述步骤,DecorView 已经被创建并初始化完毕。Activity 的布局文件已经被添加到 DecorView 的 mContentParent 中,但是这时 DecorView 还没有被 WindowManager 正式添加到 Widnow 中。
Window 更多表示一种抽象的功能集合。
虽然在 Activity 的 attach 方法中 Window 已经被创建,此时由于 DecorView 没有被 WindowManager 识别,所以这时的 Window 无法提供具体功能,他无法接收外界的输入信息。
在 ActivityThread 的 handleResumeActivity()
中,首先调用 Activity 的 onResume()
,接着调用 Activity 的 makeVisible()
。在这个方法中,DecorView 真正完成了添加和显示这两个过程,此时 Activity 的视图才能被用户真正看到。
android.app.ActivityThread#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;
// 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 = ActivityManagerNative.getDefault().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();
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 && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, 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 */);
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
+ r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
+ isForward);
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(
TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
复制代码
Activity#makeVisible
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
复制代码
Dialog 的 Window 创建过程
#1. 创建 Winow
同样是直接 new PhoneWindow
创建 Window,和 Activity 一致。
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
// ...
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
复制代码
#2. 初始化 DecorView 并将 Dialog 的视图添加到 DecorView 中
和 Activity 一样。
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
复制代码
#3. 将 DecorView 添加到 Window 中并显示
public void show() {
// ...
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
}
复制代码
删除的时候通过 mWindowManager.removeViewImmediate(mDecor);
移除 DecorView。
void dismissDialog() {
if (mDecor == null || !mShowing) {
return;
}
if (mWindow.isDestroyed()) {
Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
return;
}
try {
mWindowManager.removeViewImmediate(mDecor);
} finally {
if (mActionMode != null) {
mActionMode.finish();
}
mDecor = null;
mWindow.closeAllPanels();
onStop();
mShowing = false;
sendDismissMessage();
}
}
复制代码
Dialog 必须采用 Activity 的 Context,如若使用 Apllication 的 Context,就会报错没有 token。
token 一般只有 Activity 才有。
系统 Window 不需要 token 就可以显示。
Caused by: android.view.WindowManager$BadToken-
Exception: Unable to add window --token null is not for an application
复制代码
Toast 的 Window 创建过程
Toast 也是基于 Window 来实现的。
内部有 2 类 IPC 过程:
- Toast 访问 NotificationManagerService
- NotificationManagerService 回调 Toast 里的 TN 接口
Toast 属于系统 Window。