詳解 CmProcess 跨程式通訊的實現

huansky發表於2020-07-19

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 死了。

 

參考文章:

 

ContentProvider的啟動過程分析

Parcel類詳解

 

 

相關文章