跨進(jìn)程的EventBus详瑞,開源框架HermesEventBus使用小結(jié)

為什么使用HermesEventBus

原有項(xiàng)目場(chǎng)景:Socket通信模塊在子進(jìn)程RemoteService中请毛,主進(jìn)程想要發(fā)送一個(gè)Socket協(xié)議,先從業(yè)務(wù)模塊通過EventBus post一個(gè)Event到本地LocalService聂薪,本地Service通過ServiceConnection與RemoteService綁定家乘,本地LocalService在ServiceConnection的連接上將AIDL接口封裝為一個(gè)Messenger,通過Messenger向RemoteService發(fā)送Message藏澳,而Event就是被包裹在Message中的Bundle里仁锯。

說到這里感覺不借助圖形很難描述:

原進(jìn)程通信.png

問題在這里:

  • 每個(gè)序列化的Object要放入Bundle中,Bundle對(duì)象再放入Message中翔悠,過程繁瑣
  • 每個(gè)Message都有what屬性需要Handler在處理時(shí)判斷
  • 業(yè)務(wù)模塊想要執(zhí)行子進(jìn)程的RemoteService的方法先通過EventBus post一個(gè)Object业崖,在LocalService必須有相應(yīng)的處理。
  • 子進(jìn)程中的RemoteService接收到了服務(wù)器發(fā)來的消息給業(yè)務(wù)模塊蓄愁,先要通過本地LocalService處理Message双炕,再由EventBus把Message中的Object post出去。

綜上所述撮抓,多了一個(gè)LocalService妇斤,顯得很礙眼。

使用HermesEventBus

HermesEventBus是一種支持跨進(jìn)程丹拯,跨APP通信的以EventBus為基礎(chǔ)的框架站超,使用起來就像EventBus。
項(xiàng)目地址:https://github.com/elemers/HermesEventBus

** 如果你的應(yīng)用是單個(gè)app咽笼,在Application中可以初始化HermesEventBus**:

    private void initHermesEventBus() {
        HermesEventBus.getDefault().init(getBaseApplication());
    }

由于是多進(jìn)程顷编,以我的項(xiàng)目為例戚炫,主進(jìn)程會(huì)跑一遍這個(gè)方法剑刑,RemoteService啟動(dòng)時(shí)子進(jìn)程也會(huì)跑一遍這個(gè)方法。

** 如果項(xiàng)目是多個(gè)app,選擇一個(gè)作為主APP注冊(cè)Service**:

<service android:name="xiaofei.library.hermes.HermesService$HermesService0"/>

然后在其他app的Application的onCreate中執(zhí)行:

HermesEventBus.getDefault().connectApp(this, packageName);

** 當(dāng)進(jìn)程不再需要使用HermesEventBus施掏,通過調(diào)用以下方法銷毀HermesEventBus**:

HermesEventBus.getDefault().destroy();

發(fā)送事件就像EventBus一樣簡(jiǎn)單,在任意進(jìn)程內(nèi)執(zhí)行:

 HermesEventBus.getDefault().postSticky(event);

