个人博客:

http://www.milovetingting.cn

理解Window和WindowManager

Window表示一个窗口的概念,是一个抽象类,具体实现是PhoneWindow,可以通过WindowManager创建一个Window。WindowManager是外界访问Window的入口,Window具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。

Window和WindowManager

WindowManager.LayoutParams

关注flags和type两个参数:

Flags参数表示Window的属性,可以控制Window的显示特性。

** FLAG_NOT_FOCUSABLE **

表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window。

** FLAG_NOT_TOUCH_MODAL **

系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件自己处理。

** FLAG_SHOW_WHEN_LOCKED **

让Window显示在锁屏的界面上。

Type参数表示Window类型

Window有三种类型:应用Window、子Window、系统Window。应用Window对应一个Activity。子Window不能单独存在,需要附属在特定的父Window中,如Dialog就是子Window。系统Window需要声明权限才能创建,如Toast和系统状态栏就是系统Window。

Window是分层的,每个Window都有对应的z-ordered,层级大的覆盖在层级小的Window上。应用Window的层级范围是1-99,子Window的层级范围是1000-1999,系统Window层级范围是2000-2999。

WindowManager常用的三个方法:添加View、更新View和删除View。这是从ViewManager实现过来的。

public interface ViewManager
{
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通过ViewRootImpl来建立联系。

Window的添加过程

Window的添加过程需要通过WindowManager的addView来实现。WindowManager是一个接口,它的实现类是WindowManagerImpl。

public void addView(View view, LayoutParams params) {
this.applyDefaultToken(params);
this.mGlobal.addView(view, params, this.mContext.getDisplay(), this.mParentWindow);
}

WindowManagerImpl并没有直接实现addView,而是通过内部的WindowManagerGlobal实现的。

public void addView(View view, android.view.ViewGroup.LayoutParams params, Display display, Window parentWindow) {
if (view == null) {
//检查view
throw new IllegalArgumentException("view must not be null");
} else if (display == null) {
//检查display
throw new IllegalArgumentException("display must not be null");
} else if (!(params instanceof LayoutParams)) {
//检查params
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
} else {
LayoutParams wparams = (LayoutParams)params;
if (parentWindow != null) {
//调整子窗口的布局参数
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
Context context = view.getContext();
if (context != null && (context.getApplicationInfo().flags & 536870912) != 0) {
wparams.flags |= 16777216;
}
} View panelParentView = null;
int index;
ViewRootImpl root;
synchronized(this.mLock) {
if (this.mSystemPropertyUpdater == null) {
this.mSystemPropertyUpdater = new Runnable() {
public void run() {
synchronized(WindowManagerGlobal.this.mLock) {
for(int i = WindowManagerGlobal.this.mRoots.size() - 1; i >= 0; --i) {
((ViewRootImpl)WindowManagerGlobal.this.mRoots.get(i)).loadSystemProperties();
} }
}
};
SystemProperties.addChangeCallback(this.mSystemPropertyUpdater);
} int index = this.findViewLocked(view, false);
if (index >= 0) {
if (!this.mDyingViews.contains(view)) {
//不允许重复添加窗口
throw new IllegalStateException("View " + view + " has already been added to the window manager.");
} ((ViewRootImpl)this.mRoots.get(index)).doDie();
} if (wparams.type >= 1000 && wparams.type <= 1999) {
index = this.mViews.size(); for(int i = 0; i < index; ++i) {
if (((ViewRootImpl)this.mRoots.get(i)).mWindow.asBinder() == wparams.token) {
panelParentView = (View)this.mViews.get(i);
}
}
} //创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
//设置LayoutParams
view.setLayoutParams(wparams);
this.mViews.add(view);
this.mRoots.add(root);
this.mParams.add(wparams);
} try {
//ViewRootImpl添加view
root.setView(view, wparams, panelParentView);
} catch (RuntimeException var15) {
synchronized(this.mLock) {
index = this.findViewLocked(view, false);
if (index >= 0) {
this.removeViewLocked(index, true);
}
} throw var15;
}
}
}

ViewRootImpl调用setView方法

public void setView(View view, LayoutParams attrs, View panelParentView) {
synchronized(this) {
if (this.mView == null) {
//... this.mAdded = true;
//1、调用requestLayout方法
this.requestLayout(); //... int res;
try {
this.mOrigWindowType = this.mWindowAttributes.type;
this.mAttachInfo.mRecomputeGlobalAttributes = true;
this.collectViewAttributes();
//2、通过Session添加Window
res = this.mWindowSession.addToDisplay(this.mWindow, this.mSeq, this.mWindowAttributes, this.getHostVisibility(), this.mDisplay.getDisplayId(), this.mAttachInfo.mContentInsets, this.mAttachInfo.mStableInsets, this.mAttachInfo.mOutsets, this.mInputChannel);
} catch (RemoteException var20) {
this.mAdded = false;
this.mView = null;
this.mAttachInfo.mRootView = null;
this.mInputChannel = null;
this.mFallbackEventHandler.setView((View)null);
this.unscheduleTraversals();
this.setAccessibilityFocus((View)null, (AccessibilityNodeInfo)null);
throw new RuntimeException("Adding window failed", var20);
} finally {
if (restore) {
attrs.restore();
} } //...
if (res < 0) {
//添加Window失败
this.mAttachInfo.mRootView = null;
this.mAdded = false;
this.mFallbackEventHandler.setView((View)null);
this.unscheduleTraversals();
this.setAccessibilityFocus((View)null, (AccessibilityNodeInfo)null);
switch(res) {
case -10:
throw new InvalidDisplayException("Unable to add window " + this.mWindow + " -- the specified window type " + this.mWindowAttributes.type + " is not valid");
case -9:
throw new InvalidDisplayException("Unable to add window " + this.mWindow + " -- the specified display can not be found");
case -8:
throw new BadTokenException("Unable to add window " + this.mWindow + " -- permission denied for window type " + this.mWindowAttributes.type);
case -7:
throw new BadTokenException("Unable to add window " + this.mWindow + " -- another window of type " + this.mWindowAttributes.type + " already exists");
case -6:
return;
case -5:
throw new BadTokenException("Unable to add window -- window " + this.mWindow + " has already been added");
case -4:
throw new BadTokenException("Unable to add window -- app for token " + attrs.token + " is exiting");
case -3:
throw new BadTokenException("Unable to add window -- token " + attrs.token + " is not for an application");
case -2:
case -1:
if (view.getContext().getPackageName().startsWith("com.google.android.gms")) {
try {
if (AppGlobals.getPackageManager().isFirstBoot()) {
Log.d(this.mTag, "firstboot crash return");
return;
}
} catch (RemoteException var22) {
var22.printStackTrace();
return;
}
} throw new BadTokenException("Unable to add window -- token " + attrs.token + " is not valid; is your activity running?");
default:
throw new RuntimeException("Unable to add window -- unknown error code " + res);
}
} //... //将view和ViewRootImpl关联起来
view.assignParent(this); //...
} }
}

1、requestLayout方法

public void requestLayout() {
if (!this.mHandlingLayoutInLayoutRequest) {
//A检测线程
this.checkThread();
this.mLayoutRequested = true;
//B开始View的绘制
this.scheduleTraversals();
} }

A:检测线程,如果不是主线程,则报错。

