Andromeda:適用于多進程架構(gòu)的組件通信框架(下)

轉(zhuǎn)載自:https://mp.weixin.qq.com/s/67YL9daArYy3dYS_aWyO1w

提升服務(wù)提供方的進程優(yōu)先級

bindService()實質(zhì)上是做了以下事情:

  • 獲取服務(wù)提供方的binder
  • client端通過bind操作娇豫,讓Service所在進程的優(yōu)先級提高


    bindService過程圖.png

    現(xiàn)在我們來思考一個問題:雖然bind操作對用戶不可見,但是怎么知道bind哪個Service呢?

在編譯時,會為每個進程都插樁一個StubService, 并且在StubServiceMatcher這個類中诵姜,插入進程名與StubService的對應(yīng)關(guān)系(編譯時通過javassist插入代碼)盟萨,這樣根據(jù)進程名就可以獲取對應(yīng)的StubService.而IDispatcher的getRemoteService()方法中獲取的BinderBean就包含有進程名信息尿背。

生命周期管理

借鑒Glide的方式逛万,即利用Fragment/Activity的FragmentManager創(chuàng)建一個監(jiān)聽用的Fragment, 這樣當Fragment/Activity回調(diào)onDestroy()時,這個監(jiān)聽用的Fragment也會收到回調(diào)戚长,在這個回調(diào)中進行unbind操作即可盗冷。


回調(diào)監(jiān)聽原理圖.png

當時其實有考慮過是否借助Google推出的Arch componentss來處理生命周期問題,但是考慮到還有的團隊沒有接入這一套同廉,加上arch components的方案其實也變過多次仪糖,所以就暫時采用了這種方案,后面會視情況決定是否借助arch components的方案來進行生命周期管理 迫肖。

IPCCallback

對于耗時操作锅劝,我們直接在client端的work線程調(diào)用是否可以?雖然可以蟆湖,但是server端可能仍然需要把耗時操作放在自己的work線程中執(zhí)行故爵,執(zhí)行完畢之后再回調(diào)結(jié)果,所以這種情況下client端的work線程就有點多余帐姻。所以為了使用方便稠集,就需要一個IPCCallback, 在server端處理耗時操作之后再回調(diào)。
對于需要回調(diào)的AIDL接口饥瓷,其定義如下:

interface IBuyApple {
        int buyAppleInShop(int userId);
        void buyAppleOnNet(int userId,IPCCallback callback);
    }
}

而client端的調(diào)用如下:

IBinder buyAppleBinder = Andromeda.getRemoteService(IBuyApple.class);
        if (null == buyAppleBinder) {
            return;
        }
        IBuyApple buyApple = IBuyApple.Stub.asInterface(buyAppleBinder);
        if (null != buyApple) {
            try {
                buyApple.buyAppleOnNet(10, new IPCCallback.Stub() {
                    @Override
                    public void onSuccess(Bundle result) throws RemoteException {
                       ...
                    }

                    @Override
                    public void onFail(String reason) throws RemoteException {
                       ...
                    }
                });

            } catch (RemoteException ex) {
                ex.printStackTrace();
            }
        }
}

但是考慮到回調(diào)是在Binder線程中剥纷,而絕大部分情況下調(diào)用者希望回調(diào)在主線程,所以lib封裝了一個BaseCallback給接入方使用呢铆,如下:

IBinder buyAppleBinder = Andromeda.getRemoteService(IBuyApple.class);
        if (null == buyAppleBinder) {
            return;
        }
        IBuyApple buyApple = IBuyApple.Stub.asInterface(buyAppleBinder);
        if (null != buyApple) {
            try {
                buyApple.buyAppleOnNet(10, new BaseCallback() {
                    @Override
                    public void onSucceed(Bundle result) {
                       ...
                    }

                    @Override
                    public void onFailed(String reason) {
                        ...
                    }
                });

            } catch (RemoteException ex) {
                ex.printStackTrace();
            }
        }
}

事件總線

Andromeda中Event的定義如下:

public class Event implements Parcelable {
    
        private String name;
    
        private Bundle data;
        
        ...
    }

即 事件=名稱+數(shù)據(jù)晦鞋,通信時將需要傳遞的數(shù)據(jù)存放在Bundle中。
其中名稱要求在整個項目中唯一棺克,否則可能出錯悠垛。 由于要跨進程傳輸,所以所有數(shù)據(jù)只能放在Bundle中進行包裝娜谊。

事件訂閱

事件訂閱很簡單确买,首先需要有一個實現(xiàn)了EventListener接口的對象。 然后就可以訂閱自己感興趣的事件了纱皆,如下:

  Andromeda.subscribe(EventConstants.APPLE_EVENT,MainActivity.this);

其中MainActivity實現(xiàn)了EventListener接口湾趾,此處表示訂閱了名稱為EventConstnts.APPLE_EVENT的事件。

事件發(fā)布

    Bundle bundle = new Bundle();
    bundle.putString("Result", "gave u five apples!");
    Andromeda.publish(new Event(EventConstants.APPLE_EVENT, bundle));

InterStellar

InterStellar支持IPC修飾符in, out, inout和oneway派草,借助InterStellar, 可以像定義本地接口一樣定義遠程接口搀缠,如下:

public interface IAppleService {

       int getApple(int money);

       float getAppleCalories(int appleNum);

       String getAppleDetails(int appleNum,  String manifacture,  String tailerName, String userName,  int userId);