發(fā)送完事件钮惠,在任意進(jìn)程內(nèi)的注冊(cè)的觀察者就會(huì)處理事件:

    /**
     * 通信層處理發(fā)送SOCKET請(qǐng)求事件
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.POSTING , sticky = true)
    public void handleRequestEvent(RequestEvent event){
        onHandleLocalSocketBizRequest(event);
    }

HermesEventBus是怎么做到跨進(jìn)程通信的

先看初始化方法:

    public void init(Context context) {
        mContext = context.getApplicationContext();
        mMainProcess = isMainProcess(context.getApplicationContext());
        if (mMainProcess) {
            Hermes.init(context);
            Hermes.register(MainService.class);
            mMainApis = MainService.getInstance();
        } else {
            mState = STATE_CONNECTING;
            Hermes.setHermesListener(new HermesListener());
            Hermes.connect(context, Service.class);
            Hermes.register(SubService.class);
        }
    }

在初始化時(shí),區(qū)分了主進(jìn)程和子進(jìn)程七芭,如果是子進(jìn)程會(huì)通過Hermes.connect(context, Service.class)綁定主進(jìn)程的Service素挽。

打開編譯好的APK,查看AndroidManifest.xml會(huì)發(fā)現(xiàn):

 <service
            android:name="xiaofei.library.hermeseventbus.HermesEventBus$Service" />

沒錯(cuò)狸驳,子進(jìn)程就是綁定了主進(jìn)程中的Service與主進(jìn)程通信的预明。而通信的載體是一個(gè)Mail類:

private final IHermesService.Stub mBinder = new IHermesService.Stub() {
        @Override
        public Reply send(Mail mail) {
            try {
                Receiver receiver = ReceiverDesignator.getReceiver(mail.getObject());
                int pid = mail.getPid();
                IHermesServiceCallback callback = mCallbacks.get(pid);
                if (callback != null) {
                    receiver.setHermesServiceCallback(callback);
                }
                return receiver.action(mail.getTimeStamp(), mail.getMethod(), mail.getParameters());
            } catch (HermesException e) {
                e.printStackTrace();
                return new Reply(e.getErrorCode(), e.getErrorMessage());
            }
        }

        @Override
        public void register(IHermesServiceCallback callback, int pid) throws RemoteException {
            mCallbacks.put(pid, callback);
        }

        @Override
        public void gc(List<Long> timeStamps) throws RemoteException {
            OBJECT_CENTER.deleteObjects(timeStamps);
        }
    };

public class Mail implements Parcelable {

    private long mTimeStamp;

    private int mPid;

    private ObjectWrapper mObject;

    private MethodWrapper mMethod;

    private ParameterWrapper[] mParameters;

    public static final Parcelable.Creator<Mail> CREATOR
            = new Parcelable.Creator<Mail>() {
        public Mail createFromParcel(Parcel in) {
            Mail mail = new Mail();
            mail.readFromParcel(in);
            return mail;
        }
        public Mail[] newArray(int size) {
            return new Mail[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeLong(mTimeStamp);
        parcel.writeInt(mPid);
        parcel.writeParcelable(mObject, flags);
        parcel.writeParcelable(mMethod, flags);
        parcel.writeParcelableArray(mParameters, flags);
    }

    public void readFromParcel(Parcel in) {
        mTimeStamp = in.readLong();
        mPid = in.readInt();
        ClassLoader classLoader = Mail.class.getClassLoader();
        mObject = in.readParcelable(classLoader);
        mMethod = in.readParcelable(classLoader);
        Parcelable[] parcelables = in.readParcelableArray(classLoader);
        if (parcelables == null) {
            mParameters = null;
        } else {
            int length = parcelables.length;
            mParameters = new ParameterWrapper[length];
            for (int i = 0; i < length; ++i) {
                mParameters[i] = (ParameterWrapper) parcelables[i];
            }
        }

    }

    private Mail() {

    }

    public Mail(long timeStamp, ObjectWrapper object, MethodWrapper method, ParameterWrapper[] parameters) {
        mTimeStamp = timeStamp;
        mPid = Process.myPid();
        mObject = object;
        mMethod = method;
        mParameters = parameters;
    }

    public int getPid() {
        return mPid;
    }

    public ParameterWrapper[] getParameters() {
        return mParameters;
    }

    public ObjectWrapper getObject() {
        return mObject;
    }

    public MethodWrapper getMethod() {
        return mMethod;
    }

    public long getTimeStamp() {
        return mTimeStamp;
    }

}

回顧一下AIDL通信,AIDL接口支持的參數(shù)類型除了基本類型耙箍、List撰糠、Map、AIDL接口就是Parcelable辩昆,而Mail這個(gè)實(shí)現(xiàn)Parcelable的類將Object封裝在內(nèi)部阅酪。

所以HermesEventBus本質(zhì)上也是通過AIDL實(shí)現(xiàn)跨進(jìn)程通信,依賴于主進(jìn)程的Service載體汁针。使用HermesEventBus省去了自己實(shí)現(xiàn)跨進(jìn)程通信的煩惱术辐。

小結(jié)幾種跨進(jìn)程方法

  • BroadCastReceiver 由于面向整個(gè)系統(tǒng)注冊(cè)的廣播,跨進(jìn)程消耗較大施无,性能不能保證辉词。
  • ContentProvider 支持跨進(jìn)程數(shù)據(jù)共享
  • AIDL 客戶端調(diào)用AIDL接口是同步并且?guī)Х祷亟Y(jié)果的,如果執(zhí)行時(shí)間較長(zhǎng)猾骡,客戶端的調(diào)用線程會(huì)一直等待较屿。服務(wù)端執(zhí)行AIDL接口是異步的,支持所有基本類型卓练、AIDL接口隘蝎、Parcelable、List襟企、Map等類型的參數(shù)嘱么,實(shí)現(xiàn)起來繁瑣。
  • Messenger 本質(zhì)是AIDL通信顽悼,客戶端發(fā)送Message后不帶返回結(jié)果曼振,服務(wù)端接收到Message是通過一個(gè)線程的Handler輪詢MessageQueue處理的,因此處理Message是在同一線程蔚龙。
  • HermesEventBus 本質(zhì)也是AIDL通信冰评,不需要自己實(shí)現(xiàn)綁定Service,發(fā)送事件也是不帶返回結(jié)果的木羹,使用簡(jiǎn)單甲雅。
  • Binder機(jī)制 Android跨進(jìn)程通信實(shí)現(xiàn)的核心解孙,AIDL就是基于Binder機(jī)制實(shí)現(xiàn)的,其中transact方法是客戶端向服務(wù)端發(fā)送消息抛人,onTransact方法是客戶端接收服務(wù)端的消息弛姜。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市妖枚,隨后出現(xiàn)的幾起案子廷臼,更是在濱河造成了極大的恐慌,老刑警劉巖绝页,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荠商,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡续誉,警方通過查閱死者的電腦和手機(jī)结啼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屈芜,“玉大人郊愧,你說我怎么就攤上這事【樱” “怎么了属铁?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)躬翁。 經(jīng)常有香客問我焦蘑,道長(zhǎng),這世上最難降的妖魔是什么盒发? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任例嘱,我火速辦了婚禮,結(jié)果婚禮上宁舰,老公的妹妹穿的比我還像新娘拼卵。我一直安慰自己,他們只是感情好蛮艰,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布腋腮。 她就那樣靜靜地躺著,像睡著了一般壤蚜。 火紅的嫁衣襯著肌膚如雪即寡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天袜刷,我揣著相機(jī)與錄音聪富,去河邊找鬼。 笑死著蟹,一個(gè)胖子當(dāng)著我的面吹牛墩蔓,可吹牛的內(nèi)容都是我干的梢莽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼钢拧,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了炕横?” 一聲冷哼從身側(cè)響起源内,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎份殿,沒想到半個(gè)月后膜钓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卿嘲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年颂斜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拾枣。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沃疮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梅肤,到底是詐尸還是另有隱情司蔬,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布姨蝴,位于F島的核電站俊啼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏左医。R本人自食惡果不足惜授帕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浮梢。 院中可真熱鬧跛十,春花似錦、人聲如沸秕硝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缝裤。三九已至屏轰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間憋飞,已是汗流浹背霎苗。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留榛做,地道東北人唁盏。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓内狸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親厘擂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昆淡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容