 void checkThread() {
if (this.mThread != Thread.currentThread()) {
throw new ViewRootImpl.CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
}
}

B:开始View的绘制

void scheduleTraversals() {
if (!this.mTraversalScheduled) {
this.mTraversalScheduled = true;
this.mTraversalBarrier = this.mHandler.getLooper().getQueue().postSyncBarrier();
//回调mTraversalRunnable
this.mChoreographer.postCallback(2, this.mTraversalRunnable, (Object)null);
if (!this.mUnbufferedInputDispatch) {
this.scheduleConsumeBatchedInput();
} this.notifyRendererOfFramePending();
this.pokeDrawLockIfNeeded();
} }

回调mTraversalRunnable

final class TraversalRunnable implements Runnable {
TraversalRunnable() {
} public void run() {
//调用doTraversal
ViewRootImpl.this.doTraversal();
}
}

调用doTraversal

void doTraversal() {
if (this.mTraversalScheduled) {
this.mTraversalScheduled = false;
this.mHandler.getLooper().getQueue().removeSyncBarrier(this.mTraversalBarrier);
if (this.mProfile) {
Debug.startMethodTracing("ViewAncestor");
} //调用performTraversals
this.performTraversals();
if (this.mProfile) {
Debug.stopMethodTracing();
this.mProfile = false;
}
} }

调用performTraversals方法,在performTraversals方法内会调用performMeasure()、PerformLayout()、PerformDraw(),并最终会调用view的measure()、layout()、draw()方法

回到ViewRootImpl的setView方法,在2处通过WindowSession调用addToDisplay()方法,WindowSession是一个Binder对象,最终的实现为Session类。Session中的addToDisplay方法:

@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);
}

