优雅使用aidl

3 篇文章 0 订阅
1 篇文章 0 订阅

前言

本文介绍通过aidl实现跨进程的一些小技巧,比如Binder池、监听服务死亡和重新绑定、RemoteCallbackList用法等内容。

aidl简单使用和in、out 、intout介绍

  • aidl默认支持的数据类型包括:byte,short,int,long,float,double,boolean,char,还有String和CharSequence类型
  • List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。
  • Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。
  • 表示在跨进程中数据的流向,in 表示数据只能由客户端流向服务端;out 表示数据只能由服务端流向客户端; inout 表示数据可在服务端与客户端之间双向流通。
  • 数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
  • 使用aidl默认支持的类型以外的类型时都需要导包,即使是同一个包下也不例外。
  • Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in

为了方便理解我们用代码来说明下,其中细节在代码备注中写得很清楚

非默认支持的类型aidl

// Preson.aidl
package com.example.fhl.aidl.inouts;

parcelable Preson; //Preson.aidl与Preson.java的包名要一样

非默认支持的类型java
Preson.aidl与Preson.java的包名要一样

/**
 *默认生成的模板类的对象只支持为 in 的定向 tag
 * 如果要支持为 out 或者 inout 的定向 tag 的话,
 * 还需要实现 readFromParcel() 方法——而这个方法其实并没有在 Parcelable 接口里面
 */
public void readFromParcel(Parcel dest){
    //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
    name = dest.readString();
    age = dest.readInt();
}

为了让Preson.aidl与Preson.java的包名要一样有两种方式,第一种方式就是把Preson.java放到java目录相同包名下;第二种是把Preson.java文件放到aidl目录下的Preson.aidl相同包名下,AS要在gradle需要配置如下;

sourceSets{
    main {
        java.srcDirs = ["src/main/java", "src/main/aidl"]
    }
}

定义方法的接口aidl

// IPresonManager.aidl
package com.example.fhl.aidl.inouts;
import com.example.fhl.aidl.inouts.Preson; //非默认数据类型需要导包,即使相同包下也要导包
// Declare any non-default types here with import statements

interface IPresonManager {
    List<Preson> getPresons();
	//非默认类型需要加上定向tag
    Preson addPresonIn(in Preson preson);
    Preson addPresonOut(out Preson preson);
    Preson addPresonInOut(inout Preson preson);
}

服务端代码

public class InoutService extends Service {

    private CopyOnWriteArrayList<Preson> list = new CopyOnWriteArrayList<>();
    @Override
    public IBinder onBind(Intent intent) {
        return new PresonManager();
    }

    private class PresonManager extends IPresonManager.Stub{

        @Override
        public List<Preson> getPresons() throws RemoteException {
            return list;
        }

        @Override
        public Preson addPresonIn(Preson preson) throws RemoteException {
            if (preson == null) {
                Log.e("InoutService", "preson is null.");
                preson = new Preson();
            }
            preson.setAge(28);
            if (!list.contains(preson)) {
                list.add(preson);
            }
            Log.e("InoutService", "list: "+list.toString());
            return preson;
        }

        @Override
        public Preson addPresonOut(Preson preson) throws RemoteException {
            if (preson == null) {
                Log.e("InoutService", "preson is null.");
                preson = new Preson();
            }
            preson.setAge(28);
            if (!list.contains(preson)) {
                list.add(preson);
            }
            Log.e("InoutService", "list: "+list.toString());
            return preson;
        }

        @Override
        public Preson addPresonInOut(Preson preson) throws RemoteException {
            if (preson == null) {
                Log.e("InoutService", "preson is null.");
                preson = new Preson();
            }
            preson.setAge(28);
            if (!list.contains(preson)) {
                list.add(preson);
            }
            Log.e("InoutService", "list: "+list.toString());
            return preson;
        }
    }
}

Binder连接池

我在开发中经常一个业务对应一个aidl接口,一个aidl对应一个service,如果有多个业务就有很多aidl接口,就需要service,这样服务端开销就会变大。如果我们把所有的aidl放入一个service中进行管理,这样就能避免开销大的问题,那有什么办法呢,接下来我讲下binder连接池来解决这个问题。

首先我们定义一个用来查找其他业务模块的aidl。

// IBinderPooll.aidl
package com.example.fhl.aidl;

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

interface IBinderPooll {
    IBinder queryBinder(int code); //根据code查询对应的业务模块binder
}

