设计模式
构建者模式
在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。简单示例如下 :
//显示基本的AlertDialog
private void showDialog(Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon);
builder.setTitle("Title");
builder.setMessage("Message");
builder.setPositiveButton("Button1",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button1");
}
});
builder.setNeutralButton("Button2",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button2");
}
});
builder.setNegativeButton("Button3",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button3");
}
});
builder.create().show(); // 构建AlertDialog, 并且显示
}
下面我们看看AlertDialog的相关源码 :
// AlertDialog
public class AlertDialog extends Dialog implements DialogInterface {
// Controller, 接受Builder成员变量P中的各个参数
private AlertController mAlert;
// 构造函数
protected AlertDialog(Context context, int theme) {
this(context, theme, true);
}
// 4 : 构造AlertDialog
AlertDialog(Context context, int theme, boolean createContextWrapper) {
super(context, resolveDialogTheme(context, theme), createContextWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = new AlertController(getContext(), this, getWindow());
}
// 实际上调用的是mAlert的setTitle方法
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
mAlert.setTitle(title);
}
// 实际上调用的是mAlert的setCustomTitle方法
public void setCustomTitle(View customTitleView) {
mAlert.setCustomTitle(customTitleView);
}
public void setMessage(CharSequence message) {
mAlert.setMessage(message);
}
// AlertDialog其他的代码省略
// ************ Builder为AlertDialog的内部类 *******************
public static class Builder {
// 1 : 存储AlertDialog的各个参数, 例如title, message, icon等.
private final AlertController.AlertParams P;
// 属性省略
/**
* Constructor using a context for this builder and the {@link AlertDialog} it creates.
*/
public Builder(Context context) {
this(context, resolveDialogTheme(context, 0));
}
public Builder(Context context, int theme) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, theme)));
mTheme = theme;
}
// Builder的其他代码省略 ......
// 2 : 设置各种参数
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
public Builder setIcon(int iconId) {
P.mIconId = iconId;
return this;
}
public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
P.mPositiveButtonText = text;
P.mPositiveButtonListener = listener;
return this;
}
public Builder setView(View view) {
P.mView = view;
P.mViewSpacingSpecified = false;
return this;
}
// 3 : 构建AlertDialog, 传递参数
public AlertDialog create() {
// 调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
// 5 : 将P中的参数应用的dialog中的mAlert对象中
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
}
}
可以看到,通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。我们看看apply函数的实现 :
public 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);
}
}
}
实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。在这里,Builder同时扮演了上文中提到的builder、ConcreteBuilder、Director的角色,简化了Builder模式的设计。
---------------------------------------------
外观模式
在开发过程中,Context是最重要的一个类型。它封装了很多重要的操作,比如startActivity()、sendBroadcast()等,几乎是开发者对应用操作的统一入口。Context是一个抽象类,它只是定义了抽象接口,真正的实现在ContextImpl类中。它就是今天我们要分析的外观类。
在应用启动时,首先会fork一个子进程,并且调用ActivityThread.main方法启动该进程。ActivityThread又会构建Application对象,然后和Activity、ContextImpl关联起来,然后再调用Activity的onCreate、onStart、onResume函数使Activity运行起来。我们看看下面的相关代码:
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 代码省略
// 1、创建并且加载Activity,调用其onCreate函数
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
// 2、调用Activity的onResume方法,使Activity变得可见
handleResumeActivity(r.token, false, r.isForward);
}
}
private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
// 代码省略
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
// 1、创建Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
// 2、创建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
// ***** 构建ContextImpl ******
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
appContext.setOuterContext(activity);
// 获取Activity的title
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mConfiguration);
// 3、Activity与context, Application关联起来
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);
// 代码省略
// 4、回调Activity的onCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state);
// 代码省略
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
}
return activity;
}
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
unscheduleGcIdler();
// 1、最终调用Activity的onResume方法
ActivityClientRecord r = performResumeActivity(token, clearHide);
// 代码省略
// 2、这里是重点,在这里使DecorView变得可见
if (r.window == null && !a.mFinished && willBeVisible) {
// 获取Window,即PhoneWindow类型
r.window = r.activity.getWindow();
// 3、获取Window的顶级视图,并且使它可见
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 4、获取WindowManager
ViewManager wm = a.getWindowManager();
// 5、构建LayoutParams参数
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
// 6、将DecorView添加到WindowManager中,最终的操作是通过WindowManagerService的addView来操作
wm.addView(decor, l);
}
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// 代码省略
}
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
ActivityClientRecord r = mActivities.get(token);
if (r != null && !r.activity.mFinished) {
try {
// 代码省略
// 执行onResume
r.activity.performResume();
// 代码省略
} catch (Exception e) {
}
}
return r;
}
Activity启动之后,Android给我们提供了操作系统服务的统一入口,也就是Activity本身。这些工作并不是Activity自己实现的,而是将操作委托给Activity父类ContextThemeWrapper的mBase对象,这个对象的实现类就是ContextImpl ( 也就是performLaunchActivity方法中构建的ContextImpl )。
class ContextImpl extends Context {
private final static String TAG = "ApplicationContext";
private final static boolean DEBUG = false;
private final static boolean DEBUG_ICONS = false;
private static final Object sSync = new Object();
private static AlarmManager sAlarmManager;
private static PowerManager sPowerManager;
private static ConnectivityManager sConnectivityManager;
private AudioManager mAudioManager;
LoadedApk mPackageInfo;
private Resources mResources;
private PackageManager mPackageManager;
private NotificationManager mNotificationManager = null;
private ActivityManager mActivityManager = null;
// 代码省略
@Override
public void sendBroadcast(Intent intent) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, false, false);
} catch (RemoteException e) {
}
}
@Override
public void startActivity(Intent intent) {
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1);
}
@Override
public ComponentName startService(Intent service) {
try {
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()));
if (cn != null && cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
}
return cn;
} catch (RemoteException e) {
return null;
}
}
@Override
public String getPackageName() {
if (mPackageInfo != null) {
return mPackageInfo.getPackageName();
}
throw new RuntimeException("Not supported in system context");
}
}
可以看到,ContextImpl内部有很多xxxManager类的对象,也就是我们上文所说的各种子系统的角色。ContextImpl内部封装了一些系统级别的操作,有的子系统功能虽然没有实现,但是也提供了访问该子系统的接口,比如获取ActivityManager的getActivityManager方法。
比如我们要启动一个Activity的时候,我们调用的是startActivity方法,这个功能的内部实现实际上是Instrumentation完成的。ContextImpl封装了这个功能,使得用户根本不需要知晓Instrumentation相关的信息,直接使用startActivity即可完成相应的工作。其他的子系统功能也是类似的实现,比如启动Service和发送广播内部使用的是ActivityManagerNative等。ContextImpl的结构图如下 :
context
外观模式非常的简单,只是封装了子系统的操作,并且暴露接口让用户使用,避免了用户需要与多个子系统进行交互,降低了系统的耦合度、复杂度。如果没有外观模式的封装,那么用户就必须知道各个子系统的相关细节,子系统之间的交互必然造成纠缠不清的关系,影响系统的稳定性、复杂度。
-------------------------------------------------------------------------
模板方法模式
在Android中,使用了模板方法且为我们熟知的一个典型类就是AsyncTask了,关于AsyncTask的更详细的分析请移步Android中AsyncTask的使用与源码分析,我们这里只分析在该类中使用的模板方法模式。
在使用AsyncTask时,我们都有知道耗时的方法要放在doInBackground(Params... params)中,在doInBackground之前如果还想做一些类似初始化的操作可以写在onPreExecute方法中,当doInBackground方法执行完成后,会执行onPostExecute方法,而我们只需要构建AsyncTask对象,然后执行execute方法即可。我们可以看到,它整个执行过程其实是一个框架,具体的实现都需要子类来完成。而且它执行的算法框架是固定的,调用execute后会依次执行onPreExecute,doInBackground,onPostExecute,当然你也可以通过onProgressUpdate来更新进度。我们可以简单的理解为如下图的模式 :
async-flow
下面我们看源码,首先我们看执行异步任务的入口, 即execute方法 :
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
可以看到execute方法(为final类型的方法)调用了executeOnExecutor方法,在该方法中会判断该任务的状态,如果不是PENDING状态则抛出异常,这也解释了为什么AsyncTask只能被执行一次,因此如果该任务已经被执行过的话那么它的状态就会变成FINISHED。继续往下看,我们看到在executeOnExecutor方法中首先执行了onPreExecute方法,并且该方法执行在UI线程。然后将params参数传递给了mWorker对象的mParams字段,然后执行了exec.execute(mFuture)方法。
mWorker和mFuture又是什么呢?其实mWorker只是实现了Callable接口,并添加了一个参数数组字段,关于Callable和FutureTask的资料请参考Java中的Runnable、Callable、Future、FutureTask的区别与示例,我们挨个来分析吧,跟踪代码我们可以看到,这两个字段都是在构造函数中初始化。
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
final Result result = get();
postResultIfNotInvoked(result);
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
}
};
}
简单的说就是mFuture就包装了这个mWorker对象,会调用mWorker对象的call方法,并且将之返回给调用者。
关于AsyncTask的更详细的分析请移步Android中AsyncTask的使用与源码分析,我们这里只分析模板方法模式。总之,call方法会在子线程中调用,而在call方法中又调用了doInBackground方法,因此doInBackground会执行在子线程。doInBackground会返回结果,最终通过postResult投递给UI线程。 我们再看看postResult的实现 :
private Result postResult(Result result) {
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可以看到,postResult就是把一个消息( msg.what == MESSAGE_POST_RESULT)发送给sHandler,sHandler类型为InternalHandler类型,当InternalHandler接到MESSAGE_POST_RESULT类型的消息时就会调用result.mTask.finish(result.mData[0])方法。我们可以看到result为AsyncTaskResult类型,源码如下 :
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
可以看到mTask就是AsyncTask对象,调用AsyncTask对象的finish方法时又调用了onPostExecute,这个时候整个执行过程就完成了。 总之,execute方法内部封装了onPreExecute, doInBackground, onPostExecute这个算法框架,用户可以根据自己的需求来在覆写这几个方法,使得用户可以很方便的使用异步任务来完成耗时操作,又可以通过onPostExecute来完成更新UI线程的工作。
另一个比较好的模板方法示例就是Activity的声明周期函数,例如Activity从onCreate、onStart、onResume这些程式化的执行模板,这就是一个Activity的模板方法。
-------------------------------------------------------------
策略模式
日常的Android开发中经常会用到动画,Android中最简单的动画就是Tween Animation了,当然帧动画和属性动画也挺方便的,但是基本原理都类似,毕竟动画的本质都是一帧一帧的展现给用户的,只不要当fps小于60的时候,人眼基本看不出间隔,也就成了所谓的流畅动画。(注:属性动画是3.0以后才有的,低版本可采用NineOldAndroids来兼容。而动画的动态效果往往也取决于插值器Interpolator不同,我们只需要对Animation对象设置不同的Interpolator就可以实现不同的效果,这是怎么实现的呢?
首先要想知道动画的执行流程,还是得从View入手,因为Android中主要针对的操作对象还是View,所以我们首先到View中查找,我们找到了View.startAnimation(Animation animation)这个方法。
public void startAnimation(Animation animation) {
//初始化动画开始时间
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
//对View设置动画
setAnimation(animation);
//刷新父类缓存
invalidateParentCaches();
//刷新View本身及子View
invalidate(true);
}
考虑到View一般不会单独存在,都是存在于某个ViewGroup中,所以google使用动画绘制的地方选择了在ViewGroup中的drawChild(Canvas canvas, View child, long drawingTime)方法中进行调用子View的绘制。
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
再看下View中的draw(Canvas canvas, ViewGroup parent, long drawingTime)方法中是如何调用使用Animation的
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
//...
//查看是否需要清除动画信息
final int flags = parent.mGroupFlags;
if ((flags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) == ViewGroup.FLAG_CLEAR_TRANSFORMATION) {
parent.getChildTransformation().clear();
parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
//获取设置的动画信息
final Animation a = getAnimation();
if (a != null) {
//绘制动画
more = drawAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
} else {
//...
}
}
可以看出在父类调用View的draw方法中,会先判断是否设置了清除到需要做该表的标记,然后再获取设置的动画的信息,如果设置了动画,就会调用View中的drawAnimation方法,具体如下:
private boolean drawAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
Transformation invalidationTransform;
final int flags = parent.mGroupFlags;
//判断动画是否已经初始化过
final boolean initialized = a.isInitialized();
if (!initialized) {
a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());
a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
onAnimationStart();
}
//判断View是否需要进行缩放
final Transformation t = parent.getChildTransformation();
boolean more = a.getTransformation(drawingTime, t, 1f);
if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
if (parent.mInvalidationTransformation == null) {
parent.mInvalidationTransformation = new Transformation();
}
invalidationTransform = parent.mInvalidationTransformation;
a.getTransformation(drawingTime, invalidationTransform, 1f);
} else {
invalidationTransform = t;
}
if (more) {
//根据具体实现,判断当前动画类型是否需要进行调整位置大小,然后刷新不同的区域
if (!a.willChangeBounds()) {
//...
}else{
//...
}
}
return more;
}
其中主要的操作是动画始化、动画操作、界面刷新。动画的具体实现是调用了Animation中的getTransformation(long currentTime, Transformation outTransformation,float scale)方法。
public boolean getTransformation(long currentTime, Transformation outTransformation,
float scale) {
mScaleFactor = scale;
return getTransformation(currentTime, outTransformation);
}
在上面的方法中主要是获取缩放系数和调用Animation.getTransformation(long currentTime, Transformation outTransformation)来计算和应用动画效果。
Interpolator mInterpolator; //成员变量
public boolean getTransformation(long currentTime, Transformation outTransformation) {
//计算处理当前动画的时间点...
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
//后续处理,以此来应用动画效果...
applyTransformation(interpolatedTime, outTransformation);
return mMore;
}
很容易发现Android系统中在处理动画的时候会调用插值器中的getInterpolation(float input)方法来获取当前的时间点,依次来计算当前变化的情况。这就不得不说到Android中的插值器Interpolator,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator(减速插值器:动画越来越慢)等,如图:
url
由于初期比较旧的版本采用的插值器是TimeInterpolator抽象,google采用了多加一层接口继承来实现兼容也不足为怪了。很显然策略模式在这里作了很好的实现,Interpolator就是处理动画时间的抽象,LinearInterpolator、CycleInterpolator等插值器就是具体的实现策略。插值器与Animation的关系图如下:
url
这里以LinearInterpolator和CycleInterpolator为例:
LinearInterpolator
public float getInterpolation(float input) {
return input;
}
CycleInterpolator
public float getInterpolation(float input) {
return (float)(Math.sin(2 * mCycles * Math.PI * input));
}
可以看出LinearInterpolator中计算当前时间的方法是做线性运算,也就是返回input*1,所以动画会成直线匀速播放出来,而CycleInterpolator是按照正弦运算,所以动画会正反方向跑一次,其它插值器依次类推。不同的插值器的计算方法都有所差别,用户设置插值器以实现动画速率的算法替换。
代理模式
直观来说,Binder是Android中的一个类,它继承了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在linux中没有;从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,etc)和相应ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
Binder一个很重要的作用是:将客户端的请求参数通过Parcel包装后传到远程服务端,远程服务端解析数据并执行对应的操作,同时客户端线程挂起,当服务端方法执行完毕后,再将返回结果写入到另外一个Parcel中并将其通过Binder传回到客户端,客户端接收到返回数据的Parcel后,Binder会解析数据包中的内容并将原始结果返回给客户端,至此,整个Binder的工作过程就完成了。由此可见,Binder更像一个数据通道,Parcel对象就在这个通道中跨进程传输,至于双方如何通信,这并不负责,只需要双方按照约定好的规范去打包和解包数据即可。
为了更好地说明Binder,这里我们先手动实现了一个Binder。为了使得逻辑更清晰,这里简化一下,我们来模拟一个银行系统,这个银行提供的功能只有一个:即查询余额,只有传递一个int的id过来,银行就会将你的余额设置为id*10,满足下大家的发财梦。
先定义一个Binder接口
package com.ryg.design.manualbinder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
public interface IBank extends IInterface {
static final String DESCRIPTOR = "com.ryg.design.manualbinder.IBank";
static final int TRANSACTION_queryMoney = (IBinder.FIRST_CALL_TRANSACTION + 0);
public long queryMoney(int uid) throws RemoteException;
}
2.创建一个Binder并实现这个上述接口
package com.ryg.design.manualbinder;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
public class BankImpl extends Binder implements IBank {
public BankImpl() {
this.attachInterface(this, DESCRIPTOR);
}
public static IBank asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBank))) {
return ((IBank) iin);
}
return new BankImpl.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_queryMoney: {
data.enforceInterface(DESCRIPTOR);
int uid = data.readInt();
long result = this.queryMoney(uid);
reply.writeNoException();
reply.writeLong(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
@Override
public long queryMoney(int uid) throws RemoteException {
return uid * 10l;
}
private static class Proxy implements IBank {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public long queryMoney(int uid) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
long result;
try {
data.writeInterfaceToken(DESCRIPTOR);
data.writeInt(uid);
mRemote.transact(TRANSACTION_queryMoney, data, reply, 0);
reply.readException();
result = reply.readLong();
} finally {
reply.recycle();
data.recycle();
}
return result;
}
}
}
ok,到此为止,我们的Binder就完成了,这里只要创建服务端和客户端,二者就能通过我们的Binder来通信了。这里就不做这个示例了,我们的目的是分析代理模式在Binder中的使用。
我们看上述Binder的实现中,有一个叫做“Proxy”的类,它的构造方法如下:
Proxy(IBinder remote) {
mRemote = remote;
}
Proxy类接收一个IBinder参数,这个参数实际上就是服务端Service中的onBind方法返回的Binder对象在客户端重新打包后的结果,因为客户端无法直接通过这个打包的Binder和服务端通信,因此客户端必须借助Proxy类来和服务端通信,这里Proxy的作用就是代理的作用,客户端所有的请求全部通过Proxy来代理,具体工作流程为:Proxy接收到客户端的请求后,会将客户端的请求参数打包到Parcel对象中,然后将Parcel对象通过它内部持有的Ibinder对象传送到服务端,服务端接收数据、执行方法后返回结果给客户端的Proxy,Proxy解析数据后返回给客户端的真正调用者。很显然,上述所分析的就是典型的代理模式。至于Binder如何传输数据,这涉及到很底层的知识,这个很难搞懂,但是数据传输的核心思想是共享内存。
责任链模式
Android中关于责任链模式比较明显的体现就是在事件分发过程中对事件的投递,其实严格来说,事件投递的模式并不是严格的责任链模式,但是其是责任链模式的一种变种体现,在ViewGroup中对事件处理者的查找方式如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 省略两行代码…………
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
// 省略N行代码…………
/*
* 如果事件未被取消并未被拦截
*/
if (!canceled && !intercepted) {
/*
* 如果事件为起始事件
*/
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// 省掉部分逻辑…………
final int childrenCount = mChildrenCount;
/*
* 如果TouchTarget为空并且子元素不为0
*/
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
final View[] children = mChildren;
final boolean customOrder = isChildrenDrawingOrderEnabled();
/*
* 遍历子元素
*/
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ?
getChildDrawingOrder(childrenCount, i) : i;
final View child = children[childIndex];
/*
* 如果这个子元素无法接收Pointer Event或这个事件点压根就没有落在子元素的边界范围内
*/
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
// 那么就跳出该次循环继续遍历
continue;
}
// 找到Event该由哪个子元素持有
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
/*
* 投递事件执行触摸操作
* 如果子元素还是一个ViewGroup则递归调用重复此过程
* 如果子元素是一个View那么则会调用View的dispatchTouchEvent并最终由onTouchEvent处理
*/
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = childIndex;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
/*
* 如果发现没有子元素可以持有该次事件
*/
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// 省去不必要代码……
}
// 省去一行代码……
return handled;
}
再来看看dispatchTransformedTouchEvent方法是如何调度子元素dispatchTouchEvent方法的:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
/*
* 如果事件被取消
*/
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
/*
* 如果没有子元素
*/
if (child == null) {
// 那么就直接调用父类的dispatchTouchEvent注意这里的父类终会为View类
handled = super.dispatchTouchEvent(event);
} else {
// 如果有子元素则传递cancle事件
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
/*
* 计算即将被传递的点的数量
*/
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
/*
* 如果事件木有相应的点那么就丢弃该次事件
*/
if (newPointerIdBits == 0) {
return false;
}
// 声明临时变量保存坐标转换后的MotionEvent
final MotionEvent transformedEvent;
/*
* 如果事件点的数量一致
*/
if (newPointerIdBits == oldPointerIdBits) {
/*
* 子元素为空或子元素有一个单位矩阵
*/
if (child == null || child.hasIdentityMatrix()) {
/*
* 再次区分子元素为空的情况
*/
if (child == null) {
// 为空则调用父类dispatchTouchEvent
handled = super.dispatchTouchEvent(event);
} else {
// 否则尝试获取xy方向上的偏移量(如果通过scrollTo或scrollBy对子视图进行滚动的话)
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
// 将MotionEvent进行坐标变换
event.offsetLocation(offsetX, offsetY);
// 再将变换后的MotionEvent传递给子元素
handled = child.dispatchTouchEvent(event);
// 复位MotionEvent以便之后再次使用
event.offsetLocation(-offsetX, -offsetY);
}
// 如果通过以上的逻辑判断当前事件被持有则可以直接返回
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
/*
* 下述雷同不再累赘
*/
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
transformedEvent.recycle();
return handled;
}
ViewGroup事件投递的递归调用就类似于一条责任链,一旦其寻找到责任者,那么将由责任者持有并消费掉该次事件,具体的体现在View的onTouchEvent方法中返回值的设置(这里介于篇幅就不具体介绍ViewGroup对事件的处理了),如果onTouchEvent返回false那么意味着当前View不会是该次事件的责任人将不会对其持有,如果为true则相反,此时View会持有该事件并不再向外传递。
命令模式
Command接口中定义了一个execute方法,客户端通过Invoker调用命令操作再来调用Recriver执行命令;把对Receiver的操作请求封装在具体的命令中,使得命令发起者和命令接收者解耦。 以Android中大家常见的Runnable为例:客户端只需要new Thread(new Runnable(){}).start()就开始执行一系列相关的请求,这些请求大部分都是实现Runnable接口的匿名类。 【O_o 模式就在我们身边~】
命令接口Runnable接口定义如下:
package java.lang;
/**
* Represents a command that can be executed. Often used to run code in a
* different {@link Thread}.
*/
public interface Runnable {
/**
* Starts executing the active part of the class' code. This method is
* called when a thread is started that has been created with a class which
* implements {@code Runnable}.
*/
public void run();
}
调用者Thread源码如下(省略部分代码): Tips:命令模式在这里本来不需要继承Runnable接口,但为了方便性等,继承了Runnable接口实现了run方法,这个run是Thread自身的运行run的方法,而不是命令Runnable的run。
public class Thread implements Runnable {
//省略部分无关代码...
/* some of these are accessed directly by the VM; do not rename them */
volatile VMThread vmThread;
volatile ThreadGroup group;
volatile boolean daemon;
volatile String name;
volatile int priority;
volatile long stackSize;
Runnable target;
private static int count = 0;
public synchronized void start() {
if (hasBeenStarted) {
throw new IllegalThreadStateException("Thread already started."); // TODO Externalize?
}
hasBeenStarted = true;
VMThread.create(this, stackSize);
}
//省略部分代码...
}
上面可以看到执行start()方法的时候实际执行了VMThread.create(this, stackSize)方法;create是VMThread的本地方法,其JNI实现在 android/dalvik/vm/native/java_lang_VMThread.cpp 中的 Dalvik_java_lang_VMThread_create方法,如下:
static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult)
{
Object* threadObj = (Object*) args[0];
s8 stackSize = GET_ARG_LONG(args, 1);
/* copying collector will pin threadObj for us since it was an argument */
dvmCreateInterpThread(threadObj, (int) stackSize);
RETURN_VOID();
}
而dvmCreateInterpThread的实现在Thread.app中,如下:
bool dvmCreateInterpThread(Object* threadObj, int reqStackSize){
Thread* self = dvmThreadSelf();
Thread* newThread = allocThread(stackSize);
newThread->threadObj = threadObj;
Object* vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
pthread_t threadHandle;
int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread);
dvmLockThreadList(self);
assert(newThread->status == THREAD_STARTING);
newThread->status = THREAD_VMWAIT;
pthread_cond_broadcast(&gDvm.threadStartCond);
dvmUnlockThreadList();
}
static Thread* allocThread(int interpStackSize)
{
Thread* thread;
thread = (Thread*) calloc(1, sizeof(Thread));
thread->status = THREAD_INITIALIZING;
}
这里是底层代码,简单介绍下就行了: 第4行通过调用 allocThread 创建一个名为newThread的dalvik Thread并设置一些属性,第5行设置其成员变量threadObj为传入的Android Thread,这样dalvik Thread就与Android Thread对象关联起来了;第7行然后创建一个名为vmThreadObj的VMThread对象,设置其成员变量vmData为前面创建的newThread,设置 Android Thread threadObj的成员变量vmThread为这个vmThreadObj,这样Android Thread通过VMThread的成员变量vmData就和dalvik Thread关联起来了。
接下来在12行通过pthread_create创建pthread线程,并让这个线程start,这样就会进入该线程的thread entry运行,下来我们来看新线程的thread entry方法 interpThreadStart,同样只列出关键的地方:
//pthread entry function for threads started from interpreted code.
static void* interpThreadStart(void* arg){
Thread* self = (Thread*) arg;
std::string threadName(dvmGetThreadName(self));
setThreadName(threadName.c_str());
//Finish initializing the Thread struct.
dvmLockThreadList(self);
prepareThread(self);
while (self->status != THREAD_VMWAIT)
pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
dvmUnlockThreadList();
/*
* Add a JNI context.
*/
self->jniEnv = dvmCreateJNIEnv(self);
//修改状态为THREAD_RUNNING
dvmChangeStatus(self, THREAD_RUNNING);
//执行run方法
Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run];
JValue unused;
ALOGV("threadid=%d: calling run()", self->threadId);
assert(strcmp(run->name, "run") == 0);
dvmCallMethod(self, run, self->threadObj, &unused);
ALOGV("threadid=%d: exiting", self->threadId);
//移出线程并释放资源
dvmDetachCurrentThread();
return NULL;
}
//Finish initialization of a Thread struct.
static bool prepareThread(Thread* thread){
assignThreadId(thread);
thread->handle = pthread_self();
thread->systemTid = dvmGetSysThreadId();
setThreadSelf(thread);
return true;
}
//Explore our sense of self. Stuffs the thread pointer into TLS.
static void setThreadSelf(Thread* thread){
int cc;
cc = pthread_setspecific(gDvm.pthreadKeySelf, thread);
}
在新线程的interpThreadStart方法中,首先设置线程的名字,然后调用prepareThread设置线程id以及其它一些属性,其中调用了setThreadSelf将新dalvik Thread自身保存在TLS中,这样之后就能通过dvmThreadSelf方法从TLS中获取它。然后在29行处修改状态为THREAD_RUNNING,并在36行调用对应Android Thread的run()方法,其中调用了Runnable的run方法,运行我们自己的代码。 绕这么深才执行到我们的run方法,累不累? v_v
/**
* Calls the <code>run()</code> method of the Runnable object the receiver
* holds. If no Runnable is set, does nothing.
* @see Thread#start
*/
public void run() {
if (target != null) {
target.run();
}
}
到此我们已经完成一次命令调用,至于底层run调用完毕后续执行代码,读者可以自行跟进看看~~~
桥接模式
在Android中也运用到了Bridge模式,我们使用很多的ListView和BaseAdpater其实就是Bridge模式的运行,很多人会问这个不是Adapter模式,接下来根据源码来分析。
首先ListAdapter.java:
public interface ListAdapter extends Adapter{
//继承自Adapter,扩展了自己的两个实现方法
public boolean areAllItemsEnabled();
boolean isEnabled(int position);
}
这里先来看一下父类AdapterView。
public abstract class AdapterView<T extends Adapter> extends ViewGroup {
//这里需要一个泛型的Adapter
public abstract T getAdapter();
public abstract void setAdapter(T adapter);
}
接着来看ListView的父类AbsListView,继承自AdapterView
public abstract class AbsListView extends AdapterView<ListAdapter>
//继承自AdapterView,并且指明了T为ListAdapter
/**
* The adapter containing the data to be displayed by this view
*/
ListAdapter mAdapter;
//代码省略
//这里实现了setAdapter的方法,实例了对实现化对象的引用
public void setAdapter(ListAdapter adapter) {
//这的adapter是从子类传入上来,也就是listview,拿到了具体实现化的对象
if (adapter != null) {
mAdapterHasStableIds = mAdapter.hasStableIds();
if (mChoiceMode != CHOICE_MODE_NONE && mAdapterHasStableIds &&
mCheckedIdStates == null) {
mCheckedIdStates = new LongSparseArray<Integer>();
}
}
if (mCheckStates != null) {
mCheckStates.clear();
}
if (mCheckedIdStates != null) {
mCheckedIdStates.clear();
}
}
大家都知道,构建一个listview,adapter中最重要的两个方法,getCount()告知数量,getview()告知具体的view类型,接下来看看AbsListView作为一个视图的集合是如何来根据实现化对象adapter来实现的具体的view呢?
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//省略代码,
//这里在加入window的时候,getCount()确定了集合的个数
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
}
}
接着来看
View obtainView(int position, boolean[] isScrap) {
//代码省略
?//这里根据位置显示具体的view,return的child是从持有的实现对象mAdapter里面的具体实现的
?//方法getview来得到的。
final View child = mAdapter.getView(position, scrapView, this);
//代码省略
return child;
}
接下来在ListView中,onMeasure调用了obtainView来确定宽高,在扩展自己的方法来排列这些view。知道了
这些以后,我们来画一个简易的UML图来看下:
uml
对比下GOF的上图,是不是发现很像呢?实际上最开始研究Adapter模式的时候,越看越不对啊,于是整理结构,画了UML发现这更像是一个bridge模式,那时候对设计模式也是模模糊糊的,于是静下来研究。抽象化的角色一个视图的集合AdapterView,它扩展了AbsListView,AbsSpinner,接下来他们分别扩展了ListView,GridView,Spinner,Gallery,用不同方式来展现这些ItemViews,我们继续扩展类似ListView的PulltoRefreshView等等。而实现化角色Adapter扩展了ListAdpater,SpinnerAdapter,接着具体的实现化角色BaseAdapter实现了他们,我们通过继承BaseAdapter又实现了我们各式各样的ItemView。
---------------------------------------------------------------------------
原型模式
在Android源码中,我们以熟悉的Intent来分析源码中的原型模式。简单示例如下 :
Uri uri = Uri.parse("smsto:0800000123");
Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri);
shareIntent.putExtra("sms_body", "The SMS text");
Intent intent = (Intent)shareIntent.clone() ;
startActivity(intent);
可以看到,我们通过shareIntent.clone方法拷贝了一个对象intent, 然后执行startActivity(intent), 随即就进入了短信页面,号码为0800000123,文本内容为The SMS text,即这些内容都与shareIntent一致。
@Override
public Object clone() {
return new Intent(this);
}
/**
* Copy constructor.
*/
public Intent(Intent o) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
this.mFlags = o.mFlags;
if (o.mCategories != null) {
this.mCategories = new ArraySet<String>(o.mCategories);
}
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
}
if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds);
}
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
if (o.mClipData != null) {
this.mClipData = new ClipData(o.mClipData);
}
}
可以看到,clone方法实际上在内部调用了new Intent(this); 这就和C++中的拷贝构造函数完全一致了,而且是深拷贝。由于该模式比较简单,就不做太多说明。