IPCInvoker,Android跨程式呼叫如此簡單

AlbieLiang發表於2018-01-02

一個APP為什麼需要多條程式?

如果一條程式能夠擁有足夠多的資源,且不會被系統kill掉的話,讓程式執行在一條程式上是最好的選擇。但是系統資源是按程式來分配的,每條程式資源分配是有個上限的,而且當我們的APP退到後臺之後,系統會根據系統資源使用情況,回收部分後臺程式資源。

具有推送或後臺播放音樂等功能的APP,在APP被退到後臺之後,為了保持良好的使用者體驗,則需要在後臺保持執行狀態。而這些功能模組的執行可以脫離主程式而執行,為了保持後臺執行,且不干預系統回收程式資源的前提下,我們將這些功能拆分到小而獨立的程式當中。

滿足什麼條件才需要拆分獨立程式呢?

  • 需要後臺保活的核心模組;(如:Server push、後臺音樂播放或APP升級等)
  • 不穩定的新功能;(為了不影響主功能的正常使用,會選擇性的放到獨立程式中)
  • 部分佔用資源比較大的功能模組;(如:WebView,相簿等)

Android上有哪些跨程式通訊方式?

在Android應用程式中跨程式通訊是非常常見的,我們常用的四大元件均支援跨程式通訊。本文中我們重點看下Service跨程式通訊方式。

Android提供的Service跨程式呼叫方式:

  • 通過AIDL定義跨程式介面呼叫跨程式邏輯;
  • 通過Messenger呼叫跨程式邏輯;

通過AIDL定義跨程式介面呼叫跨程式邏輯

通過AIDL定義跨程式介面提供給業務呼叫是Android應用程式開發中,最為常用的跨程式呼叫實現的方式,AIDL介面提供了同步與非同步呼叫的支援,基本能滿足所有的跨程式呼叫需求。

通過AIDL介面呼叫跨程式邏輯需要如下幾個步驟:

  1. 連線Service;
  2. 等待Service連線成功後,持有連線遠端Service的Binder物件的引用;
  3. 通過Binder物件跨程式呼叫遠端邏輯;

程式碼實現需要如下幾個步驟:

  1. 定義AIDL介面;
  2. 在Service端實現遠端介面邏輯;
  3. 在業務層中呼叫AIDL介面;

AIDL呼叫模型圖

通過Messenger呼叫跨程式邏輯

通過Messenger呼叫跨程式邏輯需要如下幾個步驟:

  1. 連線Service;
  2. 等待Service連線成功後,將遠端Service的Binder物件繫結到Messenger上;
  3. 通過Messenger傳送遠端呼叫Message,呼叫遠端程式邏輯;

程式碼實現需要如下幾個步驟:

  1. 在Service端實現遠端介面邏輯;
  2. 在業務層中呼叫Messenger.send(Message)介面向遠端發出呼叫命令以實現跨程式呼叫;

Messenger呼叫模型圖

IPCInvoker如何呼叫跨程式邏輯

Android系統直接提供的AIDL和Messenger的方式存在的一些問題:

  • 呼叫一個跨程式介面時,需要理解Service的連線狀態;
  • 需要等待Service連線成功後,才能非同步發起跨程式呼叫;
  • AIDL和Messenger的跨程式實現都是C/S的設計,跨程式邏輯需要預先寫在Service端,造成一定的程式碼耦合問題;
  • 在超過2條程式的複雜程式呼叫模型中,寫跨程式程式碼及其複雜;
  • 程式碼邏輯不易維護;

最初設計IPCInvoker就是為了解決AIDL和Messenger存在的問題,讓跨程式呼叫變得更簡單。下面我們來看看IPCInvoker是如何做跨程式呼叫的。

IPCInvoker呼叫模型圖

從IPCInvoker呼叫模型圖中,可以看出是AIDL的一個擴充套件,Service端作為Task執行的容器,而由呼叫者來決定Task的邏輯實現。下面一起看一下IPCInvoker的簡單使用。

同步呼叫跨程式邏輯

public class IPCInvokeSample_InvokeByType {

    private static final String TAG = "IPCInvokerSample.IPCInvokeSample_InvokeByType";

