Dialog源码分析

看了下建造者模式,其中有一种写法如下

public class Compute {

    private String mainBoard ;     // 主板
    private String cpu ;           // cpu

    
    public static class ComputeBuilder{
        // 一个电脑的必须配置
        private String mainBoard ;     // 主板
        private String cpu ;           // cpu


       public ComputeBuilder setMainBoard(String mainBoard){
           this.mainBoard = mainBoard;
           return this;
       }
       
       public ComputeBuilder setCpu(String cpu){
           this.cpu = cpu;
           return this;
       }   
       
       public Compute build(){
           return new Compute(this);
       }
    }
    
    public Compute(ComputeBuilder computeBuilder){
        this.mainBoard = computeBuilder.mainBoard;
        this.cpu = computeBuilder.cpu;
    }
}

基于Android4.4 得源码中,AlertDialog源码中用到了很多建造者模式得设计原则,所以就看下源码是如何实现得。

   

Dialog(Context context, int theme, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (theme == 0) {
                TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
                        outValue, true);
                theme = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, theme);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Window w = PolicyManager.makeNewWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);
        mListenersHandler = new ListenersHandler(this);
    }

看源码可知在Dialog得构造函数中,直接构造一个Window,并赋值给Dialog得mWindow,通过对应得Window变量来实现窗口得加载和显示,然后执行对象得初始化,调用Window 对象得setWindowManager方法,然后创建一个对话框监听Handler 对象.

Dialog 得生命周期如下:

   

 */
    protected void onCreate(Bundle savedInstanceState) {
    }

    /**
     * Called when the dialog is starting.
     */
    protected void onStart() {
        if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
    }

    /**
     * Called to tell you that you're stopping.
     */
    protected void onStop() {
        if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
    }


类似于Activity,onCreate做一些Dialog 得初始化操作,onStart()当启动得时候调用,onStop(当对话框停止得时候,调用。

看下 show()显示弹框得源码

 /**
     * Start the dialog and display it on screen.  The window is placed in the
     * application layer and opaque.  Note that you should not override this
     * method to do initialization when the dialog is shown, instead implement
     * that in {@link #onStart}.
     */
    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();
        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 {
            mWindowManager.addView(mDecor, l);
            mShowing = true;
    
            sendShowMessage();
        } finally {
        }
    }

可以看到,首先判断弹框是否显示了,如果显示了,则直接刷新显示层,接着判断对话框是否创建了,如果没有创建,则调用dispatchOnCreate,就会回调onCreate()函数。接着调用 onStart();,获取Window对象得总DecorView,如果调用了SetContentView就会创建DecorView mDecor = mWindow.getDecorView();接着获取布局得属性,判断是否有键盘弹出等逻辑,最后将//将DecorView添加到WindowManager中,然后通过sendShowMessage发送消息这些就会显示了。

看下sendShowMessage()得函数实现。

  

  private void sendShowMessage() {
        if (mShowMessage != null) {
            // Obtain a new message so this dialog can be re-used
            Message.obtain(mShowMessage).sendToTarget();
        }
    }


我们在Dialog得构造方法中初始化了sendShowMessage,所以sendShowMessage 发送的消息最终就会在ListenersHandler中被执行,看下ListenersHandler得实现。

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;
        }
    }
}


那么发送得消息是如何被接受得呢,看下setOnShowListener得实现就知道了。

public void setOnShowListener(OnShowListener listener) {
    if (listener != null) {
        mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
    } else {
        mShowMessage = null;
    }
}


在Dialog得mListenersHandler 构造了Message对象,并且在Dialog中发送showMessage得时候被mListenerHandler所接受。

看下hide()隐藏弹窗得源码实现

    
    

/**
     * Hide the dialog, but do not dismiss it.
     */
    public void hide() {
        if (mDecor != null) {
            mDecor.setVisibility(View.GONE);
        }
    }

直接就是通过mDecor 设置setVisibility(View.GONE)来隐藏。

看下cancel()方法得实现

   

 /**
     * Cancel the dialog.  This is essentially the same as calling {@link #dismiss()}, but it will
     * also call your {@link DialogInterface.OnCancelListener} (if registered).
     */
    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(mCancelMessage).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;
    }
}


 

看下dismiss()函数得实现

  

  /**
     * Dismiss this dialog, removing it from the screen. This method can be
     * invoked safely from any thread.  Note that you should not override this
     * method to do cleanup when the dialog is dismissed, instead implement
     * that in {@link #onStop}.
     */
    @Override
    public void dismiss() {
        if (Looper.myLooper() == mHandler.getLooper()) {
            dismissDialog();
        } else {
            mHandler.post(mDismissAction);
        }
    }


首先判断当前Loop是否在主线程中,由于mHnandler是在主线程中创建得,所以mHandler.getLooper返回得是主线程中创建得Looper对象,如果是则调用dismissDialog()方法,否则通过mHandler发送异步消息至主线程。这里得  mDismissAction是一个Runnable对象,看下定义。

private final Runnable mDismissAction = new Runnable() {
    public void run() {
        dismissDialog();
    }
};

所以最终还是执行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();
        }
    }

首先判断mDecor是否为空,或者是否显示,如果是则直接返回,接着调用mWindowManager.removeViewImmediate(mDecor);

这里得mDector 是是Dialog窗口得根布局,看下实现。

@Override
public void removeViewImmediate(View view) {
    mGlobal.removeView(view, true);
}


看到得是调用了 mGlobal.removeView(view, true);方法, mGlobal是WindowManagerGlabal得实例,所以看下实现。

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);
    }
}


通过view 查找到对应得index位置,然后获取到当前显示得curView,接着调用removeViewLocked来删除,看下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组件。接着看下die函数得实现。

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实现得,看下源码。

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 检查当前线程是否为主线程,如果不是,则抛出异常。接着调用dispatchDetachedFromWindow 来销毁Window中得变量属性等,然后执行destroyHardwareRenderer,销毁Window窗口得硬件渲染,由于View其实是通过draw实现绘制得,绘制得时候需要Canvas,画布对象,而Canvas内部保留一个Bitmap对象,通过操作Bitmap来显示出来,所以通过执行relayoutWindow来请求吃重新绘制,清空了View组件,去掉了Draw绘制得画面。

Dialog总结:

Dialog内部管理者一个Window对象,通过这个对象来实现界面得加载和显示逻辑。

构造AlertDialog用到了建造者模式。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值