看了下建造者模式,其中有一种写法如下
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用到了建造者模式。