Dialog源码分析
目录介绍
- 1.简单用法
- 2.AlertDialog源码分析
- 2.1 AlertDialog.Builder的构造方法
- 2.2 通过AlertDialog.Builder对象设置属性
- 2.3 builder.create方法
- 2.4 看看create方法中的P.apply(dialog.mAlert)源码
- 2.5 看看AlertDialog的show方法
- 3.Dialog源码分析
- 3.1 Dialog的构造方法
- 3.2 Dialog生命周期
- 3.3 Dialog中show方法展示弹窗
- 3.4 Dialog的dismiss销毁弹窗
- 4.Dialog弹窗问题分析
- 5.Dialog弹窗总结
好消息
- 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
- 链接地址:https://github.com/yangchong211/YCBlogs
- 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!
- DialogFragment封装库项目地址:https://github.com/yangchong211/YCDialog
- 02.Toast源码深度分析
- 最简单的创建,简单改造避免重复创建,show()方法源码分析,scheduleTimeoutLocked吐司如何自动销毁的,TN类中的消息机制是如何执行的,普通应用的Toast显示数量是有限制的,用代码解释为何Activity销毁后Toast仍会显示,Toast偶尔报错Unable to add window是如何产生的,Toast运行在子线程问题,Toast如何添加系统窗口的权限等等
- 03.DialogFragment源码分析
- 最简单的使用方法,onCreate(@Nullable Bundle savedInstanceState)源码分析,重点分析弹窗展示和销毁源码,使用中show()方法遇到的IllegalStateException分析
- 05.PopupWindow源码分析
- 显示PopupWindow,注意问题宽和高属性,showAsDropDown()源码,dismiss()源码分析,PopupWindow和Dialog有什么区别?为何弹窗点击一下就dismiss呢?
- 06.Snackbar源码分析
- 最简单的创建,Snackbar的make方法源码分析,Snackbar的show显示与点击消失源码分析,显示和隐藏中动画源码分析,Snackbar的设计思路,为什么Snackbar总是显示在最下面
- 07.弹窗常见问题
- DialogFragment使用中show()方法遇到的IllegalStateException,什么常见产生的?Toast偶尔报错Unable to add window,Toast运行在子线程导致崩溃如何解决?
- 08.Builder模式
- Builder模式使用场景,简单案例,Builder模式实际案例Demo展示,看看AlertDialog.Builder源代码如何实现,为什么AlertDialog要使用builder模式呢?builder模式优缺点分析。关于builder模式经典的案例可以参考我的弹窗封装库:https://github.com/yangchong211/YCDialog
1.简单用法
- 一般都是在使用AlertDialog,但AlertDialog主要也是继承自Dialog。下面我们来分析分析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();
}
2.AlertDialog源码分析
2.1 AlertDialog.Builder的构造方法
- 先来看一下AlertDialog.Builder的构造方法,这里的Builder是AlertDialog的内部类,用于封装AlertDialog的构造过程,看一下Builder的构造方法:
public Builder(Context context) {
this(context, resolveDialogTheme(context, 0));
}
- 然后调用的是Builder的重载构造方法:
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);
2.2 通过AlertDialog.Builder对象设置属性
- 调用了builder.setIcon方法,这里看一下setIcon方法的具体实现:
- 可以看到AlertDialog的Builder的setIcon方法,这里执行的就是给类型为AlertController.AlertParams的P的mIconId赋值为传递的iconId,并且这个方法返回的类型就是Builder。
public Builder setIcon(@DrawableRes int iconId) {
P.mIconId = iconId;
return this;
}
- 调用了builder.setMessage方法,可以看一下builder.setMessage方法的具体实现:
- 跟setIcon方法的实现逻辑类似,都是给成员变量的mMessage赋值为我们传递的Message值,且和setIcon方法类似的,这个方法返回值也是Builder。
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
- 然后看一下builder.setTitle方法:
- 发现builder的setIcon、setMessage、setTitle等方法都是给Builder的成员变量P的icon,message,title赋值。
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
- 接着看一下builder.setView方法:
- 发现这里的setView和setIcon,setMessage,setTitle等方法都是类似的,都是将我们传递的数据值赋值给Builder的成员变量P。
public Builder setView(int layoutResId) {
P.mView = null;
P.mViewLayoutResId = layoutResId;
P.mViewSpacingSpecified = false;
return this;
}
2.3 builder.create方法
- 然后调用了builder.create方法,并且这个方法返回了AlertDialog。
- 可以看到这里首先构造了一个AlertDialog,我们可以看一下这个构造方法的具体实现:
- 可以看到这里首先调用了super的构造方法,而我们的AlertDialog继承于Dialog,所以这里执行的就是Dialog的构造方法【备注:高版本是继承AppCompatDialog,然后AppCompatDialog再继承Dialog】
- 回到AlertDialog的构造方法中,在构造方法中,除了调用Dialog的构造方法之外还执行了
- 相当于初始化了AlertDiaog的成员变量mAlert。
mAlert = new AlertController(getContext(), this, getWindow());
2.4 看看create方法中的P.apply(dialog.mAlert)源码
- 再AlertDialog.Builder.create方法,在创建了一个AlertDialog之后,又执行了P.apply(dialog.mAlert);这里的P是一个AlertController.AlertParams的变量,而dialog.mAlert是刚刚创建的AlertDialog中的一个AlertController类型的变量,来看一下apply方法的具体实现:
- 在初始化AlertDialog.Builder的时候设置的icon、title、message赋值给了AlertController.AlertParams,这里就是将初始化时候设置的属性值赋值给我们创建的Dialog对象的mAlert成员变量
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);
}
}
2.5 看看AlertDialog的show方法
- 看看如下所示,可以发现直接调用了dialog中的show方法。下面接着分析
3.Dialog源码分析
3.1 Dialog的构造方法
- 如下所示
- 看源码可知在Dialog的构造方法中直接直接构造了一个PhoneWindow,并赋值给Dialog的成员变量mWindow,从这里可以看出其实Dialog和Activity的显示逻辑都是类似的,都是通过对应的Window变量来实现窗口的加载与显示的。然后我们执行了一些Window对象的初始化操作,比如设置回调函数为本身,然后调用Window类的setWindowManager方法,并传入了WindowManager。然后创建一个对话框监听handler对象。
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)里面的源代码
- 可以看到跟Activity的Window对象的windowManager的获取方式是相同的,都是通过new的方式创建一个新的WindowManagerImpl对象。
3.2 Dialog生命周期
- dialog的生命周期如下所示
/**
* 类似于Activity的onCreate函数,可以在这个方法中进行Dialog的一些初始化操作
* 包括调用setContentView方法
*/
protected void onCreate(Bundle savedInstanceState) { }
/**
* 当对话框启动的时候被调用.
*/
protected void onStart() { }
/**
* 当对话框停止的时候被调用.
*/
protected void onStop() { }
3.3 Dialog中show方法展示弹窗
- 源码如下所示,关于重点的逻辑,我在这里只是简单的注释了一下。
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;
/* 判断对话框是否创建过,如果没有创建过
* 在dispatchOnCreate里面就会回调onCreate函数
*/
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方法的执行逻辑:
- 可以看到代码的执行逻辑很简单就是回调了Dialog的onCreate方法
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方法的具体实现:
- 这样就为我们的Dialog中的mListenersHandler构造了Message对象,并且在Dialog中发送showMessage的时候被mListenersHandler所接收。
public void setOnShowListener(OnShowListener listener) {
if (listener != null) {
mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
} else {
mShowMessage = null;
}
}
3.4 Dialog的dismiss销毁弹窗
3.4.1 看看cancel()方法
- 调用alertDialog.cancel()或者alertDialog.dismiss()都可以达到销毁弹窗的效果。
- 首先看一下Dialog的cannel方法的具体实现:
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 "
+ mCancelAndDismissTaken + " and can not be replaced.");
}
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;
}
}
}
- 调用的是设置的OnCancelListener的onCancel方法,也就是说调用dialog.cancel方法时首先会判断dialog是否调用了setOnCancelListener若设置了,则先调用OnCancelListener的onCancel方法,然后再次执行dismiss方法,若我们没有为Dialog.Builder设置OnCancelListener那么cancel方法和dismiss方法是等效的。
3.4.2 看看dismiss方法
- 如下所示
- 可以看到,这里首先判断当前线程的Looper是否是主线程的Looper(由于mHandler是在主线程中创建的,所以mHandler.getLooper返回的是主线程中创建的Looper对象),若是的话,则直接执行dismissDialog()方法,否则的话,通过mHandler发送异步消息至主线程中,简单来说就是判断当前线程是否是主线程,若是主线程则执行dismissDialog方法否则发送异步消息
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
}
- 然后看一下mHandler对异步消息的处理机制,由于这里的mDismissAction是一个Runnable对象,所以这里直接看一下mDismissAction的定义:
- 这里的异步消息最终也是调用的dismissDialog方法
private final Runnable mDismissAction = new Runnable() {
public void run() {
dismissDialog();
}
};
3.4.3 cancel和dismiss方法都调用dismissDialog方法
- 所以无论执行的cancel方法还是dismiss方法,无论方法是在主线程执行还是子线程中执行,最终调用的都是dismissDialog方法,那么就看一下dismissDialog是怎么个执行逻辑。
- 首先判断当前的mDector是否为空,或者当前Dialog是否在显示,若为空或者没有在显示,则直接return掉,也就是说当前我们的dialog已经不再显示了,则不需要往下在执行。然后调用了mWindow.isDestroyed()方法,判断Window对象是否已经被销毁,若已经被销毁,则直接return,并打印错误日志。
- 然后再调用了mWindowManager.removeViewImmediate(mDector),这里的mDector是Dialog窗口的根布局,看这个方法的名字应该就是Dialog去除根布局的操作了,可以看一下这个方法的具体实现。
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
+ " but the ViewAncestor is attached to " + curView);
}
}
- 可以发现,这里在获取了保存的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,我们来看一下具体实现:
- 这里的mDrawingCache其实就是一个Bitmap类型的成员变量,而这里调用的recycler和置空操作其实就是把View中执行draw方法之后缓存的bitmap清空。
- 这里需要说明的是,我们View组件的最终显示落实是通过draw方法实现绘制的,而我们的draw方法的参数是一个Canvas,这是一个画布的对象,通过draw方法就是操作这个对象并显示出来,而Canvas对象之所以能够实现显示的效果是因为其内部保存着一个Bitmap对象,通过操作Canvas对象实质上是操作Canvas对象内部的Bitmap对象,而View组件的显示也就是通过这里的Bitmap来实现的。
- 而我们上文中置空了bitmap对象就相当于把View组件的显示效果置空了,就是相当于我们取消了View的draw方法的执行效果,继续回到我们的dispatchDetachedFromWindow方法,在执行了mView.dispatchDetachedFromWindow()方法之后,又调用了mView = null;方法,这里设置mView为空,这样我们有取消了View的meature和layouot的执行效果。
public void destroyDrawingCache() {
if (mDrawingCache != null) {
mDrawingCache.recycle();
mDrawingCache = null;
}
if (mUnscaledDrawingCache != null) {
mUnscaledDrawingCache.recycle();
mUnscaledDrawingCache = null;
}
}
5.Dialog弹窗总结
- Dialog中的Window对象与Activity中的Window对象是相似的,都对应着一个WindowManager对象;Dialog和Activity的显示逻辑是相似的都是内部管理这一个Window对象,用WIndow对象实现界面的加载与显示逻辑。
- Dialog相关的几个类:Dialog,AlertDialog,AlertDialog.Builder,AlertController,AlertController.AlertParams,其中Dialog是窗口的父类,主要实现Window对象的初始化和一些共有逻辑,而AlertDialog是具体的Dialog的操作实现类,AlertDialog.Builder类是AlertDialog的内部类,主要用于构造AlertDialog,AlertController是AlertDialog的控制类,AlertController.AlertParams类是控制参数类;
- 构造AlertDialog用到了很经典的buidler构造者模式。关于buidler模式,可以参考Builder模式。构造显示Dialog的一般流程,构造AlertDialog.Builder,然后设置各种属性,最后调用AlertDialog.Builder.create方法获取AlertDialog对象,并且create方法中会执行,构造AlertDialog,设置dialog各种属性的操作。最后我们调用Dialog.show方法展示窗口,初始化Dialog的布局文件,Window对象等,然后执行mWindowManager.addView方法,开始执行绘制View的操作,并最终将Dialog显示出来。
- 窗口的取消绘制流程是相似的,包括Activity和Dialog等;通过调用WindowManager.removeViewImmediate方法,开始执行Window窗口的取消绘制流程;Window窗口的取消绘制流程,通过清空bitma撤销draw的执行效果,通过置空View撤销meature和layout的执行效果。
关于其他内容介绍
01.关于博客汇总链接
02.关于我的博客
- 我的个人站点:www.yczbj.org,www.ycbjie.cn
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
- 简书:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
- 开源中国:https://my.oschina.net/zbj1618/blog
- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 邮箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
Dialog源码分析的更多相关文章
- Android源码分析-全面理解Context
前言 Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像 ...
- Robotium源码分析之运行原理
从上一章<Robotium源码分析之Instrumentation进阶>中我们了解到了Robotium所基于的Instrumentation的一些进阶基础,比如它注入事件的原理等,但Rob ...
- 手机自动化测试:Appium源码分析之跟踪代码分析七
手机自动化测试:Appium源码分析之跟踪代码分析七 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自 ...
- Android事件传递机制详解及最新源码分析——Activity篇
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在前两篇我们共同探讨了事件传递机制<View篇>与<ViewGroup篇>,我们知道View触摸事件是ViewGroup传递 ...
- 插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broa ...
- element-ui 组件源码分析整理笔记目录
element-ui button组件 radio组件源码分析整理笔记(一) element-ui switch组件源码分析整理笔记(二) element-ui inputNumber.Card .B ...
- EasyUI学习总结(三)——easyloader源码分析(转载)
声明:这一篇文章是转载过来的,转载地址忘记了,原作者如果看到了,希望能够告知一声,我好加上去! easyloader模块是用来加载jquery easyui的js和css文件的,而且它可以分析模块的依 ...
- Android View事件分发-从源码分析
View事件分发-从源码分析 学习自 <Android开发艺术探索> https://blog.csdn.net/qian520ao/article/details/78555397?lo ...
- Kettle 4.2源码分析第四讲--KettleJob机制与Database插件简介(含讲解PPT)
1. Job机制 一个job项代表ETL控制流中的一项逻辑任务.Job项将会顺序执行,每个job项会产生一个结果,能作为别的分支上job项的条件. 图 1 job项示例 1.1. Job类图简介 图 ...
- 【转载】AsyncTask源码分析
原文地址:https://github.com/white37/AndroidSdkSourceAnalysis/blob/master/article/AsyncTask%E5%92%8CAsync ...
随机推荐
- 图像列表组件(TImageList)
TImageList 组件是一组同样尺寸的图像列表,每一个图像由它的Index值查询. 1.TImageList组件的典型用法 图形列表用于建立和管理大量的图像,主要是配合菜单或者工具栏按钮的使用.图 ...
- Java-将文本(字符串)转化成二进制字符
今天在测试MySQL的Blob相关类型时,这种一般存放的是二进制文本,所以就想插入二进制文本. package com.aaa.dao; public class aaa { public stati ...
- 开源.NetCore通用工具库Xmtool使用连载 - 随机值篇
[Github源码] <上一篇> 详细介绍了Xmtool工具库中的散列算法类库,今天我们继续为大家介绍其中的随机值类库. 基于系统提供的Random获取随机值方法已经足够简单和易用,本类库 ...
- P9801 [NERC2018] King Kog’s Reception
题目传送门 前置知识 线段树 解法 第一眼感觉和 luogu P1083 [NOIP2012 提高组] 借教室 很像.本题同样采用线段树维护,\(sum_{l,r}(1 \le l \le r \le ...
- react 八千字长文深入了解react合成事件底层原理,原生事件中阻止冒泡是否会阻塞合成事件?
壹 ❀ 引 在前面两篇文章中,我们花了较大的篇幅介绍react的setState方法,在介绍setState同步异步时提到,在react合成事件中react对于this.state更新都是异步,但在原 ...
- STC8H8K64U 的 USB 功能测试(未成功)
对 STC8H8K64U 的 USB 功能测试, 因为存在很多问题并且未能解释/解决, 就不写到系列里了, 把记录放上来抛砖引玉吧. 代码 测试代码下载地址 http://www.stcmcudata ...
- Java设计模式-中介者模式Mediator
介绍 中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互.中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 中介者模式属于行 ...
- Spring和Spring Boot的区别
spring Spring 是一个开源轻量级框架,它允许 Java EE 7 开发人员构建简单.可靠和可扩展的企业应用程序.该框架主要侧重于提供各种方法来帮助您管理业务对象.与 Java 数据库连接 ...
- win32-制作mini dump文件
一个完整的用户模式dump是基本的用户模式转储文件. 此转储文件包括进程的整个内存空间,程序的可执行映像本身,句柄表以及其他信息,这些信息对于调试器在重建转储发生时正在使用的内存中很有用. 可以将完整 ...
- Oracle设置日志参数-ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
要实现两个数据库之间的实时同步,需要给Oracle设置参数 ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; -- 执行了12小时,等待数据库中的其它事务都提交以后才 ...