1.描述
《Java与模式》描述:模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
顾名思义,模板模式就是给你一个模板(声明的抽象方法),由你实现的子类来填充这些方法,然后执行start,这个类就会按照固定的套路执行这些方法,最后得到一个结果,完成设定的目标。
所以使用模板模式是对一些类似任务的概括,提取出固定的步骤,封装成一个模板,你只需实现这些固定步骤的具体方法,就可以非常统一地完成这些任务。
2.SimpleTask
在电话的相关的模式中一定也是用到了模板方法模式,第一个就是非常常见的AsyncTask,它用来完成的固定套路的就是异步地执行一个任务,并能够将结果返回到主线程。
当然在InCallUI中我也实现了一个SimpleTask,目的与AsyncTask类似,异步地完成一个轻量级的任务,与AsyncTask的不同是增加了超时的操作,并能够更好的管理每个任务,也防止过多相似的任务阻塞我们的线程池。
2.1首先是提供的抽象方法:
/**
* WorkThread
* @return
*/
protected abstract Result doInBackground();
/**
* MainThread
*/
protected void onPreExecute() {
}
/**
* MainThread
* @param result
*/
@SuppressWarnings({"UnusedDeclaration"})
protected void onPostExecute(Result result) {
}
这个类提供了三个抽象方法:
(1) doInBackground():看名字就知道,具体的后台任务要在这里实现,它可以有一个返回值:Result,会传到onPostExecute中。
(2) onPostExecute():这是回到主线程执行的方法,它可以得到后台任务的结果。
(3) onPreExecute():在主线程执行,就是完成后台任务开始之前的一些准备工作在这里执行。
2.2 执行的过程:
这个方法的执行流程是怎么样的呢?套路是如何进行的呢?
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public SimpleTask() {
mAsyncTask = new AsyncTask<Void, Void, Result>() {
@Override
protected void onPreExecute() {
SimpleTask.this.onPreExecute();
}
@Override
protected Result doInBackground(Void... params) {
return SimpleTask.this.doInBackground();
}
@Override
protected void onPostExecute(Result result) {
SimpleTask.this.onPostExecute(result);;
SimpleTask.this.finish();
}
@Override
protected void onCancelled(Result result) {
SimpleTask.this.finish();
}
};
}
实际上SimpleTask还是一个AsyncTask,不重复制造轮子还是一个模板模式,相比AsyncTask,我们多的是finish,至于AsyncTask如何执行,如何在主副线程切换可以参考http://blog.csdn.net/xlc2845321/article/details/60132422
相比AsyncTask多的是任务管理和超时放弃,所以看它的run方法:
/**
* MainThread
* @param timeOut
* @param isNeedPost If is time out, is need to run onPostExecute()
* @param defaultResult If need to run onPostExecute(), offer the default Result
*/
public void run(long timeOut, boolean isNeedPost, Result defaultResult) {
this.isNeedPost = isNeedPost;
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_TIME_OUT,
new SimpleTaskResult<Result>(this, defaultResult));
getHandler().sendMessageDelayed(message, timeOut);
run();
}
超时就是向主线程抛了一个timeout的message,如果我们的任务没有能在timeout的时间内完成,将执行超时任务:
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
SimpleTaskResult<?> result = (SimpleTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_TIME_OUT:
result.mTask.timeOut(result.mData);
break;
}
}
}
private void timeOut(Result result) {
if (!isCancelled() && mStatus != Status.FINISHED) {
cancel(true);
if (isNeedPost) {
onPostExecute(result);
}
Log.i(TAG, "TimeOut: " + mTag + " " + mStartTime
+ " used time:" + (System.currentTimeMillis() - mStartTime));
mStatus = Status.FINISHED;
}
}
超时后第一件事:取消后台任务。二,如果设置了isNeedPost,使用给的默认的result执行onPostExecute,最后将这个任务标记为FINISHED。
如何实现任务管理?首先需要一个tag
private String mTag = null;
如果没有这个tag在run的时候会抛出异常。
private static HashMap<String, SimpleTask> mRunningTasks = new HashMap<String, SimpleTask>();
private static HashMap<String, ArrayDeque<SimpleTask>> mPendingTasks = new HashMap<String, ArrayDeque<SimpleTask>>();
/**
* MainThread
*/
private void execute() {
if (mRunningTasks.get(mTag) == null) {
insertTask(mTag, this);
mStatus = Status.RUNNING;
mAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
} else {
insertPendingTask(mTag, this);
mStatus = Status.PENDING;
}
}
维护两个HashMap,分别代表正在执行的任务和即将执行的任务,为了更好地控制后台,相同tag的任务只有一个能够在执行中,否则的话将会进入mPendingTasks中排队。
如何执行mPendingTasks中的任务?
private void finish() {
removeTask(mTag);
Log.i(TAG, "Finish: " + mTag + " " + mStartTime
+ " used time:" + (System.currentTimeMillis() - mStartTime));
mStatus = Status.FINISHED;
}
/**
* Only the task with the tag has finished, than remove task
* Only called by finish() in MainThread
* @param tag
*/
private static void removeTask(String tag) {
if (mRunningTasks.containsKey(tag)) {
mRunningTasks.remove(tag);
}
scheduleNext(tag);
}
/**
* Last task with the tag has finished, run next task with the tag in mPendingTasks
* Only be called by removeTask() in MainThread
* @param tag
*/
private static void scheduleNext(String tag) {
ArrayDeque<SimpleTask> taskQueue = mPendingTasks.get(tag);
if (taskQueue != null) {
if (taskQueue.size() > 0) {
SimpleTask simpleTask = taskQueue.poll();
if (simpleTask != null && simpleTask.getStatus() == Status.PENDING) {
simpleTask.execute();
} else {
scheduleNext(tag);
}
} else {
mPendingTasks.remove(tag);
}
}
}
答案就在当当前任务执行完时,从mRunningTasks删除当前任务时,用当前任务的tagscheduleNext()。
还有一些线程同步与安全的细节,现在是通过人为地保证一些run和start的方法是在主线程执行的来保证的,其实还是有改进的空间。
2.3使用:
new SimpleTask<Boolean>() {
@Override
protected Boolean doInBackground() {
return Utils.isPackageAvailable(PACKAGE_NAME_NOTES);
}
@Override
protected void onPostExecute(Boolean isAvailable) {
final CallToolsUi ui = getUi();
if (ui == null) {
return;
}
if (isAvailable) {
ui.showNotesButton(true);
ui.showMessageButton(false);
} else {
ui.showNotesButton(false);
ui.showMessageButton(true);
}
}
}.withTag(SimpleTask.TASK_QUERY_PACKAGE_IS_AVAILABLE).run(3000, false, false);
使用起来还是非常简单,实现其中的抽象方法,然后run就行了,复杂的主副线程切换,超时管理任务管理都封装在其中,对于使用者来说就会非常简单。
2.4 总结:
模板模式的好处就是对类似的任务进行一些封装,你只需去实现固定的步骤,就能更简单地去执行一些固定套路的任务。
3 IncomingCallFilter
在Android N,增加了一个IncomingCallFilter,看名字就是过滤来电用的,我认为这应该也算是模板方法模式,但是更加高级,它执行的步骤使可以增加的。每一个过滤器可以认为是一个步骤,根据没有过滤器返回的结果来决定时候拦截这个电话。
3.1 抽象的方法:
public interface CallFilter {
void startFilterLookup(Call call, CallFilterResultCallback listener);
}
public interface CallFilterResultCallback {
void onCallFilteringComplete(Call call, CallFilteringResult result);
}
你要实现一个过滤器,就要继承这个接口CallFilter,过滤完了之后通过CallFilterResultCallback 回调,告知过滤的结果。
所以你要实现的抽象方法就过滤器,和最后结果返回后该怎么办。
3.2 执行过程
构造:
public IncomingCallFilter(Context context, CallFilterResultCallback listener, Call call,
TelecomSystem.SyncRoot lock, Timeouts.Adapter timeoutsAdapter,
List<CallFilter> filters) {
mContext = context;
mListener = listener;
mCall = call;
mTelecomLock = lock;
mFilters = filters;
mNumPendingFilters = filters.size();
mTimeoutsAdapter = timeoutsAdapter;
}
首先在构造时需要传入过滤器的链表,并记录过滤器的个数。
执行:
public void performFiltering() {
Log.event(mCall, Log.Events.FILTERING_INITIATED);
for (CallFilter filter : mFilters) {
filter.startFilterLookup(mCall, this);
}
// synchronized to prevent a race on mResult and to enter into Telecom.
mHandler.postDelayed(new Runnable("ICF.pFTO", mTelecomLock) { // performFiltering time-out
@Override
public void loggedRun() {
if (mIsPending) {
Log.i(IncomingCallFilter.this, "Call filtering has timed out.");
Log.event(mCall, Log.Events.FILTERING_TIMED_OUT);
mListener.onCallFilteringComplete(mCall, mResult);
mIsPending = false;
}
}
}.prepare(), mTimeoutsAdapter.getCallScreeningTimeoutMillis(mContext.getContentResolver()));
}
就是依次执行所有的过滤器,但是它也有一个超时的机制,如果到时还有过滤器没有返回结果,它将用当前的结果,执行mListener的onCallFilteringComplete。
public void onCallFilteringComplete(Call call, CallFilteringResult result) {
synchronized (mTelecomLock) { // synchronizing to prevent race on mResult
mNumPendingFilters--;
mResult = result.combine(mResult);
if (mNumPendingFilters == 0) {
// synchronized on mTelecomLock to enter into Telecom.
mHandler.post(new Runnable("ICF.oCFC", mTelecomLock) {
@Override
public void loggedRun() {
if (mIsPending) {
Log.event(mCall, Log.Events.FILTERING_COMPLETED, mResult);
mListener.onCallFilteringComplete(mCall, mResult);
mIsPending = false;
}
}
}.prepare());
}
}
}
每个过滤器也通过onCallFilteringComplete的回调告诉IncomingCallFilter各自的结果,当所有的过滤器都返回结果之后,将调用mListener的onCallFilteringComplete,来电过滤的任务也就完成了。
3.3 总结
所以我认为这里也是一个模板方法模式,所需完成的抽象方法就是所有的过滤器和对结果的处理,它的高级之处在于过滤器的个数不受限制,即抽象的方法可以不停地增加,并且这里的过滤器也是通过onCallFilteringComplete回调来告知各自的结果,对于其自生的实现没有过多的限制,可以灵活地将它作为异步任务执行,也不会因为过滤器个体的阻塞导致整体的阻塞,我觉得整体都非常灵活,非常值得我借鉴和学习。