編輯:關於Android編程
從裝飾者模式到Context類族
當觀察者模式和回調機制遇上Android源碼
Android源碼中的靜態工廠方法
Android中的工廠方法模式
前面跟大家分享了裝飾者模式、觀察者模式、靜態工廠方法、工廠方法模式,今天跟大家分享下Android源碼中的命令模式。
將一個請求封裝成一個對象,從而使你可用不同的請求對客戶進行參數化,對請求排隊或記錄請求日志,以及支持可撤銷的操作。
對於大多數請求——響應模式的功能,比較適合使用命令模式。
系統需要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。 系統需要在不同的時間指定請求、將請求排隊(如:線程池+工作隊列)和執行請求。 系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作(比如系統掛掉之後重啟做一些恢復操作,還有數據庫的事務等)。 系統需要將一組操作組合在一起,即支持宏命令。
vcnmvLC1vbXEvcfJq6O6PC9wPg0KPHN0cm9uZz6/zbunvcfJq6OoQ2xpZW50o6k8L3N0cm9uZz6jukNsaWVudL/J0tS0tL2ovt/M5bXEw/zB7rbUz/OjrLKix9LJ6NbDw/zB7rbUz/O1xL3TytXV36GjVGlwc6O6srvE3LDRQ2xpbmV0wO294s6qztLDx8a9s6PLtbXEv827p7bLo6zV4sDvtcRDbGllbnTKx9K7uPbX6deww/zB7rbUz/O6zb3TytzV37bUz/O1xL3HyaujrLvy1d/E47DRy/zA7b3izqrSu7j217DF5NXfoaMgPHN0cm9uZz6199PD1d+9x8mro6hJbnZva2Vyo6k8L3N0cm9uZz6juri61PC199PDw/zB7rbUz/PWtNDQx+vH86Oszaizo7vhs9bT0MP8we621M/zo6i/ydLUs9bT0LbguPbD/MHuttTP86OpoaNJbnZva2VyysdDbGllbnTV5tX9tKW3osP8we6yotKqx/PD/MHu1rTQ0M/g06ay2df3tcS12Le9o6jKudPDw/zB7rbUz/O1xMjrv9qjqaGjIDxzdHJvbmc+w/zB7r3HyaujqENvbW1hbmSjqTwvc3Ryb25nPqO6tqjS5cP8we61xL3Tv9qjrMn5w/e+38zlw/zB7sDg0OjSqta00NC1xLe9t6iho9XiysfSu7j2s+nP873HyauhoyA8c3Ryb25nPr7fzOXD/MHuvcfJq6OoQ29uY3JldGVDb21tYW5ko6k8L3N0cm9uZz6jusP8we6907/atcS+38zlyrXP1rbUz/OjrM2os6O74bPW09C908rV1d+jrLKitffTw73TytXV37XEuabE3MC0zeqzycP8we7Sqta00NC1xLLZ1/ehoyA8c3Ryb25nPr3TytXV373HyaujqFJlY2VpdmVyo6k8L3N0cm9uZz6julJlY2VpdmVyysfV5tX91rTQ0MP8we61xLbUz/Oho8jOus7A4La8v8nE3LPJzqrSu7j2vdPK1dXfo6zWu9Kqy/zE3Lm7yrXP1sP8we7SqsfzyrXP1rXEz+DTprmmxNyhow0KPGgyIGlkPQ=="實現">實現
命令模式其實就是對命令進行封裝,將命令請求者和命令執行者的責任分離開來實現松耦合。 這裡我們通過一個餐廳點餐的實例來剖析一下命令模式:命令接收者Cook可以做各式各樣的菜,根據Waiter送過來的訂單來滿足顧客的需求,具體命令實現類PigCook執行做烤乳豬命令,DuckCook執行燒花鴨命令等等,Client負責組裝各個部分。
命令角色
public interface Command {
public void execute();
public void undo();
public void redo();
}
命令接收者
public class Cook {
//烤乳豬的方法
public void cookPig(){
}
//燒花鴨的方法
public void cookDuck(){
}
}
具體命令角色
//做烤乳豬的命令
public class PigCook implements Command {
private Cook mCook;
public PigCook(Cook cook) {
mCook = cook;
}
@Override
public void execute() {
mCook.cookPig();
}
@Override
public void undo() {
}
@Override
public void redo() {
}
}
//做燒花鴨的命令
public class DuckCook implements Command {
private Cook mCook;
public DuckCook(Cook cook) {
mCook = cook;
}
@Override
public void execute() {
mCook.cookDuck();
}
@Override
public void undo() {
}
@Override
public void redo() {
}
}
調用者角色
public class Waiter {
private Command pig;
private Command duck;
public void setCommandPig(Command pig) {
this.pig = pig;
}
public void setCommandDuck(Command duck) {
this.duck = duck;
}
/**
* 執行正常命令,這裡省略了undo和redo操作
*/
public void invoke(int args) {
//可以根據具體情況選擇執行某些命令
if(args == 0){
pig.execute();
}else if(args == 1){
duck.execute();
}
}
}
客戶角色
public class Client {
/**
* 組裝操作
*/
public void assembleAction() {
//創建一個命令接收者
Cook mCook = new Cook();
//創建一個命令的具體實現對象,並指定命令接收者
Command pig = new PigCook(mCook);
Command duck = new DuckCook(mCook);
Waiter mWaiter = new Waiter();//創建一個命令調用者
//為調用者指定烤乳豬命令對象
mWaiter.setCommandPig(pig);
//為調用者指定燒花鴨命令對象
mWaiter.setCommandDuck(duck);
//發起調用烤乳豬命令請求
mWaiter.invoke(0);
//發起調用燒花鴨命令請求
mWaiter.invoke(1);
}
}
可是,為什麼要這麼復雜咧,我只是想點個菜而已嘛,直接這麼搞不就好了?
public class Client {
/**
* 組裝操作
*/
public void assembleAction() {
//創建一個命令接收者
Cook mCook = new Cook();
//發起調用烤乳豬命令請求
mCook.cookPig();
//發起調用燒花鴨命令請求
mCook.cookDuck();
}
}
我們知道命令模式的一個優點是支持命令的撤銷(Undo)操作和恢復(Redo)操作,如果我們像上邊一樣調用,我們要想做撤銷是不是就不那麼方便了呢。同時還可以考慮下命令模式的其他幾個優點。
每一個命令都是一個操作:請求的一方發出請求,要求執行一個操作;接收的一方收到請求,並執行操作。
命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎麼被接收,以及操作是否被執行、何時被執行,以及是怎麼被執行的。
命令模式使請求本身成為一個對象,這個對象和其他對象一樣可以被存儲和傳遞。
命令模式的關鍵在於引入了抽象命令接口,且發送者針對抽象命令接口編程,只有實現了抽象命令接口的具體命令才能與接收者相關聯。
對於Android源碼來說,Android底層邏輯對事件的轉發處理就用到了命令模式。Application Framework(應用程序框架層)中PackageManagerService類(包管理部分)也用到了命令模式。PackageManagerService是Android系統的Service之一,主要功能是實現對應用包的解析、管理、卸載等操作。我們來看下具體的結構。

