Builder建造者模式
Buider模式是一个创建型模式,用来构建复杂对象的。构建对象一般是通过构造器完成的,但是如果一个对象的参数非常多,那么构造器的参数列也会非常长,不利于代码维护管理。还有另外一种方式,就是通过setXXX的方式设置属性,但是对象在setXXX完毕之前都处于一种不一致的状态。那么,构建器模式就是用来解决这个问题的。构建器模式的另一个优点就是隐藏对象的实际构建行为,将复杂的构建过程进行封装。
来个小例子:
package com.example.a11069824.Threader;
public class JavaBean {
private int paramA;
private int paramB;
private int paramC;
private int paramD;
private int paramE;
private JavaBean() {
}
private void setParamA(int paramA) {
this.paramA = paramA;
}
private void setParamB(int paramB) {
this.paramB = paramB;
}
private void setParamC(int paramC) {
this.paramC = paramC;
}
private void setParamD(int paramD) {
this.paramD = paramD;
}
private void setParamE(int paramE) {
this.paramE = paramE;
}
public static class Builder {
private JavaBean mJavaBean;
public Builder() {
mJavaBean = new JavaBean();
}
public Builder buildA(int a) {
mJavaBean.setParamA(a);
return this;
}
public Builder buildB(int a) {
mJavaBean.setParamB(a);
return this;
}
public Builder buildC(int a) {
mJavaBean.setParamC(a);
return this;
}
public Builder buildD(int a) {
mJavaBean.setParamD(a);
return this;
}
public Builder buildE(int a) {
mJavaBean.setParamE(a);
return this;
}
}
}
外部用户无需知道JavaBean的具体构建过程,只需通过Builder就可以得到JavaBean对象,并且能够通过Builder提供的一系列方法精细控制JavaBean的属性。
Android源码中的建造者模式
开发过程中,经常用到AlertDialog,AlertDialog的创建其实就是通过AlertDialog.Builder来构建的,这就是建造者模式的具体应用。
AlertDialog dialog = new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_launcher_background)
.setTitle("对话框")
.setMessage("Tips")
.create();
dialog.show();
构建Dialog的过程非常的清爽,代码写起来也比较爽。
深入源码看下吧~
public static class Builder {
private final AlertController.AlertParams P;
public Builder setTitle(@StringRes int titleId) {
P.mTitle = P.mContext.getText(titleId);
return this;
}
/**
* Set the title displayed in the {@link Dialog}.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
}
Builder的作用就是将用户设置的一些列参数保存在对象P中。
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
P.apply(dialog.mAlert);
return dialog
}
Builder.create其实就是new了一个Dialog对象,然后将P中保存的参数值应用到Dialog对象。
最后再看下,Dialog的show方法:
mDecor = mWindow.getDecorView();
WindowManager.LayoutParams l = mWindow.getAttributes();
boolean restoreSoftInputMode = false;
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
l.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
restoreSoftInputMode = true;
}
mWindowManager.addView(mDecor, l);
获取Dialog对应Window的DecorView,然后将DecorView通过WindowManager的addView方法显示出来。
WMS研究
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
Dialog的构造方法中,通过getSystemService的方式获取WindowManager,然后新建一个PhoneWindow,最后通过setWindowManager的方式将WindowManager与PhoneWindow关联起来。WindowManager的实现类是WindowManagerImpl。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerImpl其实不是干活的类,只是一个委托类,真正干活的是WindowManagerGlobal。
看WindowManagerGlobal的addView方法:
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
关键步骤
1、构建ViewRootImpl
2、将params设置给view
3、将view、params、root保存
4、调用ViewRootImpl的setView方法
其中,第四步是View能够显示出来的关键点。
ViewRootImpl的构造器
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mThread = Thread.currentThread();
}
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
WindowSession是一个远程binder对象,实际上是用来跟WindowManagerService进行通信的。
mThread用来保存当前线程,用于实现只能在主线程更新UI的功能。