1. 程式人生 > 實用技巧 >rabbitmq 延時佇列 外掛方式實現 每條訊息都延時自己時間

rabbitmq 延時佇列 外掛方式實現 每條訊息都延時自己時間

CmProcess是 Android 一個跨程式通訊框架,整體程式碼比較簡單,總共 20 多個類,能夠很好的便於我們去了解跨程式實現的原理。

個人猜測 CmProcess 也是借鑑了 VirtualApp(該 APP 很強大,是一個沙盒,可以在裡面安裝其他 apk) 的原始碼,從中整理出來一套通訊方案。VirtualAPP 功能很強大,實現比較複雜,需要較深的 framework 方面的知識才好理解。

按作者所說,CmProcess是更方便更簡潔的 Android 程式通訊方案,無需進行 bindService() 操作,不用定義 Service,也不需要定義 aidl。 支援 IPC 級的 Callback,並且支援跨程式的事件匯流排。可同步獲取服務。採用面向介面方式進行服務註冊與呼叫,服務呼叫方和使用者完全解耦。

基礎知識準備:

多程式:

Android多程式概念:一般一個 app 只有一個程式,所有的 components 都執行在同一個程式中,程式名稱就是 app 包名。但是每一個程式都有記憶體的限制,如果一個程式的記憶體超過了這個限制的時候就會報 OOM 錯誤。為瞭解決記憶體限制的問題,Android 引入了多程式的概念,將佔用記憶體的操作放在一個單獨的程式中分擔主程式的壓力。

多程式的好處:

  • 分擔主程式的記憶體壓力。

  • 常駐後臺任務。

  • 守護程式,主程式和守護程式相互監視,有一方被殺就重新啟動它。

  • 多麼塊,對有風險的模組放在單獨程式,崩潰後不會影響主程式的執行。

多程式的缺點:

  • Applicaton的重新建立,每個程式有自己獨立的virtual machine,每次建立新的程式就像建立一個新的Application

  • 靜態成員變數和單例模式失效,每個程式有自己獨立的虛擬機器器,不同虛擬機器器在記憶體分配上有不同的地址空間,這就導致不同虛擬機器器在訪問同一個物件時會產生多分副本。

  • SharedPreference的可靠性下降,不支援多程式

  • 執行緒同步機制失效

Bundle類

bundle 定義 bundle 是一個 final 類,final 類通常功能是完整的,它們不能被繼承。Java 中有許多類是 final 的,譬如 String, Interger 以及其他包裝類。

public final class Bundle extends BaseBundle implements Cloneable, Parcelable

bundle 傳遞的資料可以是 boolean、byte、int、long、float、double、string 等基本型別或它們對應的陣列,也可以是物件或物件陣列。但是如果傳遞物件或物件陣列,該物件必須實現 Serializable 或 Parcelable 介面。由 Bundle 定義我們也可以看到其實現了 Parcelable 介面,所以支援實現了Parcelable 介面的物件。
因此當我們在一個程式中啟動了另外一個程式的 Activity、Service、Receiver,我們就可以在 Bundle 中附加我們需要傳輸給遠端程式的資訊(前提是能夠被序列化)並通過 Intent 傳送出去。

AIDL

對於 AIDL 還沒接觸過的小夥伴,可以看看Android AIDL 例項與原理分析,可以讓你有個印象。

程式碼解析

首先來看三個 AIDL 介面:

1、IEventReceiver:事件接收器

// 事件接受器
interface IEventReceiver {
   // 這裡的 event 是 bundle 型別
void onEventReceive(String key,in Bundle event);
}

2、IPCCallback:看名字也可以看出來是跨程式 callback

interface IPCCallback {
   // result 也是 bundle
void onSuccess(in Bundle result);
void onFail(String reason);
}

3、IServiceFetcher:獲取服務的。可以再此進行註冊。

interface IServiceFetcher {
   // service 是 Ibinder 型別
android.os.IBinder getService(java.lang.String name);
   // 註冊服務
void addService(java.lang.String name, android.os.IBinder service);
   // 添加回調
void addEventListener(java.lang.String name, android.os.IBinder service);
   // 移除 service
void removeService(java.lang.String name);
   // 移除回撥
void removeEventListener(java.lang.String name);
  // 傳送訊息
void post(String key,in Bundle result);
}

啟動分析