addToDisplay方法通过调用WindowManagerService的addWindow方法。具体的addWindow方法,此处不再分析。

Window的删除过程

Window的删除过程和添加过程基本一样,都是先通过WindowManagerImpl,再通过WindowManagerGlobal来实现的。

public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
} else {
synchronized(this.mLock) {
int index = this.findViewLocked(view, true);
View curView = ((ViewRootImpl)this.mRoots.get(index)).getView();
this.removeViewLocked(index, immediate);
if (curView != view) {
throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView);
}
}
}
}

removeView方法中又调用removeViewLocked方法

private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = (ViewRootImpl)this.mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(((View)this.mViews.get(index)).getWindowToken());
}
} boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent((ViewParent)null);
if (deferred) {
this.mDyingViews.add(view);
}
} }

在removeViewLocked方法里调用ViewRootImpl的die方法

boolean die(boolean immediate) {
if (immediate && !this.mIsInTraversal) {
this.doDie();
return false;
} else {
if (!this.mIsDrawing) {
this.destroyHardwareRenderer();
} else {
Log.e(this.mTag, "Attempting to destroy the window while drawing!\n window=" + this + ", title=" + this.mWindowAttributes.getTitle());
} this.mHandler.sendEmptyMessage(3);
return true;
}
}

die方法中,如果不是立即移除,则通过Handler发送一个移除消息,如果是立即移除,则调用doDie方法

void doDie() {
//检查线程
this.checkThread();
synchronized(this) {
if (this.mRemoved) {
return;
} this.mRemoved = true;
if (this.mAdded) {
this.dispatchDetachedFromWindow();
} if (this.mAdded && !this.mFirst) {
this.destroyHardwareRenderer();
if (this.mView != null) {
int viewVisibility = this.mView.getVisibility();
boolean viewVisibilityChanged = this.mViewVisibility != viewVisibility;
if (this.mWindowAttributesChanged || viewVisibilityChanged) {
try {
if ((this.relayoutWindow(this.mWindowAttributes, viewVisibility, false) & 2) != 0) {
this.mWindowSession.finishDrawing(this.mWindow);
}
} catch (RemoteException var6) {
}
} this.mSurface.release();
}
} this.mAdded = false;
} WindowManagerGlobal.getInstance().doRemoveView(this);
}

真正移除Window是在dispatchDetachedFromWindow方法中实现的

void dispatchDetachedFromWindow() {
if (this.mView != null && this.mView.mAttachInfo != null) {
this.mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
this.mView.dispatchDetachedFromWindow();
} this.mAccessibilityInteractionConnectionManager.ensureNoConnection();
this.mAccessibilityManager.removeAccessibilityStateChangeListener(this.mAccessibilityInteractionConnectionManager);
this.mAccessibilityManager.removeHighTextContrastStateChangeListener(this.mHighContrastTextManager);
this.removeSendWindowContentChangedCallback();
this.destroyHardwareRenderer();
this.setAccessibilityFocus((View)null, (AccessibilityNodeInfo)null);
this.mView.assignParent((ViewParent)null);
this.mView = null;
this.mAttachInfo.mRootView = null;
this.mSurface.release();
if (this.mInputQueueCallback != null && this.mInputQueue != null) {
this.mInputQueueCallback.onInputQueueDestroyed(this.mInputQueue);
this.mInputQueue.dispose();
this.mInputQueueCallback = null;
this.mInputQueue = null;
} if (this.mInputEventReceiver != null) {
this.mInputEventReceiver.dispose();
this.mInputEventReceiver = null;
} try {
this.mWindowSession.remove(this.mWindow);
} catch (RemoteException var2) {
} if (this.mInputChannel != null) {
this.mInputChannel.dispose();
this.mInputChannel = null;
} this.mDisplayManager.unregisterDisplayListener(this.mDisplayListener);
this.unscheduleTraversals();
}

