Android IPC机制:Binder与Aidl

1.序列化与反序列化及android常见IPC方式:

1.1Serializable:

Serializable是java提供的序列化接口,为对象提供标准的序列化与反序列化操作。serialVersionUID是需要声明的,一般情况下赋值为1L,正常情况下不声明也没关系。但是在某些情况下可能会反序列化失败,这是因为在序列化对象的时候会把ID写入序列化文件中,在反序列化的时候会将这个ID和当前类的serialVersionUID进行比较,如果不一致就会反序列化失败。常见的情况如版本升级类的成员变量发生了改变,这时候如果没有声明serialVersionUID程序便会计算当前类的hash值把它赋值给serialVersionUID,那么和序列化文件中的ID比较肯定不一致,程序就会出现崩溃。但是如果类名或者成员变量的类型发生了改变,serialVersionUID虽然可以校验通过但是还是会序列化失败。
不参与序列化的成员:静态成员变量和transient关键字修饰的成员变量
父类与子类序列化:1.如果父类实现了Serializable接口,子类是的成员也是可以序列化的 2.如果子类实现了Serializable接口,父类的的成员不会被序列化赋值的 3.外部类实现了Serializable接口,内部类同样也是要实现Serializable接口,否则内部类不会被序列化

1.2.Parcelable:

序列化过程由writeToParcel方法来完成,反序列化由createFromParcel方法来完成。
parcelable序列化的几个方法Serializable与Parcelable的选择:Serializable使用简单但是开销比较大涉及比较多的IO操作,Parcelable是android平台推荐的序列化方法效率高但是使用稍麻烦。如果将对象序列化到存储设备或者网络传输建议使用Serializable,其他情况建议使用Parcelable。

1.3常用的IPC方式:

在这里插入图片描述

1.3.1.bundle

这个比较常见,Activity、Service和Receiver都是支持在intent中传递bundle的,这是一种最简单的IPC方式。

1.3.2.文件共享:

文件共享最大的问题就是并发,并发读写读出的内容可能不是最新的,并发写就更严重了,需要开发人员自己加线程同步来限制多个线程同时写操作。常见的文件共享有xml等文件以及sharedprefrence,sharedprefrence不支持进程间通信(不可靠),这是由它的缓存策略导致的,在内存中会有一份sharedprefrence文件的缓存。

1.3.3.Messenger:

这种底层是Binder,messenger实际上是对aidl做了封装,它需要和Handler配合起来使用,先来看看它的两个构造方法:

public Messenger(Handler target){
    mTarget = target.getIMessenger();
}
public Messenger(IBinder target){
    mTarget = IMessenger.Stub.asInterface(target);
}

在这里插入图片描述首先服务端进程:创建service处理客户端连接请求,然后创建一个handler同时使用handler创建messenger(上图的第一个构造函数),最后在onBind方法中返回messenger底层的binder即可。
客户端进程:首先要绑定远程服务,绑定成功后根据回调回来的binder对象创建messenger,然后就可以使用该messenger对象向服务端进程发送message了
服务端回复客户端消息:首先客户端需要new一个replyMessenger和replyhHandler,然后客户端还需要messenger发送消息的时候需要带上一个replyTo,将replyMessenger赋值给replyTo。最后在服务端收到消息时,取出replyTo中的replyMessenger就可以回复消息给客户端了。

1.3.4.ContentProvider:

这个底层也是Binder实现的,是比较常规的操作,实现一个例子即可,如果app中有多个进程推荐使用contentprovider代替数据库,可以解决数据库不能进程间共享的问题。还有就是contentprovider不是线程安全的,会存在并发问题,需要自己在icud方法内做好线程同步。

1.3.5.Aidl:

aidl也是Binder实现的对其进行了封装,后面会有详细描述使用起来稍微复杂一点。

1.3.6.Socket管道

这个需要建立在三次握手成功的基础上,这种IPC方式在android中使用的不是很多。

1.3.7.RemoteViews:

android里面ui方面跨进程通信的方式,使用场景只有notification和桌面小部件。

2.Binber的基本概念

