[Android]你不知道的Android進程化(5)--進程通訊Messenger框架

你們好,我係蒼王。
如下是我這個系列的相關文章,有興趣能夠參考一下,能夠給個喜歡或者關注個人文章。java

[Android]如何作一個崩潰率少於千分之三噶應用app--章節列表編程

宣傳一波新書,裏面介紹了衆多組件化編程技術,以及讓你對工程架構認識和理解有新的提高。安全

                    
                                  Android組件化架構熱賣中

組件化羣1已經滿員,能夠加羣2 763094035
服務器



上一節,介紹了使用AIDL的進程通訊框架。
這一節給你們介紹Messenger的通訊框架,而Messenger其意思是「信使」的意思
使用Messenger的優點在於
1.實際傳遞的是Message,能夠複用信息池
2.支持信息回調
3.不須要編寫aidl架構

Messenger通訊原理圖


Messenger繼承了Parcelable接口,能夠做爲序列化對象用於傳輸。
這裏能夠傳入Handler,Handler裏面有包含IMessenger對象併發

/** * Create a new Messenger pointing to the given Handler. Any Message * objects sent through this Messenger will appear in the Handler as if * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had * been called directly. * * @param target The Handler that will receive sent messages. */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
複製代碼

或者傳入IBinder對象,Stub當中存在IMessenger對象app

/** * Create a Messenger from a raw IBinder, which had previously been * retrieved with {@link #getBinder}. * * @param target The IBinder this Messenger should communicate with. */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
複製代碼

實際上Handler中IMessager實現對象是MessengerImpl框架

final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }
複製代碼

這裏IMessenger是調用Handler的send方法來發送消息的。異步

private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }
複製代碼

每一個Message當中也包含了一個replyTo的變量用戶回調ide

/** * Optional Messenger where replies to this message can be sent. The * semantics of exactly how this is used are up to the sender and * receiver. */
    public Messenger replyTo;
複製代碼

就這幾個步驟Messenger獨立的實現了Parcelable和使用aidl的通訊方式

接下來咱們介紹一下Modular框架是用Messenger通訊設計。
不一樣於上一節介紹的ModularArchitecture的aidl中通訊是1對1的通訊,Modular提供的通訊框架是經過1對多的發送方式來傳遞的。

如下是Messenger的註冊流程圖


Messenger註冊流程圖

1.每一個模塊初始化時啓動一個消息隊列來監聽消息。

@Override
    public void init() {
        mBaseModule = this;
        //啓動線程接收消息
        mWorkThread = new WorkThread();
        mWorkThread.start();
        //註冊跳轉路由
        OkBus.getInstance().register(Constants.ROUTER_OPEN_URL, new Event() {
            @Override
            public void call(Message msg) {
                String url = (String) msg.obj;
               //實際跳轉使用的Router 
               Router.openLocalUrl(BaseAppModuleApp.getBaseApplication(), url);
            }
        }, Bus.UI); //線程參數
    }

    public class WorkThread extends Thread {
        Handler mHandler;
        public Messenger clientHandler;

        @Override
        public void run() {
            Looper.prepare();
            //每一個module都有接收消息處理ClientHandler
            mHandler = new ClientHandler();
            clientHandler = new Messenger(mHandler);
            if(resultRef!=null){
                try {
                    resultRef.set(clientHandler);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    //使用CountDownLatch喚醒機制保證線程安全
                    latch.countDown();
                }
            }
            Looper.loop();
        }

        public void quit() {
            mHandler.getLooper().quit();
        }
    }
複製代碼

2.綁定MessengerService做爲進程間管理,而且綁定每一個模塊的ServiceConnection

/** * 鏈接服務器 */
    public void connectService() {
        Intent intent = new Intent(MessengerService.class.getCanonicalName());// 5.0+ need explicit intent
        intent.setPackage(Constants.SERVICE_PACKAGE_NAME); // the package name of Remote Service
        //綁定MessengerService做爲進程間管理,而且綁定每一個模塊的ServiceConnection
        boolean mIsBound = bindService(intent, mBaseModule.mConnection, BIND_AUTO_CREATE);
        LogUtils.i(Constants.TAG + " connectService", " ServiceConnection-->bindService mIsBound: " + mIsBound);
    }