在dispatchDetachedFromWindow中,最终通过Session来移除Window。

移除Window后,调用WindowManagerGlobal的doRemoveView方法将之前列表中的记录清除

void doRemoveView(ViewRootImpl root) {
synchronized(this.mLock) {
int index = this.mRoots.indexOf(root);
if (index >= 0) {
this.mRoots.remove(index);
this.mParams.remove(index);
View view = (View)this.mViews.remove(index);
this.mDyingViews.remove(view);
}
} if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
this.doTrimForeground();
} }

Window的更新过程

Window的更新是通过WindowManagerGlobal的updateViewLayout方法来实现的

public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
} else if (!(params instanceof LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
} else {
LayoutParams wparams = (LayoutParams)params;
view.setLayoutParams(wparams);
synchronized(this.mLock) {
int index = this.findViewLocked(view, true);
ViewRootImpl root = (ViewRootImpl)this.mRoots.get(index);
this.mParams.remove(index);
this.mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
}

Window创建过程

Activity的Window创建过程

先说明Activity的创建过程。Activity是在ActivityThread的performLaunchActivity方法中创建的。

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);
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);
}
} try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir()); if (activity != null) {
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;
//创建window
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
//将activity和window关联起来
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); //... } catch (SuperNotCalledException e) {
throw e; } catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
} return activity;
}

Activity的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*/); //创建window
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
//... //设置WindowManager
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); setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
enableAutofillCompatibilityIfNeeded();
}

window创建完成后,在Activity的setContentView方法中,将View附加到Window中的DecorView上。

public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

window的唯一实现类为PhoneWindow,因此查看PhoneWindow的setContentView方法

public void setContentView(int layoutResID) {
if (this.mContentParent == null) {
//如果没有DecorView,则创建
this.installDecor();
} else if (!this.hasFeature(12)) {
this.mContentParent.removeAllViews();
} if (this.hasFeature(12)) {
Scene newScene = Scene.getSceneForLayout(this.mContentParent, layoutResID, this.getContext());
this.transitionTo(newScene);
} else {
this.mLayoutInflater.inflate(layoutResID, this.mContentParent);
} this.mContentParent.requestApplyInsets();
android.view.Window.Callback cb = this.getCallback();
if (cb != null && !this.isDestroyed()) {
cb.onContentChanged();
} this.mContentParentExplicitlySet = true;
}

View被添加到Window中的DecorView后,Window并没有马上被添加。在Activity的onResume方法中,通过调用makeVisible方法才被添加。

void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}

Dialog的Window创建过程

1、创建Window

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == ResourceId.ID_NULL) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
} mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); //创建window
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中

public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}

3、将DecorView添加到Window

public void show() {
//... mWindowManager.addView(mDecor, l);
mShowing = true; sendShowMessage();
}

普通Dialog必须采用Activity的Context。

Toast的Window创建过程

Toast也是基于Window来实现,但由于Toast具有定时取消功能,所以系统采用了Handler。

Toast的显示

public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
} INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView; try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}

在show方法中,通过IPC调用NotificationManager的enqueueToast方法

        @Override
