IPC之Binder連接池機制

一.前言

由于最近負責小度在家平臺接入咪咕視頻,因為咪咕視頻是第三方的app,這樣就涉及小度query指令透傳到咪咕,咪咕的UIControl需要同步給小度云端的跨進程互相調(diào)用的場景。然后我看項目里又接入了愛奇藝,CIBN等一系列的第三方app,都是通過AIDL方式跨進程調(diào)用的,查看項目發(fā)現(xiàn)每個app對應(yīng)一個Serivce,假如接入30個第三方app,按照項目現(xiàn)有的實現(xiàn)方式就會有30個Service,我們知道Service是系統(tǒng)組件,勢必導致系統(tǒng)開銷。
我們項目提供給第三方的sdk基本是AIDL方式提供接口的,AIDL應(yīng)該很簡單,這里我們回顧下AIDL的大概流程:新建一個Serivce和一個AIDL接口,然后繼承AIDL接口的Stub并實現(xiàn)抽象方法,接著在Service的onBind方法中返回AIDL接口實例,接著客戶端綁定Serivce并將Service#onBind方法IBinder實例傳給ServiceConnection#onServiceConnected中,通過asInterface轉(zhuǎn)成得到接口實例,這樣客戶端就可以調(diào)用服務(wù)端的接口方法了。

二.優(yōu)化方案

基于上面提出的問題,主要是解決Service數(shù)量多的問題,同時每個模塊有自己的AIDL實現(xiàn)接口并且通過BinderPool管理一個AIDL接口用于查詢各模塊的AIDL的IBinder實例,BinderPool對應(yīng)僅僅一個Service,在Service中返回用于獲取Binder連接池里的AIDL接口實例,通過這種機制可以用1個Service、多個AIDL接口的SDK方式對應(yīng)幾乎所有第三方的app接入。我們只要在Service#onBind的AIDL接口區(qū)分不同的標識,通過不同的標識返回對應(yīng)模塊的AIDL接口實例,我們就可以調(diào)用到各自模塊的接口。

Binder池實現(xiàn)

1.新建二個AIDL接口

IMyAidlInterface(獲取一個字符串)如下:

// IMyAidlInterface.aidl
package zw.chowen.binderpool;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    String getValue();
}

IMyAidlInterface2(獲取兩個整形相加和)如下:

package zw.chowen.binderpool;

// Declare any non-default types here with import statements

interface IMyAidlInterface2 {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    int add(int a, int b);
}

以上兩個AIDL接口對應(yīng)不同的進程。

2.新建AIDL池接口:用于獲取相應(yīng)模塊的的IBinder接口。

IBinderPoolInterface:

// IBinderPoolInterface.aidl
package zw.chowen.binderpool;

// Declare any non-default types here with import statements

interface IBinderPoolInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    IBinder queryInterface(int type);
}

3.分別實現(xiàn)這三個AIDL接口

public class IMyAidlInterfaceImpl extends zw.chowen.binderpool.IMyAidlInterface.Stub {

        @Override
        public String getValue() throws RemoteException {
            return "chowen#this is IMyAidlInterface";
        }
    }

public class IMyAidlInterfaceImpl2 extends IMyAidlInterface2.Stub {

        @Override
        public int add(int a, int b) throws RemoteException {
            LOGGER.info("chowen#a and b >>" + (a + b));
            return a+b;
        }
    }

public class BinderPoolServiceImpl extends IBinderPoolInterface.Stub {

        public BinderPoolServiceImpl() {
            super();
        }

        @Override
        public IBinder queryInterface(int type) throws RemoteException {
            IBinder binder = null;
            switch (type) {
                case 1:
                    binder = new IMyAidlInterfaceImpl();
                    break;
                case 2:
                    binder = new IMyAidlInterfaceImpl2();
                    break;
            }
            return binder;
        }
    }

分別繼承Stub并實現(xiàn)各自抽象方法,通過IBinderPoolInterface的實現(xiàn)類BinderPoolServiceImpl中queryInterface方法即可返回各自模塊對應(yīng)類型的IBinder對象。

4.創(chuàng)建Service及IBinder池機制

Service

/**
 * Created by zhouwen on 2019-10-06 22:30
 */
