一句话说透Android里面的命令模式

160 阅读3分钟

一句话总结:
命令模式就像「外卖订单」—— 把用户请求(点菜)封装成对象(订单),交给系统(骑手)处理,Android 里的点击事件、异步任务、Activity启动都用这套机制解耦执行流程!


一、命令模式的核心思想

核心要素

  1. 命令(Command) :封装操作请求(如 RunnableIntent
  2. 调用者(Invoker) :触发命令执行(如 HandlerView
  3. 接收者(Receiver) :实际执行操作的对象(如 ActivityThreadButton

类比场景

  • 顾客(Client) → 点宫保鸡丁(Command)
  • 服务员(Invoker) → 传递订单给后厨
  • 厨师(Receiver) → 实际炒菜

二、Android 源码中的命令模式案例

1. Handler 的 Runnable 命令

// 定义命令  
Runnable task = new Runnable() {  
    @Override  
    public void run() {  
        updateUI(); // 实际执行的操作  
    }  
};  

// 调用者发送命令  
Handler handler = new Handler(Looper.getMainLooper());  
handler.post(task); // 将命令加入消息队列  

// 底层实现:Message 携带命令  
public class Handler {  
    public final boolean post(Runnable r) {  
        return sendMessageDelayed(getPostMessage(r), 0);  
    }  

    private static Message getPostMessage(Runnable r) {  
        Message m = Message.obtain();  
        m.callback = r; // 命令存储在 Message 中  
        return m;  
    }  

    // 分发执行命令  
    public void dispatchMessage(Message msg) {  
        if (msg.callback != null) {  
            msg.callback.run(); // 执行 Runnable 命令  
        }  
    }  
}  

设计亮点

  • 将异步操作封装成 Runnable 命令对象
  • Handler 作为调用者统一管理命令队列

2. Activity 启动的 Intent 命令

// 封装启动命令  
Intent intent = new Intent(this, TargetActivity.class);  
intent.putExtra("key", "value");  

// 触发命令执行  
startActivity(intent);  

// 源码流程:  
// - Activity.startActivity() → Instrumentation.execStartActivity()  
// - 通过 Binder 发送 Intent 命令到 ActivityManagerService (AMS)  
// - AMS 处理命令,启动目标 Activity  

关键源码

// frameworks/base/core/java/android/app/Activity.java  
public void startActivity(Intent intent) {  
    startActivityForResult(intent, -1);  
}  

// frameworks/base/core/java/android/app/Instrumentation.java  
public ActivityResult execStartActivity(...) {  
    // 跨进程调用 AMS  
    int result = ActivityTaskManager.getService().startActivity(...);  
}  

命令传递
Intent 作为命令对象,跨进程传输 → AMS 解析并执行


3. 点击事件的 OnClickListener

// 定义命令  
View.OnClickListener clickCommand = new View.OnClickListener() {  
    @Override  
    public void onClick(View v) {  
        handleClick();  
    }  
};  

// 设置命令到调用者  
button.setOnClickListener(clickCommand);  

// 源码实现:View 处理命令分发  
public class View implements OnClickListener {  
    private OnClickListener mOnClickListener;  

    public void setOnClickListener(OnClickListener l) {  
        mOnClickListener = l; // 存储命令  
    }  

    // 触发执行  
    public void performClick() {  
        if (mOnClickListener != null) {  
            mOnClickListener.onClick(this);  
        }  
    }  
}  

三、命令模式的底层优势

1. 解耦请求与实现

  • 发送者(如 Button)不关心谁处理点击
  • 接收者(如 Activity)可灵活替换处理逻辑

2. 支持命令队列与撤销

// 实现命令队列  
Queue<Runnable> commandQueue = new LinkedList<>();  
commandQueue.offer(task1);  
commandQueue.offer(task2);  

// 依次执行  
while (!commandQueue.isEmpty()) {  
    commandQueue.poll().run();  
}  

// 实现撤销(需命令对象记录状态)  
public interface UndoableCommand {  
    void execute();  
    void undo();  
}  

3. 跨进程通信的天然适配

  • Intent 通过 Binder 传递,天然符合命令模式
  • 序列化机制(Parcelable)保证命令对象可传输

四、命令模式在开源框架中的应用

1. Retrofit 的 Call 命令

// 定义网络请求命令  
Call<User> call = retrofit.create(UserService.class).getUser(123);  

// 异步执行命令  
call.enqueue(new Callback<User>() {  
    @Override  
    public void onResponse(Call<User> call, Response<User> response) {  
        // 处理响应(相当于命令执行结果)  
    }  
});  

2. RxJava 的 Action 命令

Observable.just("Hello")  
    .subscribe(new Consumer<String>() {  
        @Override  
        public void accept(String s) { // 命令执行  
            System.out.println(s);  
        }  
    });  

五、总结与最佳实践

使用场景口诀

点击异步加跳转,命令模式解耦强
需要队列或撤销,此模式是好搭档

Android 源码对照表

场景命令对象调用者接收者
异步任务RunnableHandlerLooper/主线程
Activity 跳转IntentActivityActivityManagerService
点击事件OnClickListenerView实现接口的 Activity

注意事项

  1. 简单回调无需过度设计成命令模式
  2. 注意匿名内部类导致的内存泄漏(如 Runnable 持有 Activity 引用)
  3. 跨进程命令要确保 Parcelable 正确实现

总结口诀
请求封装成命令,解耦调用和执行
Handler 发 Runnable,Intent 跳转 Activity
队列撤销都能做,灵活扩展显威力