個人博客: "http://www.milovetingting.cn" 理解Window和WindowManager Window和WindowManager WindowManager.LayoutParams 關註flags和type兩個參數: Flags參數表示Window的屬性,可以控制W ...
理解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。