BinderPool — Andorid端的“服務(wù)發(fā)現(xiàn)治理工具”

導(dǎo)語

最近在學(xué)習(xí)微服務(wù)相關(guān)知識,突然想到:微服務(wù)的思想雖然是在server端的場景下提出來的,但是無論是server,還是移動端,思想是相通的,移動端也會有多服務(wù)的場景,就同樣面臨多服務(wù)需要整合治理的問題。

1. 背景

隨著公司業(yè)務(wù)的發(fā)展,項目規(guī)模會越來越大,可能會遇到多多服務(wù)IPC的場景,有很多模塊,而每一個模塊都需要和服務(wù)端通訊,那么我們也要為每一個模塊創(chuàng)建特定的AIDL文件,那么服務(wù)端service也會產(chǎn)生很多個。作為四大組件之一,過多使用也會造成性能資源消耗。所以我們可以設(shè)計只有一個Service,對于不同可客戶端我們只是去返回一個不同的Binder即可,這樣就避免了創(chuàng)建了大量的Service。

BinderPool工作原理

2. AIDL

模擬Binder連接池, 使用兩個簡單的AIDL接口與實現(xiàn), 一個是加解密, 一個是加法。

package com.mantoudev.binderpooldemo;

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String pwd);
}

加密和解密的實現(xiàn), 這里使用簡單的異或運算處理。

public class SecurityCenterImpl extends ISecurityCenter.Stub {
    private static final char CODE_SECRET = 'z';

    @Override public String encrypt(String content) throws RemoteException {
        char[] chars = content.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] ^= CODE_SECRET;
        }
        return new String(chars);
    }

    @Override public String decrypt(String password) throws RemoteException {
        return encrypt(password);
    }
}

求和的AIDL接口:

package com.mantoudev.binderpooldemo;

interface ICompute {
    int add(int a, int b);
}

求和的實現(xiàn):

public class ComputeImpl extends ICompute.Stub {
    @Override public int add(int a, int b) throws RemoteException {
        return a + b;
    }
}

Binder連接池通過ID查找Bidner, 查詢并返回匹配的Binder:

package com.mantoudev.binderpooldemo;

interface IBinderPool {
    IBinder queryBinder(int binderCode);
}

3. Binder 連接池

Service服務(wù)通過Binder連接池動態(tài)選擇Binder請求:

private Binder mBinderPool = new BinderPool.BinderPoolImpl(); 

@Override public IBinder onBind(Intent intent) {
    Log.e(TAG, "onBind");
    return mBinderPool;
}

Binder連接池的具體實現(xiàn), 創(chuàng)建BinderPool單例, 連接服務(wù):

private BinderPool(Context context) {
    mContext = context.getApplicationContext();
    connectBinderPoolService(); // 連接服務(wù)
}

public static BinderPool getInstance(Context context) {
    if (sInstance == null) {
        synchronized (BinderPool.class) {
            if (sInstance == null) {
                sInstance = new BinderPool(context);
            }
        }
    }
    return sInstance;
}

綁定服務(wù), 通過CountDownLatch類, 把異步操作轉(zhuǎn)換為同步操作, 防止綁定沖突,對通過CountDownLatch類不了解的請自行百度:

