mirahs 2019-06-28
2.AlertDialog源码分析
3.Dialog源码分析
最简单用法如下所示
private AlertDialog alertDialog=null; public void showDialog(){ AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setIcon(R.mipmap.ic_launcher); builder.setMessage("潇湘剑雨"); builder.setTitle("这个是标题"); builder.setView(R.layout.activity_main); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { alertDialog.dismiss(); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { alertDialog.dismiss(); } }); alertDialog = builder.create(); alertDialog.show(); }
先来看一下AlertDialog.Builder的构造方法,这里的Builder是AlertDialog的内部类,用于封装AlertDialog的构造过程,看一下Builder的构造方法:
public Builder(Context context) { this(context, resolveDialogTheme(context, 0)); }
public Builder(Context context, int themeResId) { P = new AlertController.AlertParams(new ContextThemeWrapper( context, resolveDialogTheme(context, themeResId))); }
接着这里的P是AlertDialog.Builder中的一个AlertController.AlertParams类型的成员变量,可见在这里执行了P的初始化操作。
public AlertParams(Context context) { mContext = context; mCancelable = true; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); }
可以看到这里主要执行了AlertController.AlertParams的初始化操作,初始化了一些成员变量。这样执行了一系列操作之后我们的代码:
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
调用了builder.setIcon方法,这里看一下setIcon方法的具体实现:
public Builder setIcon(@DrawableRes int iconId) { P.mIconId = iconId; return this; }
调用了builder.setMessage方法,可以看一下builder.setMessage方法的具体实现:
public Builder setMessage(CharSequence message) { P.mMessage = message; return this; }
然后看一下builder.setTitle方法:
public Builder setTitle(CharSequence title) { P.mTitle = title; return this; }
接着看一下builder.setView方法:
public Builder setView(int layoutResId) { P.mView = null; P.mViewLayoutResId = layoutResId; P.mViewSpacingSpecified = false; return this; }
然后调用了builder.create方法,并且这个方法返回了AlertDialog。
可以看到这里首先构造了一个AlertDialog,我们可以看一下这个构造方法的具体实现:
回到AlertDialog的构造方法中,在构造方法中,除了调用Dialog的构造方法之外还执行了
mAlert = new AlertController(getContext(), this, getWindow());
再AlertDialog.Builder.create方法,在创建了一个AlertDialog之后,又执行了P.apply(dialog.mAlert);这里的P是一个AlertController.AlertParams的变量,而dialog.mAlert是刚刚创建的AlertDialog中的一个AlertController类型的变量,来看一下apply方法的具体实现:
ublic void apply(AlertController dialog) { if (mCustomTitleView != null) { dialog.setCustomTitle(mCustomTitleView); } else { if (mTitle != null) { dialog.setTitle(mTitle); } if (mIcon != null) { dialog.setIcon(mIcon); } if (mIconId != 0) { dialog.setIcon(mIconId); } if (mIconAttrId != 0) { dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); } } if (mMessage != null) { dialog.setMessage(mMessage); } if (mPositiveButtonText != null) { dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText, mPositiveButtonListener, null); } if (mNegativeButtonText != null) { dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText, mNegativeButtonListener, null); } if (mNeutralButtonText != null) { dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText, mNeutralButtonListener, null); } if (mForceInverseBackground) { dialog.setInverseBackgroundForced(true); } // For a list, the client can either supply an array of items or an // adapter or a cursor if ((mItems != null) || (mCursor != null) || (mAdapter != null)) { createListView(dialog); } if (mView != null) { if (mViewSpacingSpecified) { dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom); } else { dialog.setView(mView); } } else if (mViewLayoutResId != 0) { dialog.setView(mViewLayoutResId); } }
看看如下所示,可以发现直接调用了dialog中的show方法。下面接着分析
如下所示
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (themeResId == 0) { final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true); themeResId = outValue.resourceId; } //创建一个Context mContext = new ContextThemeWrapper(context, themeResId); } else { mContext = context; } //获取一个WindowManager对象 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); //创建一个Window对象 final Window w = new PhoneWindow(mContext); //将Window对象w赋值给mWindow mWindow = w; //为Windowd对象设置回调,并且它本身实现了这些回调函数 w.setCallback(this); w.setOnWindowDismissedCallback(this); //为Window对象设置WindowManager对象 w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); //创建一个对话框监听Handler mListenersHandler = new ListenersHandler(this); }
接着看看w.setWindowManager(mWindowManager, null, null)里面的源代码
dialog的生命周期如下所示
/** * 类似于Activity的onCreate函数,可以在这个方法中进行Dialog的一些初始化操作
*/ protected void onCreate(Bundle savedInstanceState) { } /** * 当对话框启动的时候被调用. */ protected void onStart() { } /** * 当对话框停止的时候被调用. */ protected void onStop() { } ```
源码如下所示,关于重点的逻辑,我在这里只是简单的注释了一下。
public void show() { //首先判断对话框是否显示 if (mShowing) { if (mDecor != null) { if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); } mDecor.setVisibility(View.VISIBLE); } return; } mCanceled = false; /* 判断对话框是否创建过,如果没有创建过
*/ if (!mCreated) { dispatchOnCreate(null); } //回调onStart函数 onStart(); //获取Window对象总的DecorView,如果调用了setContentView就会创建DecorView mDecor = mWindow.getDecorView(); //下面就会获取布局的一些属性 if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); mActionBar = new WindowDecorActionBar(this); } WindowManager.LayoutParams l = mWindow.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); nl.copyFrom(l); nl.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; l = nl; } try { //将DecorView添加到WindowManager中,这些就会显示了 mWindowManager.addView(mDecor, l); //将mShowing置为true mShowing = true; sendShowMessage(); } finally { } } ```
方法体的内容比较多,由于一开始mShowing变量用于表示当前dialog是否正在显示,由于我们刚刚开始调用执行show方法,所以这里的mShowing变量的值为false,所以if分支的内容不会被执行,继续往下看:
if (!mCreated) { dispatchOnCreate(null); }
mCreated这个控制变量控制dispatchOnCreate方法只被执行一次,由于是第一次执行,所以这里会执行dispatchOnCreate方法,好吧,看一下dispatchOnCreate方法的执行逻辑:
void dispatchOnCreate(Bundle savedInstanceState) { if (!mCreated) { onCreate(savedInstanceState); mCreated = true; } }
调用了onStart方法,这个方法主要用于设置ActionBar,这里不做过多的说明,然后初始化WindowManager.LayoutParams对象,并最终调用我们的mWindowManager.addView()方法。
protected void onStart() { if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); }
最后调用了sendShowMessage方法,可以看一下这个方法的实现:
private void sendShowMessage() { if (mShowMessage != null) { // Obtain a new message so this dialog can be re-used Message.obtain(mShowMessage).sendToTarget(); } }
这里会发送一个Dialog已经显示的异步消息,该消息最终会在ListenersHandler中的handleMessage方法中被执行:
private static final class ListenersHandler extends Handler { private WeakReference<DialogInterface> mDialog; public ListenersHandler(Dialog dialog) { mDialog = new WeakReference<DialogInterface>(dialog); } @Override public void handleMessage(Message msg) { switch (msg.what) { case DISMISS: ((OnDismissListener) msg.obj).onDismiss(mDialog.get()); break; case CANCEL: ((OnCancelListener) msg.obj).onCancel(mDialog.get()); break; case SHOW: ((OnShowListener) msg.obj).onShow(mDialog.get()); break; } } }
由于我们的msg.what = SHOW,所以会执行OnShowListener.onShow方法,那么这个OnShowListener是何时赋值的呢?还记得我们构造AlertDialog.Builder么?
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { } });
这样就为我们的AlertDialog.Builder设置了OnShowListener,可以看一下setOnShowListener方法的具体实现:
public void setOnShowListener(OnShowListener listener) { if (listener != null) { mShowMessage = mListenersHandler.obtainMessage(SHOW, listener); } else { mShowMessage = null; } }
调用alertDialog.cancel()或者alertDialog.dismiss()都可以达到销毁弹窗的效果。
public void cancel() { if (!mCanceled && mCancelMessage != null) { mCanceled = true; // Obtain a new message so this dialog can be re-used Message.obtain(mCancelMessage).sendToTarget(); } dismiss(); }
可以看到方法体中,若当前Dialog没有取消,并且设置了取消message,则调用Message.obtain(mCancel).sendToTarget(),前面已经分析过这里的sendToTarget方法会回调注册的异步消息处理逻辑:
public void setOnCancelListener(final OnCancelListener listener) { if (mCancelAndDismissTaken != null) { throw new IllegalStateException( "OnCancelListener is already taken by "
} if (listener != null) { mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener); } else { mCancelMessage = null; } } ```
可以看到如果在初始化AlertDialog.Builder时,设置了setOnCancelListener,那么就会执行mListenersHandler的异步消息处理,好吧,这里看一下mListenersHandler的定义:
private static final class ListenersHandler extends Handler { private WeakReference<DialogInterface> mDialog; public ListenersHandler(Dialog dialog) { mDialog = new WeakReference<DialogInterface>(dialog); } @Override public void handleMessage(Message msg) { switch (msg.what) { case DISMISS: ((OnDismissListener) msg.obj).onDismiss(mDialog.get()); break; case CANCEL: ((OnCancelListener) msg.obj).onCancel(mDialog.get()); break; case SHOW: ((OnShowListener) msg.obj).onShow(mDialog.get()); break; } } }
如下所示
public void dismiss() { if (Looper.myLooper() == mHandler.getLooper()) { dismissDialog(); } else { mHandler.post(mDismissAction); } }
然后看一下mHandler对异步消息的处理机制,由于这里的mDismissAction是一个Runnable对象,所以这里直接看一下mDismissAction的定义:
private final Runnable mDismissAction = new Runnable() { public void run() { dismissDialog(); } };
所以无论执行的cancel方法还是dismiss方法,无论方法是在主线程执行还是子线程中执行,最终调用的都是dismissDialog方法,那么就看一下dismissDialog是怎么个执行逻辑。
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(); } }
mWindowManager其实是WindowManagerImpl的实例,所以这里的removeViewImmediate方法应该是WindowManagerImpl中的方法,看一下它的具体实现:
@Override public void removeViewImmediate(View view) { mGlobal.removeView(view, true); }
可以发现,这里它调用了mGlobal.removeView方法,而这里的mGlobal是WindowManagerGlobal的实例,所以再看一下WIndowManagerGlobal中removeView的实现逻辑:
public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { 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
} } ```
可以发现,这里在获取了保存的mDector组件之后,又调用了removeViewLocked方法,在看一下这个方法的具体实现逻辑:
private void removeViewLocked(int index, boolean immediate) { 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 = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); } } }
看到了么,我们获取了mDector组件的ViewRootImpl,然后调用了其的die方法,通过这个方法实现Window组件的销毁流程。
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(TAG, "Attempting to destroy the window while drawing!\n" + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } mHandler.sendEmptyMessage(MSG_DIE); return true; }
可以看到在方法体中有调用了doDie方法,看名字应该就是真正执行window销毁工作的方法了,我们在看一下doDie方法的具体实现:
void doDie() { checkThread(); if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface); synchronized (this) { if (mRemoved) { return; } mRemoved = true; if (mAdded) { 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; } WindowManagerGlobal.getInstance().doRemoveView(this); }
可以看到方法体中,首先调用了checkThread方法,判断当前执行代码的线程,若不是主线程,则抛出异常:
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
顺着doDie的方法往下看,又调用了dispatchDetachedFromWindow()方法,这个方法主要是销毁Window中的各中成员变量,临时变量等
void dispatchDetachedFromWindow() { if (mView != null && mView.mAttachInfo != null) { mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); 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; } 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(); }
可以看到在方法中调用了mView.dispatchDetachedFromWindow方法,这个方法的作用就是将mView从Window中detach出来,可以看一下这个方法的具体实现:
void dispatchDetachedFromWindow() { AttachInfo info = mAttachInfo; if (info != null) { int vis = info.mWindowVisibility; if (vis != GONE) { onWindowVisibilityChanged(GONE); } } onDetachedFromWindow(); onDetachedFromWindowInternal(); InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { imm.onViewDetachedFromWindow(this); } ListenerInfo li = mListenerInfo; final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners = li != null ? li.mOnAttachStateChangeListeners : null; if (listeners != null && listeners.size() > 0) { // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. for (OnAttachStateChangeListener listener : listeners) { listener.onViewDetachedFromWindow(this); } } if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0) { mAttachInfo.mScrollContainers.remove(this); mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED; } mAttachInfo = null; if (mOverlay != null) { mOverlay.getOverlayView().dispatchDetachedFromWindow(); } }
其中onDetachedFromWindow方法是一个空的回调方法,这里重点看一下onDetachedFromWindowInternal方法:
protected void onDetachedFromWindowInternal() { mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT; removeUnsetPressCallback(); removeLongPressCallback(); removePerformClickCallback(); removeSendViewScrolledAccessibilityEventCallback(); stopNestedScroll(); // Anything that started animating right before detach should already // be in its final state when re-attached. jumpDrawablesToCurrentState(); destroyDrawingCache(); cleanupDraw(); mCurrentAnimation = null; }
onDetachedFromWindowInternal方法的方法体也不是特别长,都是一些调用函数,这里看一下destropDrawingCache方法,这个方法主要是销毁View的缓存Drawing,我们来看一下具体实现:
public void destroyDrawingCache() { if (mDrawingCache != null) { mDrawingCache.recycle(); mDrawingCache = null; } if (mUnscaledDrawingCache != null) { mUnscaledDrawingCache.recycle(); mUnscaledDrawingCache = null; } }