public void enqueueToast(String pkg, ITransientNotification callback, int duration)
{
if (DBG) {
Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
+ " duration=" + duration);
} if (pkg == null || callback == null) {
Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
return ;
}
final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
final boolean isPackageSuspended =
isPackageSuspendedForUser(pkg, Binder.getCallingUid()); if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
(!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
|| isPackageSuspended)) {
Slog.e(TAG, "Suppressing toast from package " + pkg
+ (isPackageSuspended
? " due to package suspended by administrator."
: " by user request."));
return;
} synchronized (mToastQueue) {
int callingPid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
ToastRecord record;
int index;
// All packages aside from the android package can enqueue one toast at a time
if (!isSystemToast) {
index = indexOfToastPackageLocked(pkg);
} else {
index = indexOfToastLocked(pkg, callback);
} // If the package already has a toast, we update its toast
// in the queue, we don't move it to the end of the queue.
if (index >= 0) {
record = mToastQueue.get(index);
record.update(duration);
record.update(callback);
} else {
Binder token = new Binder();
mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
record = new ToastRecord(callingPid, pkg, callback, duration, token);
mToastQueue.add(record);
index = mToastQueue.size() - 1;
}
keepProcessAliveIfNeededLocked(callingPid);
// If it's at index 0, it's the current toast. It doesn't matter if it's
// new or just been updated. Call back and tell it to show itself.
// If the callback fails, this will remove it from the list, so don't
// assume that it's valid after this.
if (index == 0) {
showNextToastLocked();
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
}

showNextToastLocked方法

    @GuardedBy("mToastQueue")
void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
record.callback.show(record.token);
scheduleTimeoutLocked(record);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to show notification " + record.callback
+ " in package " + record.pkg);
// remove it from the list and let the process die
int index = mToastQueue.indexOf(record);
if (index >= 0) {
mToastQueue.remove(index);
}
keepProcessAliveIfNeededLocked(record.pid);
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
record = null;
}
}
}
}

通过record.callback,即Toast中的TN对象,调用show方法

        @Override
public void show(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.obtainMessage(SHOW, windowToken).sendToTarget();
}

通过Handler发送消息,然后在处理消息的逻辑中调用了handleShow

public void handleShow(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ " mNextView=" + mNextView);
// If a cancel/hide is pending - no need to show - at this point
// the window token is already invalid and no need to do any work.
if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
return;
}
if (mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
Context context = mView.getContext().getApplicationContext();
String packageName = mView.getContext().getOpPackageName();
if (context == null) {
context = mView.getContext();
}
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
// We can resolve the Gravity here by using the Locale for getting
// the layout direction
final Configuration config = mView.getContext().getResources().getConfiguration();
final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = mDuration ==
Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
mParams.token = windowToken;
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
// Since the notification manager service cancels the token right
// after it notifies us to cancel the toast there is an inherent
// race and we may attempt to add a window after the token has been
// invalidated. Let us hedge against that.
try {
mWM.addView(mView, mParams);
trySendAccessibilityEvent();
} catch (WindowManager.BadTokenException e) {
/* ignore */
}
}
}

在handleShow方法中,将View添加到了window。

【Android开发艺术探索】理解Window和WindowManager的更多相关文章

  1. 《android开发艺术探索》读书笔记(八)--WindowManager

    接上篇<android开发艺术探索>读书笔记(七)--动画 No1: Window是一个抽象类,它的具体实现是PhoneWindow.创建一个Window是很简单的事,只需要通过Windo ...

  2. Android开发艺术探索第五章——理解RemoteViews

    Android开发艺术探索第五章--理解RemoteViews 这门课的重心在于RemoteViews,RemoteViews可以理解为一种远程的View,其实他和远程的Service是一样的,Rem ...

  3. Android开发艺术探索——新的征程,程序人生路漫漫!

    Android开发艺术探索--新的征程,程序人生路漫漫! 偶尔写点东西分享,但是我还是比较喜欢写笔记,看书,群英传看完了,是学到了点东西,开始看这本更加深入Android的书籍了,不知道适不适合自己, ...

  4. 《android开发艺术探索》读书笔记(四)--View工作原理

    接上篇<android开发艺术探索>读书笔记(三) No1: View的三大流程:测量流程.布局流程.绘制流程 No2: ViewRoot对应于ViewRootImpl类,它是连接Wind ...

  5. 《android开发艺术探索》读书笔记(二)--IPC机制

    接上篇<android开发艺术探索>读书笔记(一) No1: 在android中使用多进程只有一种方法,那就是给四大组件在AndroidMenifest中指定android:process ...

  6. Android开发艺术探索——第二章:IPC机制(上)

    Android开发艺术探索--第二章:IPC机制(上) 本章主要讲解Android的IPC机制,首先介绍Android中的多进程概念以及多进程开发模式中常见的注意事项,接着介绍Android中的序列化 ...

  7. Android开发艺术探索笔记——第一章:Activity的生命周期和启动模式

    Android开发艺术探索笔记--第一章:Activity的生命周期和启动模式 怀着无比崇敬的心情翻开了这本书,路漫漫其修远兮,程序人生,为自己加油! 一.序 作为这本书的第一章,主席还是把Activ ...

  8. Android开发艺术探索读书笔记——进程间通信

    1. 多进程使用场景 1) 应用某些模块由于特殊需求须要执行在单独进程中. 如消息推送,使消息推送进程与应用进程能单独存活,消息推送进程不会由于应用程序进程crash而受影响. 2) 为加大一个应用可 ...

  9. Android开发艺术探索笔记——View(二)

    Android开发艺术探索笔记--View(二) View的事件分发机制 学习资料: 1.Understanding Android Input Touch Events System Framewo ...

  10. 《Android开发艺术探索》读书笔记 (9) 第9章 四大组件的工作过程

    第9章 四大组件的工作过程 9.1 四大组件的运行状态 (1)四大组件中只有BroadcastReceiver既可以在AndroidManifest文件中注册,也可以在代码中注册,其他三个组件都必须在 ...