根據程式碼可知,咱們有三個程式,分別是:

  • com.ipc.code:vc :TestActivity 執行所在的程式;這是屬於使用者測的。

  • com.ipc.code:vm : 也就是BinderProvider 存在的程式;IPCBus 也在該程式,主要是用於儲存和傳遞資料

  • com.ipc.code :MainActivity 主程式;也就是

每個程式在初始化的時候,都會走一遍Application 的初始化,因此如果需要對程式做啥操作,可以判斷出具體的程式,然後做一些額外的操作。對於 CmProcess ,所有程式的初始化邏輯都是一樣的。

public class App extends Application {
private static final String TAG = "App"; @Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// 先啟動主程式,之後才啟動其他程式
VCore.init(base);
}
}

啟動過程中,會主動為每個程式註冊回撥,注意是每個程式。

該 init 方法最終會走入到下面的方法中:

    public void startup(Context context) {
if (!isStartUp) {
// 在主執行緒啟動,每個程式都有一個自己的主執行緒
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("VirtualCore.startup() must called in main thread.");
} ServiceManagerNative.SERVICE_CP_AUTH = context.getPackageName() + "." + ServiceManagerNative.SERVICE_DEF_AUTH;
this.context = context;
// 傳入了一個 cache 例項,這個例項是隻有主執行緒有的
IPCBus.initialize(new IServerCache() {
@Override
public void join(String serverName, IBinder binder) {
ServiceManagerNative.addService(serverName, binder);
} @Override
public void joinLocal(String serverName, Object object) {
ServiceCache.addLocalService(serverName,object);
} @Override
public void removeService(String serverName) {
ServiceManagerNative.removeService(serverName);
} @Override
public void removeLocalService(String serverName) {
ServiceCache.removeLocalService(serverName);
} @Override
public IBinder query(String serverName) {
return ServiceManagerNative.getService(serverName);
} @Override
public Object queryLocal(String serverName) {
return ServiceCache.getLocalService(serverName);
} @Override
public void post(String key,Bundle bundle) {
ServiceManagerNative.post(key,bundle);
}
});
       // 這裡是根據程式名字新增註冊的事件接收器
ServiceManagerNative.addEventListener(AppUtil.getProcessName(context, Process.myPid()), EventReceiver.getInstance());
isStartUp = true;
}
}

這裡整個邏輯很簡單,就是在主執行緒初始化了IPCBus,然後給該程式註冊了一個事件分發的監聽。

EventReceiver

public class EventReceiver extends IEventReceiver.Stub {

    private static final String TAG = "EventReceiver";

    private static final EventReceiver EVENT_RECEIVER = new EventReceiver();

    private EventReceiver(){}

    public static final EventReceiver getInstance(){
return EVENT_RECEIVER;
} @Override
public void onEventReceive(String key,Bundle event) {
EventCenter.onEventReceive(key,event);
}
}

整個類的程式碼很簡單。但是要注意的是,其繼承了IEventReceiver.Stub,說明他具有跨程式傳輸的能力。主要就是通過EventCenter 來分發訊息。

由於每個程式都會走一遍初始化邏輯,所以每個程式都註冊了事件的接收。

ServiceManagerNative

