模板方法模式以及在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按钮单击方法(崩溃)

从某个类调用片段中的方法

是否有在单个活动中处理多个片段的 Android 设计模式?

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情

如何修复空白片段?