我的博客:
http://www.milovetingting.cnjava
Window表示一個窗口的概念,是一個抽象類,具體實現是PhoneWindow,能夠經過WindowManager建立一個Window。WindowManager是外界訪問Window的入口,Window具體實現位於WindowManagerService中,WindowManager和WindowManagerService的交互是一個IPC過程。
WindowManager.LayoutParamsandroid
關注flags和type兩個參數:app
Flags參數表示Window的屬性,能夠控制Window的顯示特性。ide
** FLAG_NOT_FOCUSABLE **oop
表示Window不須要獲取焦點,也不須要接收各類輸入事件,此標記會同時啓用FLAG_NOT_TOUCH_MODAL,最終事件會直接傳遞給下層的具備焦點的Window。佈局
** FLAG_NOT_TOUCH_MODAL **post
系統會將當前Window區域之外的單擊事件傳遞給底層的Window,當前Window區域之內的單擊事件本身處理。this
** FLAG_SHOW_WHEN_LOCKED **google
讓Window顯示在鎖屏的界面上。spa
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都對應一個View和一個ViewRootImpl,Window和View經過ViewRootImpl來創建聯繫。
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; //一、調用requestLayout方法 this.requestLayout(); //... int res; try { this.mOrigWindowType = this.mWindowAttributes.type; this.mAttachInfo.mRecomputeGlobalAttributes = true; this.collectViewAttributes(); //二、經過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); //... } } }
一、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的刪除過程和添加過程基本同樣,都是先經過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的更新是經過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); } } }
先說明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); }
一、建立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); }
二、初始化DecorView並將Dialog的視圖添加到DecorView中
public void setContentView(@LayoutRes int layoutResID) { mWindow.setContentView(layoutResID); }
三、將DecorView添加到Window
public void show() { //... mWindowManager.addView(mDecor, l); mShowing = true; sendShowMessage(); }
普通Dialog必須採用Activity的Context。
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。