在这里插入图片描述Binder驱动:如上图binder驱动在android系统中运行在内核空间的,负责各个用户进程通过binder进行通信的模块。binder驱动在程序运行时被链接到内核作为内核模块的一部分运行,它起的作用就是各个用户进程之间的桥梁,通过这个桥梁完成进程间通信。
Binder:通常情况下binder指的是一种机制,一种进程间通信的机制。就进程而言:对于server进程来说binder指的是binder本地对象,对于client进程来说binder指的是binderProxy对象,它只是server进程binder对象的远程代理。
IBinder:是一个接口,代表着跨进程传输的能力,是Binder的抽象。只要实现了IBinder接口,底层的binder驱动会自动对binder对象做处理,自动完成本地对象和代理对象之间的转换。
IInterface:也是一个接口,代表的是远程server对象具有什么样的能力,具体来说就是aidl里面的接口.
BinderProxy:Binderproxy是Binder的一个内部类,代表的是远程进程的Binder对象的本地代理,binderproxy也是实现了IBinder同样的也是具有跨进程传输能力。
Stub:在使用aidl的时候android自动生成的抽象静态内部类,它继承了Binder说明它是一个Binder本地对象。同时它又实现了IInterface接口,说明具有server端向client端传输数据的能力。stub是个抽象类,具体的实现需要我们自己完成。
StubProxy:stubproxy是stub的代理,持有Binder对象,同时实现了IInterface接口,说明也是具有server端向client端传输数据的能力。
asInterface:将服务端的Binder对象转换成客户端需要的aidl接口类型对象,在这里检查客户端和服务端是否处于同一进程,同一进程直接返回Binder本地对象(也就是stub),不同进程则返回Binder代理对象(也就是stubproxy)。
asBinder:返回当前的Binder对象,获取IIterface接口所关联的BinderProxy。

3.AIDL支持的数据类型:

1.基本数据类型:int, long, char, boolean, double
2.String和CharSequence类型
3.List,只支持Arraylist,并且list里面每一个元素都必须能被aidl支持
4.Map,只支持HaspMap,并且map里面每一个元素都必须能被aidl支持,包括key和value
5.Parcelable,所有实现了Parcelable接口的对象
6.AIDL,所有的aidl接口本身也是可以在aidl中使用的

4.AIDL过程需要注意的地方:

4.1.如果aidl文件中使用了自定义的parcelable对象

那么必须要创建一个和它同名的aidl文件,并在其中声明为Parcelable类型。并且aidl中除了基本类型,其他类型的参数必须要标明方向int、out或者inout,in输入型参数,out输出型参数,inout输入输出型参数。此外aidl接口只可以声明方法,不能声明静态常量的这一点是和传统的接口有区别的。

interface IBookManager {
     List<Book> getBookList();
     void addBook(in Book book);
}
package com.ryg.chapter_2.aidl;
parcelable Book;

4.2.aidl服务端的线程同步

由于aidl服务端方法是在binder线程池中执行的,当多个客户端来访问的时候会存在并发读写问题。比较常见的可以使用CopyOnWriteArrayList来支持并发读写自动实现线程同步,类似的还有ConcurrentHashMap

4.3.跨进程解注册监听器RemoteCallbackList

aidl跨进程unRegisterListener解注册监听器的时候,由于跨进程客户端和服务端不是同一个对象所以会解注册失败。可以使用RemoteCallbackList处理,它是系统专门提供的用来删除跨进程listener的接口。它的实现原理:内部有一个map来保存所有的callback回调,map的key值是IBinder类型,value是callback类型。RemoteCallbackList本质上不是一个list,因此不可以有类似list的操作,使用的时候必须beginBroadcast和finishBroadcast配套使用。

4.4.aidl服务端耗时可能引起的ANR问题

如果确认服务端方法耗时的话客户端调用的时候放在非ui线程即可。

4.5.在aidl中使用权限验证功能

4.5.1一种方法是在onBind中做permission验证,验证不通过返回null使得客户端无法连接服务

首先需要在服务端的 AndroidMenifest 文件中声明所需权限

<permission android:name="com.example.xxx.permission.ACCESS_COMPUTE_SERVICE" android:protectionLevel="normal" />
<uses-permission android:name="com.example.xxx.permission.ACCESS_COMPUTE_SERVICE" />

客户端 AndroidManifest.xml 中添加权限:

<uses-permission android:name="com.example.xxx.permission.ACCESS_COMPUTE_SERVICE" />

在 Service 中的 onBind 方法中处理代码如下:

public IBinder onBind(Intent t) {
     // 远程调用的验证方式
    int check = checkCallingPermission("com.example.xxx.permission.ACCESS_COMPUTE_SERVICE"); 
    if (check == PackageManager.PERMISSION_DENIED) {
        // 权限校验失败
         return null;
    } 
    return mBinder;
}

4.5.2一种是在服务端的onTransact方法中来做验证,同样也可以做permission验证。最后还可以做包名验证,拿到客户端应用的uid和pid来做包名验证。