複製代碼

3.啓動MessengerService,啓動消息循環

@Override
    public void onCreate() {
        super.onCreate();
        LogUtils.i(Constants.TAG + " essengerService", "MessengerService -->onCreate");
        mWorkThread = new WorkThread();
        mWorkThread.start();
    }

    public class WorkThread extends Thread {
        public ServiceHandler mHandler;

        @Override
        public void run() {
            Looper.prepare();
            LogUtils.i(Constants.TAG + " essengerService", "MessengerService -->new ServiceHandler");
            //經過ServiceHandler來處理收到的消息
            mHandler = new ServiceHandler();
            Messenger mMessenger = new Messenger(mHandler);
          // OkBus.getInstance().mServiceMessenger = mMessenger;
            try {
                resultRef.set(mMessenger);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
            Looper.loop();
        }

        public void quit() {
            mHandler.getLooper().quit();
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        try {
            latch.await(10, TimeUnit.SECONDS); //最多等待10秒
        } catch (Exception e) { //等待中斷
            e.printStackTrace();
        }
        Messenger mMessenger = resultRef.get();
        return mMessenger.getBinder();
    }
複製代碼

4.初始化OkBus併發送消息註冊

public void initModule(BaseModule mBaseModule, Messenger mServiceMessenger, int mModuleId, Messenger mClientMessenger) {
        this.mServiceMessenger = mServiceMessenger;
        this.mModuleId = mModuleId;
        this.mBaseModule = mBaseModule;
        isModule.set(true);
        mBaseModule.isConnected.set(true);
        //線程池獲取信息
        Message msg = Message.obtain();
        Bundle data = new Bundle();
        data.putInt(Constants.REGISTER_ID, mModuleId);//註冊模塊信息
        msg.setData(data);
        msg.replyTo = mClientMessenger;   //將處理消息的Messenger綁定到消息上帶到服務端
        try {
            //發送到MessengerService中處理
            mServiceMessenger.send(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
複製代碼

5.ServiceHandler中維護一個對Service Messenger到多個Client Messenger的關係

@Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        try {
            Bundle bundle = msg.getData();
            int registerId = bundle.getInt(Constants.REGISTER_ID, -1);
            if (registerId > 0) {//註冊模塊類型的消息
                LogUtils.logOnUI(Constants.TAG, "handleMessage: msg = [收到註冊模塊類型的消息]: registerId: " + Integer.toHexString(registerId));
                //每一個模塊對應的ClientHandler
                Messenger client = msg.replyTo; 
                mClientMessengers.put(registerId, client);//存儲Client端接受處理消息的Messenger來發送Message到Client

                Message data = Message.obtain();
                Bundle mBundle = new Bundle();
                mBundle.putInt(Constants.REGISTER_RES, Constants.REGISTER_SEC);    //通知Client模塊註冊成功
                data.setData(mBundle);
                try {
                    client.send(data);  //回調註冊狀態給模塊
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
複製代碼

6.介紹的模塊註冊結果

public class ClientHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
        ……
        int resCode = bundle.getInt(Constants.REGISTER_RES, -1);
        if (resCode < 0) {//收到普通消息
        ……
        } else {//收到模塊註冊結果消息
            boolean isRegisterSec = resCode == Constants.REGISTER_SEC;
            if (isRegisterSec) {
                LogUtils.logOnUI(Constants.TAG, "handleMessage() : reply = [註冊成功]");
            }
        }
    }
}
複製代碼

7.其綁定完畢以後,將module信息註冊到OkBus,以後會使用afterConneted()來初始化想要接收的消息

public ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            LogUtils.logOnUI(Constants.TAG, "ServiceConnection-->onServiceConnected 已自動喚醒服務器");
            Messenger mServiceMessenger = new Messenger(service);
            OkBus.getInstance().initModule(mBaseModule, mServiceMessenger, getModuleId(), mWorkThread.clientHandler);
            afterConnected();
        }
    
複製代碼

8.經過ServiceBus來註冊其餘module會跨module調用過來的消息。

@Override public void afterConnected() {

        ServiceBus.getInstance().registerService(Constants.SERVICE_A_UID, msg -> {
            LogUtils.logOnUI(Constants.TAG, "afterConnected a 進程收到[服務請求]消息:ServiceMessage-->hello: " + Integer.toHexString(Math.abs(msg.what)));
            return "10086";
        });
    }
複製代碼

9.服務註冊後,能夠看到其會經過onEvent返回回調的msg對象(CallBack接口)

/** * 註冊服務 * * @param serviceId 服務id * @param callback 服務調用的回調 * @param <T> 服務返回的數據範型 */
    public <T> void registerService(final int serviceId, final CallBack<T> callback) {
        LogUtils.logOnUI(Constants.TAG, "註冊服務 " + Integer.toHexString(Math.abs(serviceId)));
        okBus.unRegister(serviceId);//服務提供者只能有一個
        okBus.register(serviceId, msg -> {
            //TODO 優化到子線程
            okBus.onEvent(serviceId - 1, callback.onCall(msg));
        });
    }
複製代碼

到這裏就介紹完初始化和註冊流程了。

接下來介紹一下消息發送的架構,注意一下的模塊服務規則

//==================模塊間的服務定義============//
    /** * 服務定義規則: * 一、服務的請求ID必須是負值(正值表示事件) * 二、服務的請求ID必須是奇數,偶數表示該服務的返回事件, * 即: requestID-1 = returnID * 例如 -0xa001表示服務請求 -0xa002表示-0xa001的服務返回 */
    public static final int SERVICE_A_UID = -0xa001;

        /** * 異步調用遠端服務 */
        findViewById(R.id.bt_1).setOnClickListener(v -> {
        //異步調用,
           ServiceBus.getInstance().fetchService(Constants.SERVICE_A_UID, msg -> {
                LogUtils.logOnUI(Constants.TAG, "b 進程收到[異步服務返回]消息: 獲取到的UID-->" + msg.obj);
                Toast.makeText(BModuleActivity.this,
                        "b 進程收到[異步服務返回]消息: 獲取到的UID-->" + msg.obj,
                        Toast.LENGTH_SHORT).show();
            });
        });
複製代碼

具體步驟
1.對ID的請求限制
2.module鏈接的判斷,沒有鏈接就嘗試鏈接
3.喚醒目標進程
4.註冊回調
5.通知目標模塊

跨模塊通訊
/** * 異步調用服務 * * @param serviceId 服務id * @param callback 回調 */
    public void fetchService(final int serviceId, final Event callback) {
        if (serviceId > 0 || serviceId % 2 == 0) {
            assert false : "請求ID必須是負奇值!";
            return;
        }
        if (okBus.isModule() && !okBus.isModuleConnected()) {
            LogUtils.logOnUI(Constants.TAG, "請求失敗,服務已經斷開連接,嘗試從新打開服務,進行請求");
            BaseAppModuleApp.getBaseApplication().connectService();
            return;
        }

        //自動喚醒目標進程
        if (okBus.isModule()) {
            String module_name = Integer.toHexString(Math.abs(serviceId)).substring(0, 1);
            noticeModule(module_name, serviceId, null);
        }

        //一、先註冊回調
        okBus.register(serviceId - 1, msg -> {
            callback.call(msg);
            okBus.unRegister(serviceId - 1);//服務是單次調用,觸發後即取消註冊
        });
        //二、通知目標模塊
        okBus.onEvent(serviceId);
    }
複製代碼

喚醒目標進程

/** * 喚醒目標進程 * * @param module_name 模塊名 * @param serviceId 服務ID * @param url 要打開的url */
    public void noticeModule(String module_name, int serviceId, String url) {
        Intent ait = new Intent(NoticeService.class.getCanonicalName());// 5.0+ need explicit intent //喚醒目標進程的服務Action名
        ait.setPackage(Constants.MODULE_PACKAGE_PRE + module_name);   //喚醒目標進程的包名
        //綁定包名
        BaseAppModuleApp.getBaseApplication().bindService(ait, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                if (service != null) {
                    LogUtils.logOnUI(Constants.TAG, "已經自動喚醒" + module_name);
                    Messenger moduleNameMessenger = new Messenger(service);
                    Message _msg = Message.obtain();
                    Bundle _data = new Bundle();
                    _data.putBoolean(Constants.NOTICE_MSG, true);
                    _msg.setData(_data);
                    _msg.replyTo = okBus.mServiceMessenger;//把服務器的信使給目標組件的信使,讓他倆本身聯繫,這裏僅僅是通知
                    try {
                        moduleNameMessenger.send(_msg);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    try {
                        Thread.sleep(200);//給服務器和目標組件500ms聯繫的時間
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    LogUtils.logOnUI(Constants.TAG, module_name + "進程,原本就是醒的");
                }

                if (serviceId < 0) {  //喚醒成功,繼續發送異步請求,通知目標模塊
                    okBus.onEvent(serviceId);
                }
                if (!TextUtils.isEmpty(url)) {  //目標url不爲空,繼續打開目標
                    OkBus.getInstance().onEvent(Constants.ROUTER_OPEN_URL, url);
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                LogUtils.logOnUI(Constants.TAG, "自動喚醒目標進程失敗 module_name:" + module_name);
            }
        }, BIND_AUTO_CREATE);
    }
複製代碼

啓動模塊,並傳遞綁定對象

/** * 收到喚醒通知以後,初始化模塊,並自動去服務器註冊 * * @param intent * @return */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        LogUtils.logOnUI(Constants.TAG, getPackageName() + " 收到喚醒通知");
        //獲取模塊的module
        BaseModule mBaseModule = BaseAppModuleApp.getBaseApplication().mBaseModule;
        if (!mBaseModule.isConnected.get()) {
            LogUtils.logOnUI(Constants.TAG, getPackageName() + " 我被喚醒啦");
            //初始化module,啓動module的ClientHandler(Messenger)
            mBaseModule.init(latch, resultRef);
            mBaseModule.afterConnected();
            try {
                //超時限制
                latch.await(2000, TimeUnit.SECONDS);
            } catch (Exception e) { //等待中斷
                e.printStackTrace();
            }
        }
        //返回ClientHandler的Binder對象
        return mBaseModule.mWorkThread.clientHandler.getBinder();
    }
複製代碼

發送消息,其最終是經過ServiceBus轉發到每一個模塊OkBus,而後Binder傳遞的Messenger關聯來完成信息傳遞。

/** * @param tag 發送消息的事件ID * @param data 發送消息的數據 * @return */
    public OkBus onEvent(int tag, Object data) {
        //發送時間,因此tag小於0
        String hex = Integer.toHexString(Math.abs(tag));
        LogUtils.i("Message OkBus", "onEvent " + (tag > 0 ? "[普通]" : "[服務]") + " tag: " + hex);

        //一、本地先處理非服務消息
        if (tag >= 0) onLocalEvent(tag, data);

        //二、若是是組建化,向服務器發消息
        if (isModule.get()) {
            //保證發送時服務啓動
            if (!isModuleConnected()) {
                LogUtils.i("Message OkBus", "發消息失敗,服務已經斷開連接,嘗試從新打開服務,進行發消息");
                BaseAppModuleApp.getBaseApplication().connectService();
                return this;
            }
           //數據爲空,即爲事件
            if (data == null || data instanceof Serializable) {
                Message newMsg = new Message();
                if (data != null) {
                    Bundle bundle = new Bundle();
                    bundle.putSerializable(Constants.MESSAGE_DATA, (Serializable) data);
                    newMsg.setData(bundle);
                }
                newMsg.arg1 = mModuleId;
                newMsg.what = tag;
                try {
                    //發送信息到目標Service
                    mServiceMessenger.send(newMsg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                assert false : "跨進程時,你傳遞的對象沒有序列化!";
            }
        } else if (tag < 0) {//非組件化時本地處理服務消息
            onLocalEvent(tag, data);
        }
        return this;
    }
複製代碼

使用Messenger通訊框架設計就介紹到這裏。1.Modular框架,模塊內傳輸使用了OkBus的路由傳輸,而在跨模塊則使用Messenger的方式來完成2.Messenger實際是一個封裝好的IBinder對象3.Modular經過合理設置跨模塊的傳輸的協議邏輯來完成信息傳輸

相關文章
相關標籤/搜索