模板模式的应用

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回调来告知各自的结果,对于其自生的实现没有过多的限制,可以灵活地将它作为异步任务执行,也不会因为过滤器个体的阻塞导致整体的阻塞,我觉得整体都非常灵活,非常值得我借鉴和学习。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值