private synchronized void connectBinderPoolService() {
    mCountDownLatch = new CountDownLatch(1); // 只保持一個綁定服務(wù)
    Intent service = new Intent(mContext, BinderPoolService.class);
    mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
    try {
        mCountDownLatch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

通過DeathRecipient處理Binder連接池死亡重聯(lián)機制:

很多人喜歡在servierConnection的onServiceDisconnected()回調(diào)中做重連處理,這里我簡單介紹下DeathRecipient

DeathRecipient

Binder有可以讓對端的進程得到意外退出通知的機制:Link-To-Death。我這里以我們這里Service被通知Client意外退出的情況為例,實現(xiàn)的方法如下:

  1. Client傳遞一個Binder對象給Service,此Binder對象與Client的進程關(guān)聯(lián);

  2. 在Sevice中接受到這個Binder對象,并且使用binder.linkToDeath(),注冊一個DeathRecipient回調(diào);

  3. 實現(xiàn)DeathRecipient。當Client意外退出的時候,DeathRecipient.binderDied()將被回調(diào),我們可以在這里釋放相關(guān)的資源。

// 失效重聯(lián)機制, 當Binder死亡時, 重新連接
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
    @Override public void binderDied() {
        Log.e(TAG, "binderDied@DeathRecipient()");
        mBinderPool.asBinder().unlinkToDeath(mDeathRecipient, 0);
        mBinderPool = null;
        connectBinderPoolService();
    }
};

// Binder的服務(wù)連接
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
    @Override public void onServiceConnected(ComponentName name, IBinder service) {
        Log.e(TAG, "onServiceConnected@ServiceConnection()");
        mBinderPool = IBinderPool.Stub.asInterface(service);
        try {
            mBinderPool.asBinder().linkToDeath(mDeathRecipient, 0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        mCountDownLatch.countDown();
    }

    @Override public void onServiceDisconnected(ComponentName name) {
      Log.e(TAG, "onServiceDisconnected@ServiceConnection()");   
      }
};

通過ID連接不同的Binder請求.

public IBinder queryBinder(int binderCode) {
 Log.e(TAG, "queryBinder---BinderCode:" + binderCode );
    IBinder binder = null;
    try {
        if (mBinderPool != null) {
            binder = mBinderPool.queryBinder(binderCode);
        }
    } catch (RemoteException e) {
        e.printStackTrace();
    }

    return binder;
}

Binder連接池AIDL的具體實現(xiàn), 通過ID選擇Binder.

public static class BinderPoolImpl extends IBinderPool.Stub {
    public BinderPoolImpl() {
        super();
    }

    @Override public IBinder queryBinder(int binderCode) throws RemoteException {
        IBinder binder = null;
        switch (binderCode) {
            case BINDER_COMPUTE:
                binder = new ComputeImpl();
                break;
            case BINDER_SECURITY_CENTER:
                binder = new SecurityCenterImpl();
                break;
            default:
                break;
        }
        return binder;
    }
}

1.4 Binder客戶端

我們在子線程進行耗時操作:

//測試
private void doTest(){
     new Thread(new Runnable() {
        @Override public void run(){
            encryptTest();
        }
    }).start();
}

private void encryptTest() {
    BinderPool binderPool = BinderPool.getInstance(getApplicationContext());
    IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
    mISecurityCenter = SecurityCenterImpl.asInterface(securityBinder);
    String msg = "BinderPool Test Msg!~~~";
    try {
        String encryptMsg = mISecurityCenter.encrypt(msg);
        Log.e(TAG, "加密了: " + encryptMsg);
        String decryptMsg = mISecurityCenter.decrypt(encryptMsg);
        Log.e(TAG, "解密了: " + decryptMsg);
        Message hm = new Message();
        hm.what = 0;
        hm.obj = encryptMsg + "\n" + decryptMsg;
        mHandler.sendMessage(hm);

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

1.5 總結(jié)

使用BinderPool我們就只需要建立一個Service就足夠了。當我們添加一個AIDL接口的時候只需要在BinderPool中添加一個id,然后根據(jù)這個id,在BinderPoolImpl中創(chuàng)建一個對應(yīng)的Binder對象即可。這樣就很大程度上簡化了我們的工作,同時也節(jié)省了系統(tǒng)資源開銷,要知道,在移動端,資源、性能的要求要做到更高。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,785評論 18 139
  • 3.5 Android進程間通信 3.5.1 背景知識 傳統(tǒng)IPC Linux傳統(tǒng)的IPC機制分為如下幾種:管道、...
    jianhuih閱讀 5,580評論 1 5
  • Android跨進程通信IPC整體內(nèi)容如下 1、Android跨進程通信IPC之1——Linux基礎(chǔ)2、Andro...
    隔壁老李頭閱讀 11,984評論 11 56
  • Jianwei's blog 首頁 分類 關(guān)于 歸檔 標簽 巧用Android多進程,微信,微博等主流App都在用...
    justCode_閱讀 5,943評論 1 23
  • 這是我在簡書的第四篇文章。 引子: 2015年的Toastmasters國際演講比賽上,來自沙特阿拉伯的工程師Mo...
    中大猩猩閱讀 492評論 6 4