IPC之Binder连接池机制

2019-10-07  本文已影响0人  码上述Andy

一.前言

由于最近负责小度在家平台接入咪咕视频,因为咪咕视频是第三方的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

上一篇下一篇

猜你喜欢

热点阅读