從名字也可以看出來,這個跟我們平時看到的ServiceManager 很像。主要就是用來獲取 service 和註冊 listener 的。

    public static void addEventListener(String name, IBinder service) {
IServiceFetcher fetcher = getServiceFetcher();
if (fetcher != null) {
try {
fetcher.addEventListener(name, service);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}

首先是呼叫getServiceFetcher 來獲取最終儲存服務的 fetcher。

註冊回撥的時候,會先獲取是否存在 (binder)ServiceFetcher ,在將其轉化為本地 binder;這樣 ServiceFetcher 的管理器就可以用了。

   private static IServiceFetcher getServiceFetcher() {
if (sFetcher == null || !sFetcher.asBinder().isBinderAlive()) {
synchronized (ServiceManagerNative.class) {
Context context = VirtualCore.get().getContext();
Bundle response = new ProviderCall.Builder(context, SERVICE_CP_AUTH).methodName("@").call();
if (response != null) {
IBinder binder = BundleCompat.getBinder(response, "_VM_|_binder_");
linkBinderDied(binder);
sFetcher = IServiceFetcher.Stub.asInterface(binder);
}
}
}
return sFetcher;
}

首先是看ProviderCall.Builder(context, SERVICE_CP_AUTH).methodName("@").call(),他最終會呼叫下面的方法:

//ContentProviderCompat
public static Bundle call(Context context, Uri uri, String method, String arg, Bundle extras) {
     // 這裡還區分了版本
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return context.getContentResolver().call(uri, method, arg, extras);
}
        ContentProviderClient client = crazyAcquireContentProvider(context, uri);  // 這裡會不斷重試最終會獲得對 BinderProvider 的引用
Bundle res = null;
try {
// 通過約定好的方法名字獲得bindle
res = client.call(method, arg, extras);
} catch (RemoteException e) {
e.printStackTrace();
} finally {
releaseQuietly(client);
}
return res;
}

BinderProvider

下面看下 BinderProvider 的 call 方法。

新建了一個 bundle 物件,然後將 binder 儲存在裡面。注意這是通過跨程式呼叫,最終將 bundle 傳回主程式,然後拿到了ServiceFetcher 的 binder,並將其轉為本地 binder。

可以發現這裡對於方法名是 "@" 時,就會返回 bundle ,否則就是返回 null 。

   public Bundle call(String method,  String arg,  Bundle extras) {
     if ("@".equals(method)) {
Bundle bundle = new Bundle();
BundleCompat.putBinder(bundle, "_VM_|_binder_", mServiceFetcher);
       return bundle;
}
return null;
}

簡單來說,就是大家都通過 binderProvider 這個程式來儲存對於回撥的註冊,儲存是基於進城名字來的,因此可以保證不會被覆蓋

此處的mServiceFetcher 是BinderProvider 內部內的例項,但是其繼承了IServiceFetcher.Stub,因此也就有了跨程式的能力。

到這裡,理一下前面的邏輯:

 ServiceManagerNative.addEventListener(AppUtil.getProcessName(context, Process.myPid()), EventReceiver.getInstance());

某個程式的主執行緒呼叫這個方法,所做的具體事情如下:

  1. 通過 binder 拿到了binderProvider 中的 IServiceFetcher.Stub 的例項;

  2. 向IServiceFetcher.Stub 註冊回撥,該回撥最終會被儲存binderProvider 程式裡面。

BinderProvider 啟動分析

上面介紹了其是怎麼將 listener 註冊到 binderProvider 程式的,但是並沒有講到接下去我們看下 BinderProvider 的啟動過程,

下圖是ContentProvider 的啟動流程。當我們在主程式想獲取 server 的時候,這時候,會看看 provider 存不存在,沒有的就會進行啟動,同時會走 Application 的初始化邏輯,

具體我們可以看下面這個啟動流程圖:

  • Application 的 attachBaseContext 方法是優先執行的;

  • ContentProvider 的 onCreate的方法 比 Application的onCreate的方法先執行;

  • Activity、Service 的 onCreate 方法以及 BroadcastReceiver 的 onReceive 方法,是在 MainApplication 的 onCreate 方法之後執行的;

  • 呼叫流程為: Application 的 attachBaseContext ---> ContentProvider 的 onCreate ----> Application 的 onCreate ---> Activity、Service 等的 onCreate(Activity 和 Service 不分先後);

這裡主要是梳理了下 provider 的啟動過程,並沒有很細講,但是有必要了解一下。

MainActivity

接下去,開始看MainActivity 裡面的程式碼。

呼叫registerService 註冊服務,傳入IPayManager.class 和MainActivity;記得MainActivity 也實現了IPayManager 介面。

VCore.getCore().registerService(IPayManager.class, this);

看下,裡面的具體程式碼邏輯

   // Vcore
public VCore registerService(Class<?> interfaceClass, Object server){ if (VirtualCore.get().getContext() == null){
return this;
}
Object o = IPCBus.getLocalService(interfaceClass);
     // 如果是第一次呼叫就會返回空
IBinder service = ServiceManagerNative.getService(interfaceClass.getName());
if (service != null && o != null){
return this;
}
IPCBus.registerLocal(interfaceClass,server);
     // 這裡的註冊就是把 server 儲存到 binder 中
IPCBus.register(interfaceClass,server);
return this;
}

這裡使用了一個registerLocal和register 方法,但是本質上兩個方法是有區別的。registerLocal 意思很明確,就是本地ServiceCache儲存一份。但是register,確實做了一些額外的操作。

 public static void register(Class<?> interfaceClass, Object server) {
checkInitialized();
ServerInterface serverInterface = new ServerInterface(interfaceClass);
     // 這裡主要是獲取一個 binder,或者換句話來說,採用 binder 來儲存相關資料
TransformBinder binder = new TransformBinder(serverInterface, server);
     // 這裡就是把 binder 儲存到 binderProvider
sCache.join(serverInterface.getInterfaceName(), binder);
}

首先這裡建立了一個ServerInterface 例項,該例項內部儲存了傳過了來的介面和介面的方法,並將方法和 code 聯絡在一起。

    public ServerInterface(Class<?> interfaceClass) {
this.interfaceClass = interfaceClass;
Method[] methods = interfaceClass.getMethods();
codeToInterfaceMethod = new SparseArray<>(methods.length);
methodToIPCMethodMap = new HashMap<>(methods.length);
for (int i = 0; i < methods.length; i++) {
       // 這裡每一個方法都有一個 code
int code = Binder.FIRST_CALL_TRANSACTION + i;
       // 組成一個 ipcMenhod
IPCMethod ipcMethod = new IPCMethod(code, methods[i], interfaceClass.getName());
codeToInterfaceMethod.put(code, ipcMethod);
       // 儲存他們的對映關係
methodToIPCMethodMap.put(methods[i], ipcMethod);
}
}

同時利用TransformBinder 將介面和 例項儲存到 binder 中。再將 binder 和 介面名字 儲存到ServiceCache 中。

註冊完以後,下面是呼叫獲取本地服務:

// 其實 service 本質還是這個 MainActivity
IPayManager service = VCore.getCore().getLocalService(IPayManager.class);

最後註冊了一個回撥:

 VCore.getCore().subscribe("key", new EventCallback() {
@Override
public void onEventCallBack(Bundle event) {
}
});

最終EventCenter 會儲存相關資訊;

TestActivity

最後啟動 TestActivity ,這個是在另一個程式。在 onCreate 裡面呼叫下面的方法:

IPayManager service = VCore.getCore().getService(IPayManager.class);

程式剛剛建立,我們看看是怎麼獲取服務的:

// Vcore
public <T> T getService(Class<T> ipcClass){
T localService = IPCBus.getLocalService(ipcClass);
if (localService != null){
return localService;
}
return VManager.get().getService(ipcClass);
}

這裡很明確,本地肯定是沒有的,因此,最後會從 VManager 中獲取:

// VManager
public <T> T getService(Class<T> ipcClass) {
T t = IPCBus.get(ipcClass);
if (t != null){
return t;
}
IPCSingleton<T> tipcSingleton = mIPCSingletonArrayMap.get(ipcClass);
if (tipcSingleton == null){
tipcSingleton = new IPCSingleton<>(ipcClass);
mIPCSingletonArrayMap.put(ipcClass,tipcSingleton);
}
return tipcSingleton.get();
}

接下去我們看下IPCSingleton 相關邏輯

// IPCSingleton
public T get() {
if (instance == null) {
synchronized (this) {
if (instance == null) {
instance = IPCBus.get(ipcClass);
}
}
}
return instance;
}

這是一個單例,目的也很明確,就是隻獲取一次,可以看到後面又調到了 IPCBus 裡面。

//   IPCBus
public static <T> T get(Class<?> interfaceClass) {
checkInitialized();
ServerInterface serverInterface = new ServerInterface(interfaceClass);
// 這裡獲取的 binder 應該是 TransformBinder
IBinder binder = sCache.query(serverInterface.getInterfaceName());
if (binder == null) {
return null;
}
     // 這裡使用了動態代理
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new IPCInvocationBridge(serverInterface, binder));
}