    public static void invokeSync() {
        Bundle bundle = new Bundle();
        bundle.putString("name", "AlbieLiang");
        bundle.putInt("pid", android.os.Process.myPid());
        IPCString result = IPCInvoker.invokeSync(PushProcessIPCService.PROCESS_NAME,
                    bundle, IPCRemoteInvoke_BuildString.class);
        Log.i(TAG, "invoke result : %s", result);
    }

    private static class IPCRemoteInvoke_BuildString implements IPCRemoteSyncInvoke<Bundle, IPCString> {

        @Override
        public IPCString invoke(Bundle data) {
            String msg = String.format("name:%s|fromPid:%s|curPid:%s",
                        data.getString("name"), data.getInt("pid"), android.os.Process.myPid());
            Log.i(TAG, "build String : %s", msg);
            return new IPCString(msg);
        }
    }
}
複製程式碼

IPCInvoker實現跨程式呼叫主要分為兩部分:

  • 示例中的IPCRemoteInvoke_BuildString類中的invoke函式的實現就是跨程式邏輯,這裡只是輸出了一行log,並把msg作為返回值return了;
  • 示例中IPCString result = IPCInvoker.invokeSync(xxx)便是跨程式呼叫,這裡的呼叫把需要在遠端程式執行的邏輯的class作為引數傳入了IPCInvoker。

看到上面示例程式碼,是不是根本沒有感覺到這是在寫跨程式程式碼?也沒看到有連線Service的過程了!對的IPCInvoker設計的初衷就是讓跨程式呼叫變得簡單,就像Handler.post一個Runnable一樣簡單。

如果大家進一步思考上述示例中的程式碼,並對比AIDL和Messenger的跨程式實現,可以很明顯看出IPCInvoker的跨程式實現是高內聚的,跨程式邏輯不用寫在Service裡面,這樣業務邏輯就不用與Service產生任何的耦合了,Service在IPCInvoker框架中只是一個執行遠端邏輯的容器。

下面我們在看一下IPCInvoker非同步呼叫跨程式邏輯程式碼是如何寫的

非同步呼叫跨程式邏輯

public class IPCInvokeSample_InvokeByType {

    private static final String TAG = "IPCInvokerSample.IPCInvokeSample_InvokeByType";

    public static void invokeAsync() {
        Bundle bundle = new Bundle();
        bundle.putString("name", "AlbieLiang");
        bundle.putInt("pid", android.os.Process.myPid());
        IPCInvoker.invokeAsync(PushProcessIPCService.PROCESS_NAME, bundle,
                IPCRemoteInvoke_PrintSomething.class, new IPCRemoteInvokeCallback<IPCString>() {
            @Override
            public void onCallback(IPCString data) {
                Log.i(TAG, "onCallback : %s", data.value);
            }
        });
    }

    private static class IPCRemoteInvoke_PrintSomething implements IPCRemoteAsyncInvoke<Bundle, IPCString> {

        @Override
        public void invoke(Bundle data, IPCRemoteInvokeCallback<IPCString> callback) {
            String result = String.format("name:%s|fromPid:%s|curPid:%s",
                        data.getString("name"), data.getInt("pid"), android.os.Process.myPid());
            callback.onCallback(new IPCString(result));
        }
    }
}
複製程式碼

非同步呼叫是通過呼叫IPCInvoker.invokeAsync實現的,這個介面與IPCInvoker.invokeSync相比多了一個IPCRemoteInvokeCallback引數,IPCRemoteInvokeCallback的onCallback函式的回撥依賴遠端邏輯的主動呼叫,onCallback可以被多次呼叫。

上面使用的是IPCInvoker中最為基本和最簡單的兩個介面IPCInvoker.invokeSyncIPCInvoker.invokeAsync,可以很明顯看出IPCInvoker相比普通的AIDL和Messenger實現的跨程式呼叫更為直觀,介面更容易使用。

IPCInvoker元件裡面還包括了幾大模組:

最後

到目前為止本文還沒介紹如何接入IPCInvoker,其實IPCInvoker的接入也是非常簡單的,這裡就不展開說明了,大家可以通過閱讀《接入IPCInvoker》來接入IPCInvoker。IPCInvoker的wiki文件可以到《IPCInvoker wiki》中詳細閱讀。

歡迎使用IPCInvoker,有興趣的同學可以一起維護該專案。(專案地址為github.com/AlbieLiang/…

相關文章