public class BinderPoolService extends Service {
    private static Logger LOGGER = Logger.getLogger("BinderPoolService");
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        LOGGER.info("chowen#onBind=" + (IBinder) BinderPoolManager.getIns(this).getBinder());
        //返回獲取各模塊IBinder的AIDL接口
        return (IBinder) BinderPoolManager.getIns(this).getBinder();
    }

    @Override
    public void onCreate() {
        LOGGER.info("chowen#onCreate");
        LOGGER.severe("chowen#process=" + Process.myPid());
        super.onCreate();

    }


    @Override
    public void onDestroy() {
        LOGGER.info("chowen#onDestroy");
        super.onDestroy();
        BinderPoolManager.getIns(this).unbindService(this);
    }

}
通過BinderPoolManager綁定BinderPoolService這個服務(wù),然后在BinderPoolService#onBind方法返回IBinderPoolInterface實例。

IBinder池機制:

/**
 * Created by zhouwen on 2019-10-06 22:24
 * Binder連接池實例
 */
public class BinderPoolManager {

    private static Logger LOGGER = Logger.getLogger("BinderPoolManager");
    private static BinderPoolManager sBinderPoolManager;

    private IBinderPoolInterface mIBinderPoolInterface;

    public BinderPoolManager(Context context) {
        bindService(context.getApplicationContext());
    }

    public static BinderPoolManager getIns(Context context) {
        if (sBinderPoolManager == null) {
            synchronized (BinderPoolManager.class) {
                if (sBinderPoolManager == null) {
                    sBinderPoolManager = new BinderPoolManager(context);
                }
            }
        }

        return sBinderPoolManager;
    }


    public class IMyAidlInterfaceImpl extends zw.chowen.binderpool.IMyAidlInterface.Stub {

        @Override
        public String getValue() throws RemoteException {
            return "chowen#this is IMyAidlInterface";
        }
    }

    public class IMyAidlInterfaceImpl2 extends IMyAidlInterface2.Stub {

        @Override
        public int add(int a, int b) throws RemoteException {
            LOGGER.info("chowen#a and b >>" + (a + b));
            return a+b;
        }
    }

    public IBinder queryInterface(int type) {
        try {
            LOGGER.info("chowen#queryInterface#mIBinderPoolInterface>>" + mIBinderPoolInterface);
            if (mIBinderPoolInterface != null) {
                return mIBinderPoolInterface.queryInterface(type);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return null;
    }


    public class BinderPoolServiceImpl extends IBinderPoolInterface.Stub {

        public BinderPoolServiceImpl() {
            super();
        }

        @Override
        public IBinder queryInterface(int type) throws RemoteException {
            IBinder binder = null;
            switch (type) {
                case 1:
                    binder = new IMyAidlInterfaceImpl();
                    break;
                case 2:
                    binder = new IMyAidlInterfaceImpl2();
                    break;
            }
            return binder;
        }
    }

    public synchronized void bindService(Context context) {
      Intent intent = new Intent(context, BinderPoolService.class);
      context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    public IBinderPoolInterface getBinder() {
        return new BinderPoolServiceImpl();
    }


    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIBinderPoolInterface = IBinderPoolInterface.Stub.asInterface(service);
            LOGGER.info("chowen#onServiceConnected#mIBinderPoolInterface="+ mIBinderPoolInterface);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            LOGGER.info("chowen#onServiceDisconnected");
        }
    };

    public void unbindService(Context context){
        context.unbindService(serviceConnection);
    }
}

5.模擬多進程實現(xiàn)

我們在AndroidManifest文件中聲明多個進程,這里就隨便申請如下:

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".SecondActivity"
            android:process=":other" />