服务端代码:实现IBinderPooll, 通过实现的queryBinder格局code返回不同模块的ibinder

public class BinderPooll extends IBinderPooll.Stub{
    private static final int MODEL_1 = 1;
    private static final int MODEL_2 = 2;
    @Override
    public IBinder queryBinder(int code) throws RemoteException {
        IBinder iBinder = null;
        switch (code){  //根据客户端传进来的code返回对应的ibinder
            case MODEL_1:
                iBinder = new Mode1();
                break;
            case MODEL_2:
                iBinder = new Mode2();
                break;
            default:
                break;
        }
        return iBinder;
    }
}

服务端service就简单了,在onBind中直接返回BinderPooll对象

public class MyService extends Service {

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

客户端代码,首先建立一个BinderPooll 单例,在这个单例中通过同步方法connBinderService连接到服务端MyService并且拿到IBinderPooll对象,然后通过IBinderPooll的queryIBinder查找对应的ibinder

public class BinderPooll {
    private volatile static BinderPooll mBinderPooll;
    private IBinderPooll mIBinderPooll;
    private Context mContext;
    private CountDownLatch mCountDownLatch;//同步用

    private BinderPooll(Context context) {
        mContext = context.getApplicationContext();
        connBinderService();
    }

    public static BinderPooll getInstance(Context context){ //双重检测单例
        if (mBinderPooll == null) {
            synchronized(BinderPooll.class){
                if (mBinderPooll == null) {
                    mBinderPooll = new BinderPooll(context);
                }
            }
        }
        return mBinderPooll;
    }

    public IBinder queryIBinder(int code){
        IBinder iBinder = null;
        if (mIBinderPooll != null) {
            iBinder = mBinderPooll.queryIBinder(code);
        }
        return iBinder;
    }

    private synchronized void connBinderService(){
        mCountDownLatch = new CountDownLatch(1);
        Intent intent = new Intent();
        intent.setAction("com.example.fhl.aidl.servcie.easy.MyService");
        intent.setPackage("com.example.fhl.aidl");
        mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
        try {
            mCountDownLatch.wait(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (mIBinderPooll == null) {
            throw new RuntimeException("mIBinderPooll is null");
        }
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mIBinderPooll = IBinderPooll.Stub.asInterface(iBinder);
            try {
                mIBinderPooll.asBinder().linkToDeath(deathRecipient, 0); //绑定死亡监听
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mIBinderPooll = null;
        }
    };

    //服务是否死亡监听
    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            mIBinderPooll.asBinder().unlinkToDeath(deathRecipient, 0); //取消死亡监听
            mIBinderPooll = null;
            //重新连接
            connBinderService(); 
        }
    };
}

服务端死亡监听

代码就重复贴了,跟上面代码一样,通过ibinder的linkToDeath方法绑定死亡监听IBinder.DeathRecipient,但服务死亡的时候会回调binderDied方法,然后可以在这个方法里面做重置和重连等操作。

RemoteCallbackList使用

多个Activity或者多个进程调用aidl传入回调接口的时候服务端只保留一个最近的一个回调接口,所有需要一个集合在服务端保持这些回调接口,RemoteCallbackList是专门用来保持服务端回调接口的集合。

public class RemoteCallBackListService extends Service {

    private RemoteCallbackList<ILisntener> mRemoteCallBackList = new RemoteCallbackList<>();
    @Override
    public IBinder onBind(Intent intent) {
        return new ManagerListener();
    }

    private class ManagerListener extends IManagerLisntener.Stub{

        @Override
        public void registerListener(ILisntener listener) throws RemoteException {
            if (listener != null) {
                mRemoteCallBackList.register(listener);
            }
        }

        @Override
        public void unregisterListener(ILisntener listener) throws RemoteException {
            if (listener != null) {
                mRemoteCallBackList.unregister(listener);
            }
        }
    }

    //给所有注册的监听回调
    private void returnCallBack(){
        int size = mRemoteCallBackList.beginBroadcast();
        for (int i = 0; i < size; i++) {
            ILisntener lisntener = mRemoteCallBackList.getBroadcastItem(i);
            if (lisntener != null) {
                try {
                    lisntener.onCallBack("hello");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        mRemoteCallBackList.finishBroadcast();
    }

    @Override
    public void onDestroy() {
        if (mRemoteCallBackList != null) {
            mRemoteCallBackList.kill();
        }
        super.onDestroy();
    }
}

代码链接 (https://github.com/dragonfan/AIDL)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值