命令模式以及在android中的使用

Posted 爱炒饭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了命令模式以及在android中的使用相关的知识,希望对你有一定的参考价值。

一、原理

命令模式将任务请求封装成命令对象,命令对象中封装有任务处理者对象,调用者持有这个命令对象,这就使得调用者和处理者没有直接联系,降低了耦合度。同时,根据不同的请求可以参数化命令对象,可以将多个对象放入到集合中排队执行。命令模式的UML图如下,安利一个UML绘制网站https://app.diagrams.net/。

命令模式可以适用组合的方式将发出请求的对象和执行请求的对象解耦,这样后面拓展新的命令比较容易;缺点就是会出现过多的具体命令类以及处理者类。

二、实践

那上课为例吧,现在有个一个调用者-教学主任(TeachingDirector)、不同的课程(命令对象TeachChineseCommand、TeachEnglishCommand、TeachMathCommand)以及授课老师(处理者):语文老师(ChineseTeacher)、数学老师(MathTeacher)、英语老师(EnglishTeacher)。规则就是教导主任可以发布上课指令去触发上课命令对象,然后上课命令对象调用具体的老师去执行相应的教学任务。

ChineseTeacher.java

class ChineseTeacher {
    public void action() {
        LogUtil.d("teach Chinese");
    }
}

EnglishTeacher.java

class EnglishTeacher {
    public void action() {
        LogUtil.d("teach English");
    }
}

MathTeacher.java

class MathTeacher {
    public void action() {
        LogUtil.d("teach Math");
    }
}

Command.java

public interface Command {
    void execute();
}

TeachChineseCommand.java

class TeachChineseCommand implements Command{

    private ChineseTeacher chineseTeacher;

    public TeachChineseCommand(ChineseTeacher chineseTeacher) {
        this.chineseTeacher = chineseTeacher;
    }

    @Override
    public void execute() {
        if (chineseTeacher!=null) {
            chineseTeacher.action();
        }
    }
}

TeachEnglishCommand.java

class TeachEnglishCommand implements Command{

    private EnglishTeacher englishTeacher;

    public TeachEnglishCommand(EnglishTeacher englishTeacher) {
        this.englishTeacher = englishTeacher;
    }

    @Override
    public void execute() {
        if (englishTeacher!=null) {
            englishTeacher.action();
        }
    }
}

TeachMathCommand.java

class TeachMathCommand implements Command{

    private MathTeacher mathTeacher;

    public TeachMathCommand(MathTeacher mathTeacher) {
        this.mathTeacher = mathTeacher;
    }

    @Override
    public void execute() {
        if (mathTeacher!=null) {
            mathTeacher.action();
        }
    }
}

TeachingDirector.java

public class TeachingDirector {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void doAction() {
        if (this.command !=null) {
            this.command.execute();
        }
    }
}

测试代码如下:

private void doTest() {
    TeachChineseCommand teachChineseCommand = new TeachChineseCommand(new ChineseTeacher());
    TeachEnglishCommand teachEnglishCommand = new TeachEnglishCommand(new EnglishTeacher());
    TeachMathCommand teachMathCommand = new TeachMathCommand(new MathTeacher());
    TeachingDirector teachingDirector = new TeachingDirector();
    teachingDirector.setCommand(teachChineseCommand);
    teachingDirector.doAction();

    teachingDirector.setCommand(teachEnglishCommand);
    teachingDirector.doAction();

    teachingDirector.setCommand(teachMathCommand);
    teachingDirector.doAction();
}

log打印如下:

ChineseTeacher.action(L:8): teach Chinese
EnglishTeacher.action(L:8): teach English
MathTeacher.action(L:8): teach Math

此时,教学主任和学科老师没有直接联系,教学主任只需发布具体的上课命令,上课命令会通知指定的学科老师上课,达到解耦的目的。后续如果有其它学科老师以及相应的教学命令对象可以直接添加然后调用教学主任类的setCommand即可,对固有代码修改小,拓展性强。

