AndroidIPC機制(5)- Binder 連接池

一、Binder連接池

之前幾篇文章我已經(jīng)介紹了兩種 IPC 方案:AIDL 和 Messenger。當中,AIDL 也是 Messenger 的底層實現(xiàn),所以對于 AIDL 開發(fā)者需要更為重視一點,這一篇文章也將繼續(xù)對 AIDL 進行更深入的介紹

現(xiàn)在考慮一種情況,假設(shè)在一個設(shè)備上,有一個應(yīng)用作為服務(wù)端存在,作為客戶端的應(yīng)用有十個,而這十個應(yīng)用都需要與服務(wù)端進行通信,且用于與服務(wù)端進行通信的 AIDL 接口各不相同。那么,按照之前介紹的 AIDL 方案,現(xiàn)在服務(wù)端就需要創(chuàng)建十個 Service 來分別與十個客戶端對應(yīng)。這種結(jié)果起來是不可以接受的,因為 Service 作為四大組件之一,創(chuàng)建并運行太多 Service 會使服務(wù)端應(yīng)用看起來太為重量級了 ,也不利于服務(wù)端應(yīng)用的開發(fā)

為了解決這個問題,可以考慮使用 Binder 連接池來管理所有的 AIDL。機制是這樣的,服務(wù)端只創(chuàng)建一個 Service,每個客戶端在請求連接時,帶上自己的唯一標識,服務(wù)端根據(jù)這個唯一標識,返回對應(yīng)的 Binder 給客戶端。這樣,不同的客戶端就可以都綁定到同一個 Service,而獲得的 Binder 對象是唯一的,避免了創(chuàng)建多個 Service 的情況

這里就來模擬這種多個客戶端的 IPC 過程,整個流程是這樣的:有兩個客戶端,一個客戶端傳遞兩個整數(shù)給服務(wù)端進行加法操作,另一個客戶端傳遞兩個整數(shù)給服務(wù)端進行減法操作,所以總的是會有三個不同的應(yīng)用

二、服務(wù)端

首先創(chuàng)建三個需要的 AIDL 接口,IOperation.aidl 用于提供加法操作,ICompute.aidl 用于提供減法操作,IBinderPool.aidl 是一個用于中轉(zhuǎn)的 Binder 對象,含有一個 queryBinder 方法用于接收一個唯一標識,并返回客戶端實際需要的 Binder 對象

package com.czy.binder_pool_server;

interface IOperation {

    int add(int parameter1 , int parameter2);

}
package com.czy.binder_pool_server;

interface ICompute {

    int subtraction(int parameter1 , int parameter2);

}
package com.czy.binder_pool_server;

interface IBinderPool {

    IBinder queryBinder(int binderCode);
    
}

此外,服務(wù)端還需要有 ICompute.StubIOperation.Stub 的具體實現(xiàn),因為需要創(chuàng)建這兩個類的子類

/**
 * 作者:葉應(yīng)是葉
 * 時間:2018/3/23 21:17
 * 描述:https://github.com/leavesC
 */
public class IOperationImpl extends IOperation.Stub {

    @Override
    public int add(int parameter1, int parameter2) throws RemoteException {
        return parameter1 + parameter2;
    }

}
/**
 * 作者:葉應(yīng)是葉
 * 時間:2018/3/23 21:13
 * 描述:https://github.com/leavesC
 */
public class IComputeImpl extends ICompute.Stub {

    @Override
    public int subtraction(int parameter1, int parameter2) throws RemoteException {
        return parameter1 - parameter2;
    }

}

之后就是來創(chuàng)建那唯一的一個 Service 了。Service 直接返回的是 BinderPoolImpl 對象,而 BinderPoolImpl 對象的 queryBinder 可以根據(jù)傳入的參數(shù)再返回對應(yīng)的 Binder 對象,即進行中轉(zhuǎn)轉(zhuǎn)發(fā),從而使客戶端得到真實想要的 Binder 對象

/**
 * 作者:葉應(yīng)是葉
 * 時間:2018/3/23 21:55
 * 描述:https://github.com/leavesC
 */
public class BinderPoolService extends Service {

    private class BinderPoolImpl extends IBinderPool.Stub {

        @Override
        public IBinder queryBinder(int binderId) {
            switch (binderId) {
                case 100: {
                    return new IOperationImpl();
                }
                case 200: {
                    return new IComputeImpl();
                }
            }
            return null;
        }

    }

    private Binder binderPool;

    public BinderPoolService() {
        binderPool = new BinderPoolImpl();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binderPool;
    }

}

三、客戶端

本客戶端請求服務(wù)端進行加法操作,因此需要把 IOperation.aidl 文件和 IBinderPool.aidl 文件拷貝過來
與服務(wù)端的綁定操作與之前的文章介紹的 AIDL 機制大致相同,區(qū)別只在于在 onServiceConnected 中需要調(diào)用 queryBinder 方法獲取真實的 Binder 對象

/**
 * 作者:葉應(yīng)是葉
 * 時間:2018/3/23 22:32
 * 描述:https://github.com/leavesC
 */
public class MainActivity extends AppCompatActivity {

    private IOperation operation;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                IBinderPool binderPool = IBinderPool.Stub.asInterface(service);
                //本客戶端的唯一標識是 100
                //獲取真實的 Binder 對象
                operation = IOperation.Stub.asInterface(binderPool.queryBinder(100));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            operation = null;
            bindService();
        }
    };

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService();
        findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (operation != null) {
                    try {
                        Log.e(TAG, "4+2 加法:" + operation.add(4, 2));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (operation != null) {
            unbindService(serviceConnection);
        }
    }

    private void bindService() {
        Intent intent = new Intent();
        intent.setClassName("com.czy.binder_pool_server", "com.czy.binder_pool_server.BinderPoolService");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

}

另外一個客戶端的操作也類似,這里不再贅述,具體的代碼可以直接看我傳到 GitHub 上的示例代碼

運行結(jié)果如下所示

這樣,以后每增加一個客戶端,就可以再為其指定一個唯一標識,然后在服務(wù)端中返回對應(yīng)的 Binder 對象即可,從而避免了創(chuàng)建多個 Service 的情況,極大的提高了開發(fā)效率

本系列關(guān)于 Android 平臺下的 IPC 機制的文章到這里目前也就結(jié)束,之后如果還有其它值得介紹的內(nèi)容的話,我也會繼續(xù)寫下一篇

這里提供本系列文章所有的 IPC 示例代碼:IPCSamples

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容