這裡採用了動態代理創造了一個例項,最終返回的例項被儲存在一個單例中。

可以看到,這裡回去查詢存不存在 binder。

// VirtualCore
public IBinder query(String serverName) {
return ServiceManagerNative.getService(serverName);
}

還是通過ServiceManagerNative 來獲取的 service;這裡又回到我們之前分析過的邏輯。先從 binderProvider 獲取fetcher, 也就是 ServiceFetcher。

IServiceFetcher fetcher = getServiceFetcher();

它也會從ServiceFetcher 中獲取到 binder ,而這個 binder 就是之前我們儲存的TransformBinder 。拿到這個之後,還是一樣,將其轉化為該程式的本地 binder .

 // BinderProvider
private class ServiceFetcher extends IServiceFetcher.Stub {

最後,我們通過動態代理的形式,建立了一個 IPayManager 的例項。

return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new IPCInvocationBridge(serverInterface, binder));

這裡需要注意的是IPCInvocationBridge 繼承自InvocationHandler。

拿到後,開始呼叫對應的方法:

if (service != null){
Log.d(TAG, "onCreate: shentest before vcore " + AppUtil.getAppName(this));
// 首先這個 service 是跨程式呼叫的,怎麼才通知到其他元件?這裡大家可以思考下
service.pay(5000, new BaseCallback() {
@Override
public void onSucceed(Bundle result) {
textview.setText(result.getString("pay"));
Bundle bundle = new Bundle();
bundle.putString("name", "DoDo");
VCore.getCore().post("key",bundle);
} @Override
public void onFailed(String reason) { }
});
}

