美文网首页
IPC之Binder连接池机制

IPC之Binder连接池机制

作者: 码上述Andy | 来源:发表于2019-10-07 09:28 被阅读0次

    一.前言

    由于最近负责小度在家平台接入咪咕视频,因为咪咕视频是第三方的app,这样就涉及小度query指令透传到咪咕,咪咕的UIControl需要同步给小度云端的跨进程互相调用的场景。然后我看项目里又接入了爱奇艺,CIBN等一系列的第三方app,都是通过AIDL方式跨进程调用的,查看项目发现每个app对应一个Serivce,假如接入30个第三方app,按照项目现有的实现方式就会有30个Service,我们知道Service是系统组件,势必导致系统开销。
    我们项目提供给第三方的sdk基本是AIDL方式提供接口的,AIDL应该很简单,这里我们回顾下AIDL的大概流程:新建一个Serivce和一个AIDL接口,然后继承AIDL接口的Stub并实现抽象方法,接着在Service的onBind方法中返回AIDL接口实例,接着客户端绑定Serivce并将Service#onBind方法IBinder实例传给ServiceConnection#onServiceConnected中,通过asInterface转成得到接口实例,这样客户端就可以调用服务端的接口方法了。

    二.优化方案

    基于上面提出的问题,主要是解决Service数量多的问题,同时每个模块有自己的AIDL实现接口并且通过BinderPool管理一个AIDL接口用于查询各模块的AIDL的IBinder实例,BinderPool对应仅仅一个Service,在Service中返回用于获取Binder连接池里的AIDL接口实例,通过这种机制可以用1个Service、多个AIDL接口的SDK方式对应几乎所有第三方的app接入。我们只要在Service#onBind的AIDL接口区分不同的标识,通过不同的标识返回对应模块的AIDL接口实例,我们就可以调用到各自模块的接口。

    Binder池实现

    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接口对应不同的进程。

    2.新建AIDL池接口:用于获取相应模块的的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.分别实现这三个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并实现各自抽象方法,通过IBinderPoolInterface的实现类BinderPoolServiceImpl中queryInterface方法即可返回各自模块对应类型的IBinder对象。

    4.创建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这个服务,然后在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.模拟多进程实现

    我们在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进程分别调用相应的接口方法。
    主进程:

    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接口实现中对应的IBinder实例。

    6.IBinder池执行效果:

    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接口分别打印出预期结果,并且Bind一个Service。

    三.总结

    通过Binder连接池机制可以很好的解决项目中多个Service的问题,降低系统创建Service的开销。通过一个AIDL接口对外提供获取相应模块的AIDL的接口实例。

    步骤如下:

    1.创建各自的AIDL文件并实现对应接口。
    2.创建提供获取AIDL接口的AIDL文件。
    3.创建AIDL连接池,主要用于启动服务,生成IBinder对象等。
    4.创建1个Service,通过AIDL连接池获取对外的AIDL接口实例在onBind方法中返回。

    流程如下:

    image.png

    Demo实现:binderpool

    相关文章

      网友评论

          本文标题:IPC之Binder连接池机制

          本文链接:https://www.haomeiwen.com/subject/ognppctx.html