随机推荐

  1. 关于Navicat连接oralcle出现Cannot load OCI DLL 87,126,193 ,ORA-28547等错误

    navicat连接oracle数据库报ORA-28547: connection to server failed, probable Oracle Net admin error错误的解决方法 na ...

  2. 创建dynamics CRM client-side (十三) - 在HTML Web Resource中获取form elements & 获取外部js文件

    上一节我们讨论到创建HTML Web Resource. 但是纯HTML的页面不能满足我们的需求, 所以今天我们来做在HTML Web Resource中获取form elements Please ...

  3. [CF 487C Prefix Product Sequence]

    题意 将1~n的正整数重排列,使得它的前缀积在模n下形成0~n-1的排列,构造解或说明无解.n≤1E5. 思考 小范围内搜索解,发现n=1,n=4和n为质数时有解. 不难发现,n一定会放在最后,否则会 ...

  4. django1.11版本在python3.7中运行还有点兼容性问题.

    django1.11版本在python3.7中运行还有点兼容性问题. 出现SyntaxError: Generator expression must be parenthesized这个报错 找到这 ...

  5. 每日一技|巧用 Telnet 调试 Dubbo 服务

    个人博客地址 studyidea.cn,点击查看更多原创文章 0x00. 前言 想象这样一个场景,线上某个服务突发异常,导致上游服务调用异常,数据处于中间状态.服务恢复之后,我们需要修复这笔数据至正常 ...

  6. 关于Windows Server 服务器 安装tomcat部署Java Web 项目的问题

    我遇到的问题是:不知道怎么配置,感觉在服务器上部署一个web项目,应该是很高大上,步骤应该很繁琐,但是,事实却不是那样.配置反而挺简单. tomcat配置:在tomcat安装目录中的conf文件夹下有 ...

  7. 把"重试"抽象出来做个工具类吧

    背景介绍 我们在工作中难免会写一些重复性的代码,所以需要我们具备一定的抽象能力,比如把共同的逻辑抽取到抽象类中,也可以通过一些工具类来避免冗余代码 今天这篇文章就是把一个调用服务的重试功能抽取出一个工 ...

  8. Node——request使用代理

    本文知识点 Node环境搭建 使用代理 进阶学习 环境配置 Node 安装request 安装request npm install request 确认环境安装无误 node -v 代码样例 使用代 ...

  9. wordpress 如何防止盗链

    盗链是指其它站点使用了你自己网站的图片音乐等资源,然后链接又没有更换,直接显示的就是你站点的图片,这在转载文章时最常见,比如转载时将 整篇文章都转载过去,包括文章里面的图片,有些人就懒得把图片再重新上 ...

  10. jmeter 源码修改返回值中文Unicode编码问题

    修改jmeter源码,可能会对其他格式的responseData有一定影响,图片或者其他 在 ListenerNotifier 类中找到 notifyListeners 方法,在其下面添加如下代码: ...