三、android中命令模式的使用

android中适用的更多的是命令模式的变种-命令对象不再设置接收者,命令对象本身就完成了具体任务。

3.1 PackageManagerService.HandlerParams

在PackageManagerService.java中有一个HandlerParams抽象内部类,HandlerParams的子类InstallParams、MultiPackageInstallParams重写了父类的抽象方法handleStartCopy()和handleReturnCode(),startCopy()会使用模板方法模式调用handleStartCopy()、handleReturnCode()方法。

//PackageManagerService.java
private abstract class HandlerParams {
    /** User handle for the user requesting the information or installation. */
    private final UserHandle mUser;
    String traceMethod;
    int traceCookie;

    HandlerParams(UserHandle user) {
        mUser = user;
    }

    UserHandle getUser() {
        return mUser;
    }

    /**
     * Gets the user handle for the user that the rollback agent should
     * use to look up information about this installation when enabling
     * rollback.
     */
    UserHandle getRollbackUser() {
        // The session for packages installed for "all" users is
        // associated with the "system" user.
        if (mUser == UserHandle.ALL) {
            return UserHandle.SYSTEM;
        }
        return mUser;
    }

    HandlerParams setTraceMethod(String traceMethod) {
        this.traceMethod = traceMethod;
        return this;
    }

    HandlerParams setTraceCookie(int traceCookie) {
        this.traceCookie = traceCookie;
        return this;
    }

    final void startCopy() { //模板方法模式
        if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
        handleStartCopy();
        handleReturnCode();
    }

    abstract void handleStartCopy();
    abstract void handleReturnCode();
}

在installStage方法中就会将InstallParams 作为参数传入到mHandler中,mHandler是PackageHandler实例。然后在PackageHandler的doHandleMessage方法中去处理消息,当msg.what是INIT_COPY时取出的msg.obj就是InstallParams 对象,并调用InstallParams的startCopy()方法。

//PackageManagerService.java
void installStage(ActiveInstallSession activeInstallSession) {
    if (DEBUG_INSTANT) {
        if ((activeInstallSession.getSessionParams().installFlags
                & PackageManager.INSTALL_INSTANT_APP) != 0) {
            Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
        }
    }
    final Message msg = mHandler.obtainMessage(INIT_COPY);//msg.what
    final InstallParams params = new InstallParams(activeInstallSession);
    params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
    msg.obj = params;

    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
            System.identityHashCode(msg.obj));
    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
            System.identityHashCode(msg.obj));

    mHandler.sendMessage(msg);
}
……
class PackageHandler extends Handler {

    PackageHandler(Looper looper) {
        super(looper);
    }

    public void handleMessage(Message msg) {
        try {
            doHandleMessage(msg);
        } finally {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        }
    }

    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                HandlerParams params = (HandlerParams) msg.obj;
                if (params != null) {
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                            System.identityHashCode(params));
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                    params.startCopy();
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                break;
            }
			……
		}
	}
}

这里的PackageManagerService.java中InstallParams就相当于一个命令对象,其自身实现了具体任务startCopy。

3.2 executor.execute

java中有线程池,线程池对象可以执行execute方法,execute方法参数就是一个Runnable指令,此时就可以创建一个Runnable命令对象,然后通过线程池 去触发对应的操作。

Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
    @Override
    public void run() {
        LogUtil.d("do yourself in Runnable");
    }
});
Thread thread = new Thread(){
    @Override
    public void run() {
        super.run();
        LogUtil.d("do yourself in Thread");
    }
};
executor.execute(thread);

以上是关于命令模式以及在android中的使用的主要内容,如果未能解决你的问题,请参考以下文章

命令模式以及在android中的使用

命令模式以及在android中的使用

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

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

何时使用活动转换与动态片段的模式

Android - 片段中的 getIntent()