實現AIDL接口的Binder連接池

歡迎Follow我的GitHub

Binder

本文的合集已經編著成書,高級Android開發強化實戰,歡迎各位讀友的建議和指導。在京東即可購買:https://item.jd.com/12385680.html

Book

Binder作爲AIDL通信的核心, 在使用中經常需要重複利用, 動態管理AIDL接口. Binder連接池的主要作用是把Binder請求統一發送至Service執行, 即動態管理Binder操作, 避免重複創建Service. 本文使用兩種簡單的AIDL服務, 使用Binder連接池動態切換, 含有演示Demo.

本文源碼的GitHub下載地址


AIDL

模擬Binder連接池, 使用兩個簡單的AIDL接口與實現, 一個是加解密, 一個是加法.

加解密, AIDL提供兩個方法, 即加密字符串和解密字符串.

package org.wangchenlong.binderpooldemo;

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

加密和解密的實現, 使用簡單的異或運算處理.

public class SecurityCenterImpl extends ISecurityCenter.Stub {
    private static final char SECRET_CODE = 'w';

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

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

AIDL的實現方法都需要設置RemoteException的異常拋出, 防止連接異常.

求和的AIDL接口

package org.wangchenlong.binderpooldemo;

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

求和的實現, 非常簡單.

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

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

package org.wangchenlong.binderpooldemo;

interface IBinderPool {
    IBinder queryBinder(int binderCode);
}

Binder 連接池

Service服務通過Binder連接池動態選擇Binder請求.

private Binder mBinderPool = new BinderPool.BinderPoolImpl(); // 動態選擇Binder

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

Binder連接池的具體實現, 創建BinderPool單例, 連接服務.

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

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

綁定服務, 通過CountDownLatch類, 把異步操作轉換爲同步操作, 防止綁定衝突.

private synchronized void connectBinderPoolService() {
    mCountDownLatch = new CountDownLatch(1); // 只保持一個綁定服務
    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連接池死亡重聯機制.

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

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

    @Override public void onServiceDisconnected(ComponentName name) {

    }
};

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

public IBinder queryBinder(int binderCode) {
    IBinder binder = null;
    try {
        if (mBinderPool != null) {
            binder = mBinderPool.queryBinder(binderCode);
        }
    } catch (RemoteException e) {
        e.printStackTrace();
    }

    return binder;
}

Binder連接池AIDL的具體實現, 通過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;
    }
}

AIDL並不會直接生成, 使用AS的Build -> Make Project即可.


客戶端

通過AIDL接口, 把耗時任務移到Service進行. 操作Binder需要在其他線程中執行, 使用Handler回調至主線程, 並更新頁面.

public void encryptMsg(View view) {
    new Thread(new Runnable() {
        @Override public void run() {
            doEncrypt();
        }
    }).start();
}

private void doEncrypt() {
    BinderPool binderPool = BinderPool.getInstance(getApplicationContext());
    IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
    mISecurityCenter = SecurityCenterImpl.asInterface(securityBinder);
    String msg = "Hello, I am Spike!";
    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();
    }
}

其他線程使用Handler向主線程傳遞數據, 在界面中顯示效果.

加法操作類似.

public void addNumbers(View view) {
    new Thread(new Runnable() {
        @Override public void run() {
            doAddition();
        }
    }).start();
}

private void doAddition() {
    BinderPool binderPool = BinderPool.getInstance(getApplicationContext());
    IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
    mICompute = ComputeImpl.asInterface(computeBinder);
    try {
        int result = mICompute.add(12, 12);
        Log.e(TAG, "12 + 12 = " + result);

        Message hm = new Message();
        hm.what = 1;
        hm.obj = result + "";
        mHandler.sendMessage(hm);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

注意AIDL需要捕獲RemoteException的異常.


效果

Demo

AIDL是較爲高效的跨進程通信方式, 也是很多方式的低層實現; Binder連接池可以在同一服務中處理多個Binder請求, 節省資源, 因此需要熟練掌握.

OK, that’s all! Enjoy it!

發佈了721 篇原創文章 · 獲贊 3023 · 訪問量 409萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章