        <service
            android:name=".BinderPoolService"
            android:process=":binder_pool" />
    </application>

我們通過主進程,other進程分別調(diào)用相應(yīng)的接口方法。
主進程:

IBinder binder = mBinderPoolManager.queryInterface(1);
                IMyAidlInterface iMyAidlInterface1 = BinderPoolManager.IMyAidlInterfaceImpl.asInterface(binder);
                LOGGER.info("chowen#onResume#iMyAidlInterface1=" + iMyAidlInterface1);
                if (iMyAidlInterface1 != null) {
                    try {
                        LOGGER.info("chowen#onResume#value=" + iMyAidlInterface1.getValue());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }

other進程:

IBinder binder = mBinderPoolManager.queryInterface(2);
                IMyAidlInterface2 iMyAidlInterface2 = BinderPoolManager.IMyAidlInterfaceImpl2.asInterface(binder);
                LOGGER.info("chowen#iMyAidlInterfaceImpl2=" + iMyAidlInterface2);
                if (iMyAidlInterface2 != null) {
                    try {
                        LOGGER.info("chowen#a and b is>>>" + iMyAidlInterface2.add(10, 20));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }

分別通過BinderPoolManager#queryInterface方法通過各自的標識類型,這里用1,2表示,去分別獲取IBinderPoolInterface接口實現(xiàn)中對應(yīng)的IBinder實例。

6.IBinder池執(zhí)行效果:

2019-10-07 00:03:04.106 16689-16689/? E/MainActivity: chowen#process=16689
2019-10-07 00:03:04.185 16725-16725/? I/BinderPoolService: chowen#onCreate
2019-10-07 00:03:04.186 16725-16725/? E/BinderPoolService: chowen#process=16725
2019-10-07 00:03:04.189 16725-16725/? I/BinderPoolService: chowen#onBind=zw.chowen.binderpool.BinderPoolManager$BinderPoolServiceImpl@c1109bc
2019-10-07 00:03:04.191 16725-16725/? I/BinderPoolManager: chowen#onServiceConnected#mIBinderPoolInterface=zw.chowen.binderpool.BinderPoolManager$BinderPoolServiceImpl@7a09d45
2019-10-07 00:03:04.312 16689-16689/? I/BinderPoolManager: chowen#onServiceConnected#mIBinderPoolInterface=zw.chowen.binderpool.IBinderPoolInterface$Stub$Proxy@4283e77
2019-10-07 00:03:10.127 16689-16689/? I/BinderPoolManager: chowen#queryInterface#mIBinderPoolInterface>>zw.chowen.binderpool.IBinderPoolInterface$Stub$Proxy@4283e77
2019-10-07 00:03:10.132 16689-16689/? I/MainActivity: chowen#onResume#iMyAidlInterface1=zw.chowen.binderpool.IMyAidlInterface$Stub$Proxy@2a9f150
2019-10-07 00:03:10.134 16689-16689/? I/MainActivity: chowen#onResume#value=chowen#this is IMyAidlInterface
2019-10-07 00:03:13.119 16761-16761/? E/SecondActivity: chowen#process=16761
2019-10-07 00:03:13.140 16761-16761/? I/BinderPoolManager: chowen#onServiceConnected#mIBinderPoolInterface=zw.chowen.binderpool.IBinderPoolInterface$Stub$Proxy@b36e1c0
2019-10-07 00:03:17.126 16761-16761/? I/BinderPoolManager: chowen#queryInterface#mIBinderPoolInterface>>zw.chowen.binderpool.IBinderPoolInterface$Stub$Proxy@b36e1c0
2019-10-07 00:03:17.128 16761-16761/? I/SecondActivity: chowen#iMyAidlInterfaceImpl2=zw.chowen.binderpool.IMyAidlInterface2$Stub$Proxy@2c1fa84
2019-10-07 00:03:17.130 16725-16740/? I/BinderPoolManager: chowen#a and b >>30
2019-10-07 00:03:17.130 16761-16761/? I/SecondActivity: chowen#a and b is>>>30

我們從Log看出分別處于不同兩個進程,通過自己的AIDL接口分別打印出預(yù)期結(jié)果,并且Bind一個Service。

三.總結(jié)

通過Binder連接池機制可以很好的解決項目中多個Service的問題,降低系統(tǒng)創(chuàng)建Service的開銷。通過一個AIDL接口對外提供獲取相應(yīng)模塊的AIDL的接口實例。

步驟如下:

1.創(chuàng)建各自的AIDL文件并實現(xiàn)對應(yīng)接口。
2.創(chuàng)建提供獲取AIDL接口的AIDL文件。
3.創(chuàng)建AIDL連接池,主要用于啟動服務(wù),生成IBinder對象等。
4.創(chuàng)建1個Service,通過AIDL連接池獲取對外的AIDL接口實例在onBind方法中返回。

流程如下:

image.png

Demo實現(xiàn):binderpool

最后編輯于
?著作權(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)容