HandlerParams是命令接口,即我們的Command角色。
private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;
/**
* Number of times startCopy() has been attempted and had a non-fatal
* error.
*/
private int mRetries = 0;
/** 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;
}
HandlerParams setTraceMethod(String traceMethod) {
this.traceMethod = traceMethod;
return this;
}
HandlerParams setTraceCookie(int traceCookie) {
this.traceCookie = traceCookie;
return this;
}
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}
final void serviceError() {
if (DEBUG_INSTALL) Slog.i(TAG, "serviceError");
handleServiceError();
handleReturnCode();
}
abstract void handleStartCopy() throws RemoteException;
abstract void handleServiceError();
abstract void handleReturnCode();
}
具體的包的安裝、移動以及包大小的測量分別在3個具體子類InstallParams、MoveParams和MeasureParams中實現。
而PackageHandler是Handler的子類,用來負責包相關消息的處理,不同的請求對應不同的命令對象,然後通過命令對象來執行具體操作。
通過接觸Android源碼或者其他的一些源碼,我們知道有些地方是沒有命令接收者(Receiver)這個角色的,這是為什麼呢?
個人認為,有的命令接收實現非常簡,可以直接用少量的代碼來實現,沒有必要再增加類的數量。
Android:基於百度雲推送的聊天系統
一、系統運行圖 二、原理下面通過幾個問題來說明下實現的原理:1、如何實現給某個用戶發送消息呢?其實就是利用百度雲提供的REST A
Android通過json向MySQL中讀寫數據的方法詳解【寫入篇】
本文實例講述了Android通過json向MySQL中寫入數據的方法。分享給大家供大家參考,具體如下:先說一下如何通過json將Android程序中的數據上傳到MySQL
通過源碼,手把手帶你學屬性動畫(一) - 相關類總覽
在 Android 3.0(API level 11) 之後,Google 為 Android添加了屬性動畫(Property Animation),該動畫系統是一個強大
Android插件開發初探——基礎篇
Android插件開發初探對於Android的插件化其實已經討論已久了,但是市面上還沒有非常靠譜成熟的插件框架供我們使用。這裡我們就嘗試性的對比一下Java中,我們使用插