呼叫 service.pay 的時候,就會呼叫動態代理中的 invoke 方法:

    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
IPCMethod ipcMethod = serverInterface.getIPCMethod(method);
if (ipcMethod == null) {
throw new IllegalStateException("Can not found the ipc method : " + method.getDeclaringClass().getName() + "@" + method.getName());
}
     // 這裡很關鍵
return ipcMethod.callRemote(binder, args);
}

首先根據方法名獲得ipcMethod,裡面儲存了方法的 code,介面名字,引數,返回值。接著呼叫了 ipcMethod.callRemote, 該方法又會呼叫:

// IPCMethod
// 這個方法很重要,需要理解其實現過程
public Object callRemote(IBinder server, Object[] args) throws RemoteException {
Parcel data = Parcel.obtain(); // 獲取一個新的 parcel 物件
Parcel reply = Parcel.obtain();
Object result;
try {
data.writeInterfaceToken(interfaceName);
data.writeArray(args);
       // 這裡 server 就是 transformBinder
server.transact(code, data, reply, 0);
reply.readException();
result = readValue(reply);
if (resultConverter != null) {
result = resultConverter.convert(result);
}
} finally {
data.recycle();
reply.recycle();
}
return result;
}

code 變數用於標識客戶端期望呼叫服務端的哪個函式,因此,雙方需要約定一組 int 值,不同的值代表不同的服務端函式,該值和客戶端的 transact() 函式中第一個引數 code 的值是一致的。

enforceInterface() 是為了某種校驗,它與客戶端的 writeInterfaceToken() 對應,具體見下一小節。
readString() 用於從包裹中取出一個字串。如果該 IPC 呼叫的客戶端期望返回一些結果,則可以在返回包裹 reply 中呼叫 Parcel 提供的相關函式寫入相應的結果。 Parcel.writeXXX();

現在要看的是怎麼通過 binder 一步一步拿到引數。

使用 Parcel 一般是通過 Parcel.obtain() 從物件池中獲取一個新的 Parcel 物件,如果物件池中沒有則直接 new 的 Parcel 則直接建立新的一個 Parcel 物件,並且會自動建立一個Parcel-Native 物件。

writeInterfaceToken 用於寫入 IBinder 介面標誌,所帶引數是 String 型別的,如 IServiceManager.descriptor = "android.os.IServiceManager"。

之前說的 code 在這裡用上了,code 是一個私有變數,跟 method 繫結在一起的。

中間有個 server.transact(code, data, reply, 0); 該方法實現了跨程式呼叫,最終會走到 binderProvider 的下面onTransact方法:

// TransformBinder 執行在主程式
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
     // 回撥進來後,就到了 MainActivity 的程式
if (code == INTERFACE_TRANSACTION) {
reply.writeString(serverInterface.getInterfaceName());
return true;
}
IPCMethod method = serverInterface.getIPCMethod(code);
if (method != null) {
try {
method.handleTransact(server, data, reply);
} catch (Throwable e) {
e.printStackTrace();
}
return true;
}
return super.onTransact(code, data, reply, flags);
}

這裡主要是根據 code 來獲取到是哪個方法被呼叫了,下面才是真正的處理。

