Android Dialog 源码分析

1.简单用法

  • 一般都是在使用AlertDialog,但AlertDialog主要也是继承自Dialog。下面我们来分析分析Dialog源码。

  • 最简单用法如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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的构造方法:

    1
    2
    3
    public Builder(Context context) {
    this(context, resolveDialogTheme(context, 0));
    }
    • 然后调用的是Builder的重载构造方法:
    1
    2
    3
    4
    public Builder(Context context, int themeResId) {
    P = new AlertController.AlertParams(new ContextThemeWrapper(
    context, resolveDialogTheme(context, themeResId)));
    }
  • 接着这里的P是AlertDialog.Builder中的一个AlertController.AlertParams类型的成员变量,可见在这里执行了P的初始化操作。

    1
    2
    3
    4
    5
    public AlertParams(Context context) {
    mContext = context;
    mCancelable = true;
    mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
  • 可以看到这里主要执行了AlertController.AlertParams的初始化操作,初始化了一些成员变量。这样执行了一系列操作之后我们的代码:

    1
    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

2.2 通过AlertDialog.Builder对象设置属性

  • 调用了builder.setIcon方法,这里看一下setIcon方法的具体实现:

    • 可以看到AlertDialog的Builder的setIcon方法,这里执行的就是给类型为AlertController.AlertParams的P的mIconId赋值为传递的iconId,并且这个方法返回的类型就是Builder。
    1
    2
    3
    4
    public Builder setIcon(@DrawableRes int iconId) {
    P.mIconId = iconId;
    return this;
    }
  • 调用了builder.setMessage方法,可以看一下builder.setMessage方法的具体实现:

    • 跟setIcon方法的实现逻辑类似,都是给成员变量的mMessage赋值为我们传递的Message值,且和setIcon方法类似的,这个方法返回值也是Builder。
    1
    2
    3
    4
    public Builder setMessage(CharSequence message) {
    P.mMessage = message;
    return this;
    }
  • 然后看一下builder.setTitle方法:

    • 发现builder的setIcon、setMessage、setTitle等方法都是给Builder的成员变量P的icon,message,title赋值。
    1
    2
    3
    4
    public Builder setTitle(CharSequence title) {
    P.mTitle = title;
    return this;
    }
  • 接着看一下builder.setView方法:

    • 发现这里的setView和setIcon,setMessage,setTitle等方法都是类似的,都是将我们传递的数据值赋值给Builder的成员变量P。
    1
    2
    3
    4
    5
    6
    public Builder setView(int layoutResId) {
    P.mView = null;
    P.mViewLayoutResId = layoutResId;
    P.mViewSpacingSpecified = false;
    return this;
    }

2.3 builder.create方法

  • 然后调用了builder.create方法,并且这个方法返回了AlertDialog。

    • image
  • 可以看到这里首先构造了一个AlertDialog,我们可以看一下这个构造方法的具体实现:

    • 可以看到这里首先调用了super的构造方法,而我们的AlertDialog继承于Dialog,所以这里执行的就是Dialog的构造方法【备注:高版本是继承AppCompatDialog,然后AppCompatDialog再继承Dialog】
    • image
  • 回到AlertDialog的构造方法中,在构造方法中,除了调用Dialog的构造方法之外还执行了

    • 相当于初始化了AlertDiaog的成员变量mAlert。
    1
    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成员变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    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方法。下面接着分析
    • image

3.Dialog源码分析

3.1 Dialog的构造方法

  • 如下所示

    • 看源码可知在Dialog的构造方法中直接直接构造了一个PhoneWindow,并赋值给Dialog的成员变量mWindow,从这里可以看出其实Dialog和Activity的显示逻辑都是类似的,都是通过对应的Window变量来实现窗口的加载与显示的。然后我们执行了一些Window对象的初始化操作,比如设置回调函数为本身,然后调用Window类的setWindowManager方法,并传入了WindowManager。然后创建一个对话框监听handler对象。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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对象。
    • image

3.2 Dialog生命周期

  • dialog的生命周期如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * 类似于Activity的onCreate函数,可以在这个方法中进行Dialog的一些初始化操作
    * 包括调用setContentView方法
    */
    protected void onCreate(Bundle savedInstanceState) { }
    /**
    * 当对话框启动的时候被调用.
    */
    protected void onStart() { }
    /**
    * 当对话框停止的时候被调用.
    */
    protected void onStop() { }

3.3 Dialog中show方法展示弹窗

  • 源码如下所示,关于重点的逻辑,我在这里只是简单的注释了一下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    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分支的内容不会被执行,继续往下看:

    1
    2
    3
    if (!mCreated) {
    dispatchOnCreate(null);
    }
  • mCreated这个控制变量控制dispatchOnCreate方法只被执行一次,由于是第一次执行,所以这里会执行dispatchOnCreate方法,好吧,看一下dispatchOnCreate方法的执行逻辑:

    • 可以看到代码的执行逻辑很简单就是回调了Dialog的onCreate方法
    1
    2
    3
    4
    5
    6
    void dispatchOnCreate(Bundle savedInstanceState) {
    if (!mCreated) {
    onCreate(savedInstanceState);
    mCreated = true;
    }
    }
  • 调用了onStart方法,这个方法主要用于设置ActionBar,这里不做过多的说明,然后初始化WindowManager.LayoutParams对象,并最终调用我们的mWindowManager.addView()方法。

    1
    2
    3
    protected void onStart() {
    if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
    }
  • 最后调用了sendShowMessage方法,可以看一下这个方法的实现:

    • 那么发送这个消息主要是什么作用呢,逗比们,接着往下看看。
    1
    2
    3
    4
    5
    6
    private void sendShowMessage() {
    if (mShowMessage != null) {
    // Obtain a new message so this dialog can be re-used
    Message.obtain(mShowMessage).sendToTarget();
    }
    }
  • 这里会发送一个Dialog已经显示的异步消息,该消息最终会在ListenersHandler中的handleMessage方法中被执行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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么?

    1
    2
    3
    4
    5
    6
    alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
    @Override
    public void onShow(DialogInterface dialog) {

    }
    });
  • 这样就为我们的AlertDialog.Builder设置了OnShowListener,可以看一下setOnShowListener方法的具体实现:

    • 这样就为我们的Dialog中的mListenersHandler构造了Message对象,并且在Dialog中发送showMessage的时候被mListenersHandler所接收。
    1
    2
    3
    4
    5
    6
    7
    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方法的具体实现:
    1
    2
    3
    4
    5
    6
    7
    8
    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方法会回调注册的异步消息处理逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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的定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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方法否则发送异步消息
    1
    2
    3
    4
    5
    6
    7
    public void dismiss() {
    if (Looper.myLooper() == mHandler.getLooper()) {
    dismissDialog();
    } else {
    mHandler.post(mDismissAction);
    }
    }
  • 然后看一下mHandler对异步消息的处理机制,由于这里的mDismissAction是一个Runnable对象,所以这里直接看一下mDismissAction的定义:

    • 这里的异步消息最终也是调用的dismissDialog方法
    1
    2
    3
    4
    5
    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去除根布局的操作了,可以看一下这个方法的具体实现。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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中的方法,看一下它的具体实现:

    1
    2
    3
    4
    @Override
    public void removeViewImmediate(View view) {
    mGlobal.removeView(view, true);
    }
  • 可以发现,这里它调用了mGlobal.removeView方法,而这里的mGlobal是WindowManagerGlobal的实例,所以再看一下WIndowManagerGlobal中removeView的实现逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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方法,在看一下这个方法的具体实现逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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组件的销毁流程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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方法的具体实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    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方法,判断当前执行代码的线程,若不是主线程,则抛出异常:

    1
    2
    3
    4
    5
    6
    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中的各中成员变量,临时变量等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    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出来,可以看一下这个方法的具体实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    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方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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的执行效果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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构造者模式。构造显示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的执行效果。