       @oneway
       void oneWayTest(Apple apple);

       String outTest1(@out Apple apple);

       String outTest2(@out int[] appleNum);

       String outTest3(@out int[] array1, @out String[] array2);

       String outTest4(@out Apple[] apples);

       String inoutTest1(@inout Apple apple);

       String inoutTest2(@inout Apple[] apples);

   }

而接口的實現(xiàn)也跟本地服務(wù)的實現(xiàn)完全一樣,如下:

public class AppleService implements IAppleService {

    @Override
    public int getApple(int money) {
        return money / 2;
    }

    @Override
    public float getAppleCalories(int appleNum) {
        return appleNum * 5;
    }

    @Override
    public String getAppleDetails(int appleNum, String manifacture, String tailerName, String userName, int userId) {
        manifacture = "IKEA";
        tailerName = "muji";
        userId = 1024;
        if ("Tom".equals(userName)) {
            return manifacture + "-->" + tailerName;
        } else {
            return tailerName + "-->" + manifacture;
        }
    }

    @Override
    public synchronized void oneWayTest(Apple apple) {
        if(apple==null){
            Logger.d("Man can not eat null apple!");
        }else{
            Logger.d("Start to eat big apple that weighs "+apple.getWeight());
            try{
                wait(3000);
                //Thread.sleep(3000);
            }catch(InterruptedException ex){
                ex.printStackTrace();
            }
            Logger.d("End of eating apple!");
        }
    }

    @Override
    public String outTest1(Apple apple) {
        if (apple == null) {
            apple = new Apple(3.2f, "Shanghai");
        }
        apple.setWeight(apple.getWeight() * 2);
        apple.setFrom("Beijing");
        return "Have a nice day!";
    }

    @Override
    public String outTest2(int[] appleNum) {
        if (null == appleNum) {
            return "";
        }
        for (int i = 0; i < appleNum.length; ++i) {
            appleNum[i] = i + 1;
        }
        return "Have a nice day 02!";
    }

    @Override
    public String outTest3(int[] array1, String[] array2) {
        for (int i = 0; i < array1.length; ++i) {
            array1[i] = i + 2;
        }
        for (int i = 0; i < array2.length; ++i) {
            array2[i] = "Hello world" + (i + 1);
        }

        return "outTest3";
    }

    @Override
    public String outTest4(Apple[] apples) {
        for (int i = 0; i < apples.length; ++i) {
            apples[i] = new Apple(i + 2f, "Shanghai");
        }

        return "outTest4";
    }

    @Override
    public String inoutTest1(Apple apple) {
        Logger.d("AppleService-->inoutTest1,apple:" + apple.toString());
        apple.setWeight(3.14159f);
        apple.setFrom("Germany");
        return "inoutTest1";
    }

    @Override
    public String inoutTest2(Apple[] apples) {
        Logger.d("AppleService-->inoutTest2,apples[0]:" + apples[0].toString());
        for (int i = 0; i < apples.length; ++i) {
            apples[i].setWeight(i * 1.5f);
            apples[i].setFrom("Germany" + i);
        }
        return "inoutTest2";
    }}
}

可見整個過程完全不涉及到AIDL.
那它是如何實現(xiàn)的呢近迁?

本質(zhì)上AIDL編譯之后生成的Proxy其實是提供了接口的靜態(tài)代理艺普,那么我們其實可以改成動態(tài)代理來實現(xiàn),將服務(wù)方法名和參數(shù)傳遞到服務(wù)提供方,然后調(diào)用相應(yīng)的方法歧譬,最后將結(jié)果回傳即可岸浑。

總結(jié)

Andromeda的意義在于同時融合了本地通信和遠程通信,只有做到這樣瑰步,我覺得才算完整地解決了組件通信的問題助琐。其實跨進程通信都是在binder的基礎(chǔ)上進行封裝,Andromeda的創(chuàng)新之處在于將binder與Service進行剝離面氓,從而使服務(wù)的使用更加靈活。

最后附上Andromeda的開源地址 https://github.com/iqiyi/Andromeda

謝謝~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛆橡,一起剝皮案震驚了整個濱河市舌界,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泰演,老刑警劉巖呻拌,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異睦焕,居然都是意外死亡藐握,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門垃喊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猾普,“玉大人,你說我怎么就攤上這事本谜〕跫遥” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵乌助,是天一觀的道長溜在。 經(jīng)常有香客問我,道長他托,這世上最難降的妖魔是什么掖肋? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮赏参,結(jié)果婚禮上志笼,老公的妹妹穿的比我還像新娘。我一直安慰自己登刺,他們只是感情好籽腕,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纸俭,像睡著了一般皇耗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上揍很,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天郎楼,我揣著相機與錄音万伤,去河邊找鬼。 笑死呜袁,一個胖子當著我的面吹牛敌买,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播阶界,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼虹钮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膘融?” 一聲冷哼從身側(cè)響起芙粱,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎氧映,沒想到半個月后春畔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡岛都,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年律姨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臼疫。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡择份,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出多矮,到底是詐尸還是另有隱情缓淹,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布塔逃,位于F島的核電站讯壶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏湾盗。R本人自食惡果不足惜伏蚊,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望格粪。 院中可真熱鬧躏吊,春花似錦、人聲如沸帐萎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疆导。三九已至赁项,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悠菜。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工舰攒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悔醋。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓摩窃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芬骄。 傳聞我的和親對象是個殘疾皇子猾愿,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

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