// IPCMethod
public void handleTransact(Object server, Parcel data, Parcel reply) {
data.enforceInterface(interfaceName); // 確保是目標介面
Object[] parameters = data.readArray(getClass().getClassLoader());
if (parameters != null && parameters.length > 0) {
for (int i = 0; i < parameters.length; i++) {
if (converters[i] != null) {
parameters[i] = converters[i].convert(parameters[i]);
}
          // 如果引數裡麵包含有 binder
if (parameters[i] instanceof IBinder){
parameters[i] = IPCCallback.Stub.asInterface(((IBinder)parameters[i]));
}
}
}
try {
// 最終通過反射的形式實現了的呼叫
// 其實最主要的是通過 binder 拿到引數,然後知道對方呼叫的是哪個方法。
// 現在要分析的是,他怎麼將資料傳過來的
Object res = method.invoke(server, parameters);
reply.writeNoException();
reply.writeValue(res);
} catch (IllegalAccessException e) {
e.printStackTrace();
reply.writeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
reply.writeException(e);
}
}

看看 convert 裡面的操作:

  public Object convert(Object param) {
if (param != null) {
if (asInterfaceMethod == null) {
synchronized (this) {
if (asInterfaceMethod == null) {
                 // 找到 asInterface 方法
asInterfaceMethod = findAsInterfaceMethod(type);
}
}
}
try {
            // 因為 asInterface 方法是靜態方法,所以物件可以傳入空,最終轉變成所需要的引數型別
return asInterfaceMethod.invoke(null, param);
} catch (Throwable e) {
throw new IllegalStateException(e);
}
}
return null;
}

通過 convert 這個一呼叫,就轉變成我們所需要的引數了。

  private static Method findAsInterfaceMethod(Class<?> type) {
for (Class<?> innerClass : type.getDeclaredClasses()) {
// public static class Stub extends Binder implements IType
if (Modifier.isStatic(innerClass.getModifiers())
&& Binder.class.isAssignableFrom(innerClass)
&& type.isAssignableFrom(innerClass)) {
// public static IType asInterface(android.os.IBinder obj)
for (Method method : innerClass.getDeclaredMethods()) {
if (Modifier.isStatic(method.getModifiers())) {
Class<?>[] types = method.getParameterTypes();
if (types.length == 1 && types[0] == IBinder.class) {
return method;
}
}
}
}
}
throw new IllegalStateException("Can not found the " + type.getName() + "$Stub.asInterface method.");
}

findAsInterfaceMethod 通過層層篩選,最終獲得需要的那個方法:

public static com.cmprocess.ipc.server.IPCCallback com.cmprocess.ipc.server.IPCCallback$Stub.asInterface(android.os.IBinder)

通過 invoke 方法,終將獲得了我們需要的型別。

這裡 server 就是 mainActivity。在把對應的引數傳進去即可。最終調到了mainActivity 裡面的 pay 方法。

 public void pay(final int count, final IPCCallback callBack) {

        new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
Bundle bundle = new Bundle();
bundle.putString("pay", count + 100 + "");
try {
// callback 也是一個binder
callBack.onSuccess(bundle);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}).start();
}

此處,callback 也是一個binder,呼叫成功後,傳送了post。 其實最終也是呼叫了ServiceFetcher 。

// TestActivity
VCore.getCore().post("key",bundle);

其實也是通過 binder 來進行傳送訊息的。由於每個程式都註冊了訊息回撥,因此,每個程式都會收到。

// ServiceCache
public static synchronized void sendEvent(String key,Bundle event){
if (sEventCache.isEmpty()){
return;
}
for (IBinder binder:sEventCache.values()){
IEventReceiver eventReceiver = IEventReceiver.Stub.asInterface(binder);
try {
eventReceiver.onEventReceive(key, event);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}

EventReceiver 存在於每個程式,因此,對於 binderprovider 來說都是客戶端,其他程式則是服務端。最終EventCenter 會根據 KEY 值來做分發。

到這裡整個流程就基本講完了。

不過我們發現還有兩個 service ,他們的作用是幹嘛用的呢?感覺是用來保活的,防止 provider 死了。

參考文章:

Android系統中的Application和四大元件一些方法的啟動順序和一些坑

Android 核心--Binder架構分析

徹底理解Android Binder通訊架構

ContentProvider的啟動過程分析

Parcel類詳解