從mms源碼看IM應用的架構

Action+ IntentService架構

  這一部分給大家總結一下mms裏面對於後臺任務的處理。正常情況下,一個互聯網應用可能會涉及到n多的後臺任務要運行,短信應用也不例外,例如插入短信到數據庫,刪除短信,標記爲已讀,發送短信,接收短信,下載彩信等。這些都是耗時任務,並且他們之間有些還有先後順序要求,如果沒有一個良好的後臺任務管理框架,拿維護起來可就要了親命了。

  這裏我們就來看一下mms應用中的“Action+IntentService架構”是怎麼解決這個問題的。

先簡單看一下Action:

public abstract class Action implements Parcelable {
   ..........................................
    public void start() {
        DataModel.startActionService(this);
    }
    //該Action要執行的具體後臺任務放在這裏,該方法需要我們繼承的
    //Action子類實現
    protected Object executeAction() {
        return null;
    }

    ..........................................

    //需要在該Action之後執行的Action放到mBackgroundActions中,在該Action執行完
    //之後會將這些mBackgroundActions取出來執行
    private final List<Action> mBackgroundActions = new LinkedList<Action>();
    protected void requestBackgroundWork(final Action backgroundAction) {
    	LogUtil.i(TAG, "requestBackgroundWork");
        mBackgroundActions.add(backgroundAction);
    }
    protected Bundle doBackgroundWork() throws DataModelException {
        return null;
    }
    ..........................................

}
    我們大致上就可以瞭解Action是什麼了?簡單的來說,可以理解成一個Runnable,在executeAction方法中是具體的後臺任務邏輯,類似於run方法。但顯然Action比Runnable複雜一些,多維護了一個mBackgroundActions列表,他是一個List<Action>,維護的是需要在該Action執行之後再執行的Action,當然這些都是由Action對象本身維護和觸發,是典型的面向對象的設計。這樣的設計無疑給繁雜的後臺任務維護指明瞭一條簡單清晰的框架:誰關聯誰維護。這樣就不需要我們整體上再維護Action隊列來決定先後順序了,是一個非常不錯的設計。

 我們再來以短信發送過程爲例來說明:


 這個過程涉及到兩個Action,InsertNewMessageAction和SendMessageAction,分別對應的是將短信插入數據庫和真正發送的過程,都是耗時任務,所以都需要Action來處理。在這裏我們就看到SendMessageAction需要在InsertNewMessageAction之後執行,所以被放在了mBackgroundActions中了。

 而實際上,我統計了一下,整個mms應用中有大概30個Action,包括DeleteConversationActionDeleteMessageAction

DownloadMmsActionDumpDatabaseActionHandleLowStorageActionMarkAsReadActionReceiveSmsMessageActionUpdateMessagePartSizeAction等。這些紛繁的後臺任務因爲有了Action架構,就顯得不那麼亂了。

 再來看一下IntentService.

 Action本身只是一個後臺任務邏輯載體,並沒有維護線程來執行任務,所以真正的任務執行是在IntentService裏面。有經驗的Android工程師應該都知道IntentService,因爲Service本身是運行在UI Thread中的,所以一般我們執行後臺任務都要再單獨開線程,而Android框架爲了方便,直接又封裝了一個帶Thread的Service,即IntentService。並且IntentService是即用即銷燬,所以我們連生命週期都不要維護,只需要實現它的onHandleIntent方法執行自己的後臺邏輯即可。

 我們來看一下在mms源碼中對IntentService的使用:

public class ActionServiceImpl extends IntentService {
    /**
     *提供一個靜態全局方法,方便外界直接對自己實例化
     */
    private static void startServiceWithIntent(final Intent intent) {
        final Context context = Factory.get().getApplicationContext();
        final int opcode = intent.getIntExtra(EXTRA_OP_CODE, 0);
        // Increase refCount on wake lock - acquiring if necessary
        if (VERBOSE) {
            LogUtil.v(TAG, "acquiring wakelock for opcode " + opcode);
        }
        sWakeLock.acquire(context, intent, opcode);
        intent.setClass(context, ActionServiceImpl.class);

        // TODO: Note that intent will be quietly discarded if it exceeds available rpc
        // memory (in total around 1MB). See this article for background
        // http://developer.android.com/reference/android/os/TransactionTooLargeException.html
        // Perhaps we should keep large structures in the action monitor?
        if (context.startService(intent) == null) {
            LogUtil.e(TAG,
                    "ActionService.startServiceWithIntent: failed to start service for intent "
                    + intent);
            sWakeLock.release(intent, opcode);
        }
    }

