一個APP為什麼需要多條程式?
如果一條程式能夠擁有足夠多的資源,且不會被系統kill掉的話,讓程式執行在一條程式上是最好的選擇。但是系統資源是按程式來分配的,每條程式資源分配是有個上限的,而且當我們的APP退到後臺之後,系統會根據系統資源使用情況,回收部分後臺程式資源。
具有推送或後臺播放音樂等功能的APP,在APP被退到後臺之後,為了保持良好的使用者體驗,則需要在後臺保持執行狀態。而這些功能模組的執行可以脫離主程式而執行,為了保持後臺執行,且不干預系統回收程式資源的前提下,我們將這些功能拆分到小而獨立的程式當中。
滿足什麼條件才需要拆分獨立程式呢?
- 需要後臺保活的核心模組;(如:Server push、後臺音樂播放或APP升級等)
- 不穩定的新功能;(為了不影響主功能的正常使用,會選擇性的放到獨立程式中)
- 部分佔用資源比較大的功能模組;(如:WebView,相簿等)
Android上有哪些跨程式通訊方式?
在Android應用程式中跨程式通訊是非常常見的,我們常用的四大元件均支援跨程式通訊。本文中我們重點看下Service跨程式通訊方式。
Android提供的Service跨程式呼叫方式:
- 通過AIDL定義跨程式介面呼叫跨程式邏輯;
- 通過Messenger呼叫跨程式邏輯;
通過AIDL定義跨程式介面呼叫跨程式邏輯
通過AIDL定義跨程式介面提供給業務呼叫是Android應用程式開發中,最為常用的跨程式呼叫實現的方式,AIDL介面提供了同步與非同步呼叫的支援,基本能滿足所有的跨程式呼叫需求。
通過AIDL介面呼叫跨程式邏輯需要如下幾個步驟:
- 連線Service;
- 等待Service連線成功後,持有連線遠端Service的Binder物件的引用;
- 通過Binder物件跨程式呼叫遠端邏輯;
程式碼實現需要如下幾個步驟:
- 定義AIDL介面;
- 在Service端實現遠端介面邏輯;
- 在業務層中呼叫AIDL介面;
通過Messenger呼叫跨程式邏輯
通過Messenger呼叫跨程式邏輯需要如下幾個步驟:
- 連線Service;
- 等待Service連線成功後,將遠端Service的Binder物件繫結到Messenger上;
- 通過Messenger傳送遠端呼叫Message,呼叫遠端程式邏輯;
程式碼實現需要如下幾個步驟:
- 在Service端實現遠端介面邏輯;
- 在業務層中呼叫
Messenger.send(Message)
介面向遠端發出呼叫命令以實現跨程式呼叫;
IPCInvoker如何呼叫跨程式邏輯
Android系統直接提供的AIDL和Messenger的方式存在的一些問題:
- 呼叫一個跨程式介面時,需要理解Service的連線狀態;
- 需要等待Service連線成功後,才能非同步發起跨程式呼叫;
- AIDL和Messenger的跨程式實現都是C/S的設計,跨程式邏輯需要預先寫在Service端,造成一定的程式碼耦合問題;
- 在超過2條程式的複雜程式呼叫模型中,寫跨程式程式碼及其複雜;
- 程式碼邏輯不易維護;
最初設計IPCInvoker就是為了解決AIDL和Messenger存在的問題,讓跨程式呼叫變得更簡單。下面我們來看看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.invokeSync
和IPCInvoker.invokeAsync
,可以很明顯看出IPCInvoker相比普通的AIDL和Messenger實現的跨程式呼叫更為直觀,介面更容易使用。
IPCInvoker元件裡面還包括了幾大模組:
最後
到目前為止本文還沒介紹如何接入IPCInvoker,其實IPCInvoker的接入也是非常簡單的,這裡就不展開說明了,大家可以通過閱讀《接入IPCInvoker》來接入IPCInvoker。IPCInvoker的wiki文件可以到《IPCInvoker wiki》中詳細閱讀。
歡迎使用IPCInvoker,有興趣的同學可以一起維護該專案。(專案地址為github.com/AlbieLiang/…)