// 权限校验
    private boolean checkPermission(Context context, String permName, String pkgName) {
        PackageManager pm = context.getPackageManager();
        if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(permName, pkgName)) {
            return true;
        } else {
            return false;
        }
    }
    
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        // 权限校验
        String packageName = null;
        String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
        if (packages != null && packages.length > 0) {
            packageName = packages[0];
        }
        if (packageName == null) {
            return false;
        }
        boolean checkPermission = checkPermission(getApplication(),
                "com.example.xxx.permission.ACCESS_COMPUTE_SERVICE", packageName);
        return checkPermission && super.onTransact(code, data, reply, flags);
    }

5.AIDL过程(手动实现):

5.1首先声明一个aidl性质的接口,并且继承IInterface。

接口有一个方法,声明一个binder描述和id,如果接口中不止一个方法那就再多声明几个id就是了。实现如下:

public interface IHWCompute extends IInterface {  
   //第一步:声明binder描述,id和add方法
   //DESCRIPTOR:binder的唯一标识,一般使用类名+aidl接口名字
   static final String DESCRIPTOR = "com.thh.ipcdemo2binderpool.IHWCompute";
   static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);   
   public int add(int a, int b) throws android.os.RemoteException;
} 

5.2实现stub类ComputeImpl

ComputeImpl继承了Binder说明它是一个Binder本地对象,又实现了IHWCompute接口因此它携带了某种客户端需要的能力(在这里就表示add方法)

//第二步:实现ComputeImpl(Stub类),该内部类里面的方法都是运行在server端的
public class HWComputeImpl extends Binder implements IHWCompute {
    /**
     * 构造函数
     */
    public HWComputeImpl() {
        this.attachInterface(this, DESCRIPTOR);
    }
    
    //add方法运行在客户端,流程如下:
    //创建输入对象_data和返回对象_reply,接着调用transact发起RPC请求同时当前线程挂起。
    //然后服务端的onTransact方法会被调起执行,RPC返回结果后当前线程继续执行,并获取RPC的结果
    @Override
    public int add(int a, int b) throws RemoteException {
        //暂不实现
        return 0;
    }
    
    //返回当前的Binder对象
    @Override
    public IBinder asBinder() {
        return null;
    }
    
    //将服务端的Binder对象转换成客户端需要的aidl接口类型对象,在这里检查客户端和服务端是否处于同一进程,
    //同一进程直接返回Binder本地对象,不同进程则返回Binder代理对象。
    public static IHWCompute asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if ((iin != null) && (iin instanceof IHWCompute)) {
            return (IHWCompute) iin;
        }
        //待实现,返回Binder代理对象
        return new Proxy(obj);
    }
    
    //该方法位于服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求通过系统底层封装之后交给该方法来
    //处理。onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
    //几个参数解释:code标识客户端请求的方法是什么,data带有方法所需的目标参数(如果有),
    //然后方法开始执行执行完毕后向relply中写入返回值(如果有)。此方法返回true标识客户端请求成功,false标识客户端请求失败
    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_add: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.add(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }
}

5.3实现stubProxy代理内部类,它是Binder(ComputeImpl)代理对象

这里直接贴下IHWCompute的完整代码

public interface IHWCompute extends IInterface {
    //第一步:声明binder描述,id和add方法
    static final String DESCRIPTOR = "com.thh.ipcdemo2binderpool.IHWCompute";
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

    public int add(int a, int b) throws android.os.RemoteException;

    //第二步:实现ComputeImpl(Stub类),该内部类里面的方法都是运行在server端的
    public class HWComputeImpl extends Binder implements IHWCompute {

        /**
         * 构造函数
         */
        public HWComputeImpl() {
            this.attachInterface(this, DESCRIPTOR);
        }

        @Override
        public int add(int a, int b) throws RemoteException {
            //暂不实现
            return 0;
        }

        @Override
        public IBinder asBinder() {
            return null;
        }

