模板方法模式以及在android中的应用
Posted 爱炒饭
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板方法模式以及在android中的应用相关的知识,希望对你有一定的参考价值。
一、原理
模板方法模式封装了一个算法(方法)流程的基本步骤,有些步骤的实现已经在父类中实现好,而另外一些步骤则可以推迟到子类中等待子类去具体实现,子类就可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
这样做有什么好处呢?首先,体现了面向对象的封装性,一些算法的基本步骤不允许修改,防止子类随意的修改算法流程;其次,对于一些公共代码可以直接将实现封装在父类,这样就起到了代码复用的目的;同时,也可以在父类的封装算法中添加一些钩子,让子类可以适当的改变算法的部分可变流程,增加算法的拓展性。
模板方法模式和策略模式有些相似,他们都是行为型设计模式,都是对某个算法的不同实现,但二者还是有些区别的,模板方法模式是算法的基本流程已经被父类限定,子类重写替换算法的某一个或多个流程而非全部流程,它是通过类的继承达到目的的;而策略模式更多的是整个算法的替换,它是通过不同实现算法类的组合来实现的。
二、demo
举个例子,可以把人(Person)作为一个父类,生活(live)作为一个算法,里面有born,study,work,marry四个基本过程,针对marry这个过程,有些人可能是不婚主义,需要根据钩子celibacy方法去判断是否执行marry这个动作,celibacy方法就是一个钩子,子类可以不实现该方法,也可以重写父类方法达到控制marry流程的效果;针对work,这里将其作为一个抽象方法,子类不同的人群工作不一样,需要具体的子类去实现;其它的方法父类已经封装好,子类可以直接用父类的公共方法,当然如果有需要的话也可以去重写。
首先创建了Person类
public abstract class Person {
final void live(){//一般为了防止子类修改算法步骤,可以直接将算法声明为final
LogUtil.d("A person live start");
born();
study();
work();
if(!celibacy()){
marry();
}
LogUtil.d("A person continue to enjoy life");
}
protected void marry() {
LogUtil.d("I am married");
}
boolean celibacy() {
return false;
}
abstract void work() ;
void study() {
LogUtil.d("go to school");
}
void born() {
LogUtil.d("I am born");
}
}
然后创建Teacher类,实现了父类的work抽象方法。
public class Teacher extends Person{
@Override
void work() { //老师的工作是教育学生
LogUtil.d(" A teacher educates students");
}
}
接着,创建了Philosopher类,实现了父类的work抽象方法,同时,也重写了父类的钩子,有个哲学家为了全人类,痴迷思考,选择了单身。
public class Philosopher extends Person{
@Override
void work() { //哲学家的工作就是思考人生
LogUtil.d(" A philosopher think the value of life ");
}
@Override
boolean celibacy(){ //为了全人类,一个哲学家打算单身
LogUtil.d("For All Mankind,a philosopher choose celibacy");
return true;
}
}
现在,我们来是做个测试程序
Teacher teacher = new Teacher();
Philosopher philosopher= new Philosopher();
teacher.live();
SystemClock.sleep(500);
LogUtil.e("next role start");
philosopher.live();
log打印如下,可以看到Teacher和Philosopher 不同的work方式,其中Philosopher实现选择了修改钩子celibacy,不去结婚marry了,专心研究。
Person.live(L:8): A person live start
Person.born(L:33): I am born
Person.study(L:29): go to school
Teacher.work(L:9): A teacher educates students
Person.marry(L:19): I am married
Person.live(L:15): A person continue to enjoy life
MainActivity.onCreate(L:26): next role start
Person.live(L:8): A person live start
Person.born(L:33): I am born
Person.study(L:29): go to school
Philosopher.work(L:10): A philosopher think the value of life
Philosopher.celibacy(L:15): For All Mankind,a philosopher choose celibacy
Person.live(L:15): A person continue to enjoy life
上面就是一个典型的模板方法例子。
三、android源码的模板方法模式(基于Android11)
Android源码中使用模板方法模式的地方也不少,这里列举三个比较常见的场景
3.1 Activity生命周期
一般开发者创建一个Activity时系统已经帮我们处理了生命周期顺序,通常境况下一个Activity生命周期是这样的onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroy(),为什么是这个顺序呢,我们可以在onCreate()方法中覆写setContentView指定UI,为什么就不能修改这个顺序呢?这是因为源码中使用模板方法模式限定了方法执行的顺序,开发者只能修改方法的实现。这里主要说下onCreate() -> onStart() -> onResume()这三个流程的步骤把。
3.1.1 ActivityThread.main
首先应用启动会走到ActivityThread的main方法,在该方法新建了一个ActivityThread实例对象并调用实例对象的attach方法
//ActivityThread.java
public static void main(String[] args) {
……
Looper.prepareMainLooper(); //设置主线程looper
……
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
……
Looper.loop(); //开启消息循环,等待事件消息
……
}
3.1.2 ActivityThread.attach
ActivityThread方法根据第一个参数system调用了ActivityManagerService的attachApplication方法并将ActivityThread实例对象作为参数也传递过去。
//ActivityThread.java
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
……
}
……
}
3.1.3 ActivityManagerService.attachApplicationLocked
ActivityManagerService的attachApplication方法在同步代码块中调用了attachApplicationLocked方法,attachApplicationLocked方法有四百多行代码,实际上整个ActivityManagerService类有两万多行,太笨重了,这里只展示涉及到启动流程的代码。其中,thread.bindApplication会调用应用的Application类的onCreate()方法,mAtmInternal是ActivityTaskManagerInternal的一个实例,根据ActivityTaskManagerInternal.java的注释可以看出具体实现在ActivityTaskManagerService.java。
//ActivityManagerService.java
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
……
thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions,
app.mDisabledCompatChanges);
……
// See if the top visible activity is waiting to run in this process...
if (normalMode) {
try {
didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
……
}
3.1.4 ActivityTaskManagerService.attachApplication
ActivityTaskManagerService的attachApplication方法在同步代码块中调用了RootWindowContainer.java的attachApplication方法,RootWindowContainer是android新版本新加的类。
//ActivityTaskManagerService.java
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLockWithoutBoost) {
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
}
try {
return mRootWindowContainer.attachApplication(wpc);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
}
3.1.5 RootWindowContainer.attachApplication
RootWindowContainer的attachApplication方法调用了startActivityForAttachedApplicationIfNeeded方法,在startActivityForAttachedApplicationIfNeeded中又调用了ActivityStackSupervisor类的realStartActivityLocked方法。
boolean attachApplication(WindowProcessController app) throws RemoteException {
final String processName = app.mName;
……
final PooledFunction c = PooledLambda.obtainFunction(
RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivity());
stack.forAllActivities(c);
……
}
3.1.6 ActivityStackSupervisor.realStartActivityLocked
ActivityStackSupervisor的realStartActivityLocked方法就是Activity生命周期前三个方法onCreate() -> onStart() -> onResume()的模板方法了,通过clientTransaction将LaunchActivityItem、ResumeActivityItem串联了起来,LaunchActivityItem中会调用ActivityThread中的handleLaunchActivity方法,在handleLaunchActivity方法中会创建Activity实例对象并触发Activity的onCreate()方法;ResumeActivityItem会调用ActivityThread中的handleResumeActivity方法,在handleResumeActivity方法中会依次触发Activity的onStart() 和onResume()方法(onStart()是在Activity的performResume方法中触发的)。
//ActivityStackSupervisor.java
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
……
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
r.assistToken, r.createFixedRotationAdjustmentsIfNeeded()));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
……
}
3.2 View绘制
View绘制是最明显的模板方法模式了,在draw方法中的注释里面指明了1-7步的绘制流程,代码也很清晰,无锡过多解释。View类的onDraw方法和dispatchDraw方法都是抽象方法,后续开发者自定义View的话需要去实现这些抽象方法。
//View.java
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
* 7. If necessary, draw the default focus highlight
*/
// Step 1, draw the background, if needed
int saveCount;
drawBackground(canvas);
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
/**
* Implement this to do your drawing.
*
* @param canvas the canvas on which the background will be drawn
*/
protected void onDraw(Canvas canvas) {
}
/**
* Called by draw to draw the child views. This may be overridden
* by derived classes to gain control just before its children are drawn
* (but after its own view has been drawn).
* @param canvas the canvas on which to draw the view
*/
protected void dispatchDraw(Canvas canvas) {
}
3.3 AsyncTask
AsyncTask用于短暂的耗时操作,里面封装了线程和Hnadler,方便开发者执行后台任务以及更新UI,虽然现在已经废弃了,被协程所替代,但是作为一个优秀的android框架还是值得学习的。
AsyncTask通过execute方法开始任务后会调用executeOnExecutor方法,executeOnExecutor方法中定义了程序执行的顺序,首先onPreExecute()可以做一些主线程的准备工作,然后通过线程池去执行耗费操作,耗时操作在抽象方法doInBackground中执行,最后通过finish方法通知任务结束。
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
……
mStatus = Status.RUNNING;
onPreExecute(); //主线程准备工作
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
……
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams); //耗时操作
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result); //通知任务完成
}
return result;
}
};
protected abstract Result doInBackground(Params... params);
以上是关于模板方法模式以及在android中的应用的主要内容,如果未能解决你的问题,请参考以下文章
是否有在单个活动中处理多个片段的 Android 设计模式?
我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情