  protected void onHandleIntent(final Intent intent) {
        if (intent == null) {
            // Shouldn't happen but sometimes does following another crash.
            LogUtil.w(TAG, "ActionService.onHandleIntent: Called with null intent");
            return;
        }
        final int opcode = intent.getIntExtra(EXTRA_OP_CODE, 0);
        sWakeLock.ensure(intent, opcode);

        try {
            Action action;
            final Bundle actionBundle = intent.getBundleExtra(EXTRA_ACTION_BUNDLE);
            actionBundle.setClassLoader(getClassLoader());
            switch(opcode) {
                case OP_START_ACTION: {
                    action = (Action) actionBundle.getParcelable(BUNDLE_ACTION);
                    //真正執行Action
                    executeAction(action);
                    break;
                }

                case OP_RECEIVE_BACKGROUND_RESPONSE: {
                    action = (Action) actionBundle.getParcelable(BUNDLE_ACTION);
                    final Bundle response = intent.getBundleExtra(EXTRA_WORKER_RESPONSE);
                    processBackgroundResponse(action, response);
                    break;
                }

                case OP_RECEIVE_BACKGROUND_FAILURE: {
                    action = (Action) actionBundle.getParcelable(BUNDLE_ACTION);
                    processBackgroundFailure(action);
                    break;
                }

                default:
                    throw new RuntimeException("Unrecognized opcode in ActionServiceImpl");
            }
            //執行Action的BackgroundAction
            action.sendBackgroundActions(mBackgroundWorker);
        } finally {
            // Decrease refCount on wake lock - releasing if necessary
            sWakeLock.release(intent, opcode);
        }
    }

}
 我們可以看到,這裏封裝了一個ActionServiceImpl類用於執行Action,同時在最後會觸發Action的backgroundAction也去執行。這裏注意一下,backgroundAction是在另一個IntentService即BackgroundWorkerService中執行。不過這裏BackgroundWorkerService和ActionServiceImpl除了代碼結構規整之外,設計BackgroundWorkerService應該還有一些用處,因爲在DeleteMessageAction中有以下注釋:

    // Doing this work in the background so that we're not competing with sync
    // which could bring the deleted message back to life between the time we deleted
    // it locally and deleted it in telephony (sync is also done on doBackgroundWork).
    //
    // Previously this block of code deleted from telephony first but that can be very
    // slow (on the order of seconds) so this was modified to first delete locally, trigger
    // the UI update, then delete from telephony.
    @Override
    protected Bundle doBackgroundWork() {
    }
 大致意思是將刪除短信的Action放在BackgroundWorkerService中執行,以避開與SyncMessagesAction競爭。但我仔細研究了一下代碼也沒有發現它怎麼避開競爭,這一塊還要再研究一下。

 

另外,ActionMonitor也值得一說

public class ActionMonitor {
    protected int mState;
    static void setState(final Action action, final int expectedOldState,
            final int newState) {
        int oldMonitorState = expectedOldState;
        int newMonitorState = newState;
        final ActionMonitor monitor
                = ActionMonitor.lookupActionMonitor(action.actionKey);
        if (monitor != null) {
            oldMonitorState = monitor.mState;
            monitor.updateState(action, expectedOldState, newState);
            newMonitorState = monitor.mState;
        }
        .............................................
    }

    public interface ActionCompletedListener {

        abstract void onActionSucceeded(ActionMonitor monitor,
                final Action action, final Object data, final Object result);
        abstract void onActionFailed(ActionMonitor monitor, final Action action,
                final Object data, final Object result);
    }
}

 每一個Action對應一個ActionMonitor,用於維護Action所處於的狀態,這樣就可以在每個狀態與外界交互,比如在Action執行完之後回調onActionSucceeded方法。

 當然,這裏都是抽絲剝繭的說了一下整個架構,而具體的細節其實還有很多,包括各種執行失敗的處理交互,各種任務的前後關聯,這裏面內容還是比較多的,但限於篇幅這裏就不展開了,感興趣的同學可以直接查看mms源碼。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章