        public static IHWCompute asInterface(IBinder obj) {
            if (obj == null) {
                return null;
            }
            IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if ((iin != null) && (iin instanceof IHWCompute)) {
                return (IHWCompute) iin;
            }
            //待实现,返回Binder代理对象
            return new Proxy(obj);
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_add: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        /**
         * ComputeImpl的代理
         */
        private static class Proxy implements IHWCompute {
            private IBinder mRemote;

            public Proxy(IBinder remote) {
                mRemote = remote;
            }

            @Override
            public IBinder asBinder() {
                return null;
            }

            @Override
            public int add(int a, int b) throws RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(HWComputeImpl.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
    }
}

5.4linkToDeath和unlinkToDeath,监听远程服务端Binder的死亡,死亡重连,代码示例如下:

private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        IHWCompute mIHWCompute = IHWCompute.HWComputeImpl.asInterface(service);
        try {
            int count = mIHWCompute.add(3, 2);
            Log.i("MainAcivity", "doWork1 count:" + count);
            //失败重连
            mIHWCompute.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        doWork1();
    }
};

5.5AIDL总结

aidl比较固定的模式:
1先定义一个接口标明你需要哪些能力,该接口必须要实现IInterface
2.接口定义完了我们需要一个跨进程传递的对象也就是Stub,它肯定继承自Binder。在这儿作区分,如果是Binder本地对象那么继承Binder且实现你刚刚定义的接口,如果是代理对象Proxy那么就持有IBinder(挟天子以令诸侯)且实现你刚刚定义的接口。
3.在客户端绑定服务onServiceConnected回调里调用asInterface将IBinder转化为客户端需要的接口类型的对象,就可以很方便的。

6.Binber连接池

线程池的背景:如果项目中有很多个业务模块都需要进程间通信,按照常规做法就需要创建很多个aidl文件以及很多个service,但是service创建过多会消耗系统资源会很重量级。binder连接池做的就是减少service的数量,将所有的aidl放到一个service中去管理。具体:客户端每个业务模块创建自己的aidl接口并实现,然后想服务端提供自己的唯一标识和对应binder对象。服务端提供一个queryBinder接口,根据业务模块id返回对应的binder对象给客户端,客户端不同模块拿到binder对象之后便可以进行远程调用了。binder链接池的作用就是讲各个业务模块的binder请求统一转发到远程service中去执行,避免了重复创建service的过程。按照我自己的理解就是binder连接池是一个容器,它将所有的aidl接口对应的binder装在一起,再另外提供一个查询aidl接口根据模块id返回不同的binder对象给你。
在这里插入图片描述现在用一个例子来详细描述:

6.1.首先定义两个aidl接口:加解密接口和求和接口

interface ISecurityCenter {

    String encrypt(String content);
    String decrypt(String password);
}
interface ICompute {
    int add(int a, int b);
}

6.2.接着是这两个aidl接口的实现

public class SecurityCenterImpl extends ISecurityCenter.Stub {

    private static final char SECRET_CODE = '^';

    @Override
    public String encrypt(String content) throws RemoteException {
        char[] chars = content.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] ^= SECRET_CODE;
        }
        return new String(chars);
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return encrypt(password);
    }
}
public class ComputeImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }
}

6.3.aidl接口定义和实现完成之后,准备服务端和binder连接池,创建binder连接池aidl接口与实现

interface IBinderPool {
    IBinder queryBinder(int binderCode);
}
public class BinderPool {

    public static final int BINDER_SUCURITY_CENTER = 1000;
    public static final int BINDER_COMPUTE = 1001;

    private static BinderPool sInstance = null;
    private final Context mContext;
    private CountDownLatch mConnectBinderPoolCountDownLatch;
    private IBinderPool mBinderPool;

    public static BinderPool getInstance(Context context){
        if (sInstance == null){
            synchronized (BinderPool.class){
                if (sInstance == null){
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    public BinderPool(Context context){
        this.mContext = context.getApplicationContext();
        connectBinderPoolService();
    }

    private synchronized void connectBinderPoolService() {
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent intent = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        try {
            mConnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            connectBinderPoolService();
        }
    };

    public IBinder queryBinder(int binderCode){
        IBinder binder = null;
        try {
            if (mBinderPool != null) {
                binder = mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

    public static class BinderPoolImpl extends IBinderPool.Stub{

        public BinderPoolImpl(){
            super();
        }

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode){
                case BINDER_SUCURITY_CENTER:
                    binder = new SecurityCenterImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
            }
            return binder;
        }
    }
}

6.4.服务端service的实现

public class BinderPoolService extends Service {

    private Binder mBinderPool = new BinderPool.BinderPoolImpl();

    @Override
    public void onCreate() {
        super.onCreate();
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

6.5客户端调用binder连接池

private void doWork() {
    BinderPool binderPool = BinderPool.getInstance(this);
    IBinder iBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
    ICompute iCompute = ICompute.Stub.asInterface(iBinder);
    try {
        int count = iCompute.add(3, 6);
        Log.i("thhi", "[Cp3MainActivity doWork] count:" + count);
    } catch (RemoteException e) {
        e.printStackTrace();
    }

    IBinder iBinder1 = binderPool.queryBinder(BinderPool.BINDER_SUCURITY_CENTER);
    ISecurityCenter iSecurityCenter = ISecurityCenter.Stub.asInterface(iBinder1);
    try {
        String encrypt = iSecurityCenter.encrypt("abcdefghyjklmn");
        Log.i("thhi", "[Cp3MainActivity doWork] encrypt:" + encrypt);
        String decrypt = iSecurityCenter.decrypt(encrypt);
        Log.i("thhi", "[Cp3MainActivity doWork] decrypt:" + decrypt);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值