美文网首页
【Android最最简单】AIDL飞升(动态代理实现跨进程方法调

【Android最最简单】AIDL飞升(动态代理实现跨进程方法调

作者: 修鍋的江左喵郎 | 来源:发表于2021-03-20 12:55 被阅读0次

    前言

    在上一篇文章【Android最最简单】AIDL进阶(双向通信)中,已经向大家介绍了AIDL的进阶用法,用于service和client的双向调用,基本上可以满足大家的日常开发需求,且建立一套基于aidl实现的跨进程调用框架对于一个新需求或者一个新立项的项目,也完全没有什么难度,操起键盘代码一撸到底就是。但是,如果对于一个维护了N手的项目,突然要把已经实现了的N多需求转成跨进程实现,那就懵逼了,N多方法都需要用aidl实现,N多传输的bean类都要实现Parceable序列化,三N系列,想想就头大!

    img

    只要思想不滑坡,办法总比困难多!

    解题思路

    解题思路-1.0

    实现一个通用接口,所有传输数据转json字符串,在接收数据后在进行拆箱还原成bean类,好了,完美解决,喜大普奔!此文over!

    img

    如此处理,虽然也不是不行,但这样简单,操作如此复杂,还要这篇文章干嘛!

    解题思路-2.0

    进入正题,最容易想到的方法是纯AIDL实现,接口文件一一对应,所有方法都编写AIDL接口文件实现!使用系列上一篇【Android最最简单】AIDL进阶(双向通信)介绍内容即可轻松实现。

    • 优点:简单易懂。
    • 缺点:
      • 改造成本:成本高,比较适合新项目,不适合老项目;
      • 后续维护:aidl接口文件和aidl序列化对象文件多,不利于维护。

    解题思路-3.0

    既然aidl可以传输数据,那么可不可以传输要调用的接口类名、方法名以及参数,在接收到数据后根据这些数据找到对应的实现类,执行方法然后返回结果呢?这样对调用者来说无感知,对提供者来说成本最小,可以简单发散一下思维,想象一下发起一次网络请求,我们只需要知道对应的url和对应的参数就可以了。

    答案当然是可以了,要不然废话这么多干啥。是时候祭出今天的大杀器了——动态代理&反射

    杀猪刀-实操

    1.创建双向通信

    基于系列上一篇文章【Android最最简单】AIDL进阶(双向通信)中向大家介绍的进阶方法创建双向通信AIDL接口文件。

    interface IService {
        IPCResponse sendRequest(in IPCRequest request);
    
        void attach(in IClientBridge iClientBridge);
    }
    
    interface IClientBridge {
        IPCResponse sendRequest(in IPCRequest request);
    }
    

    模仿网络请求创建一个存储请求参数的接口类(IPCRequest)以及一个承载返回结果的接口类(IPCResponse),只需指定实现parcelable,具体实现类后续实现。

    parcelable IPCRequest;
    
    parcelable IPCResponse;
    

    2.缓存实现对象

    将实现object按实现接口名缓存对应的class、根据class缓存所有Method,并将object本身缓存。

    public class IPCCache {
    
        /**
         * 保存服务端处理客户端请求的interfaces对应Class映射和内部的方法
         */
        private final Map<String, Class<?>> mClazzs = new HashMap<>();
        private final Map<Class<?>, HashMap<String, Method>> mMethods = new HashMap<>();
        /**
         * 保存服务端处理客户端请求的实例
         */
        private final Map<String, WeakReference<Object>> mInstance = new HashMap<>();
    
        /**
         * 缓存对象及其方法
         *
         * @param object
         */
        public void register(Object object) {
            Class<?> clazz = object.getClass();
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class<?> cls : interfaces) {
                mClazzs.put(cls.getName(), clazz);
            }
            // 缓存Method
            HashMap<String, Method> method = new HashMap<>();
            Method[] methods = clazz.getMethods();
            for (Method m : methods) {
                method.put(m.getName(), m);
            }
            mMethods.put(clazz, method);
            addObject(clazz.getName(), object);
        }
    
        public void unRegister(Object object) {
            Class<?> clazz = object.getClass();
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class<?> cls : interfaces) {
                mClazzs.remove(cls.getName());
            }
            mMethods.remove(clazz);
            removeObject(clazz.getName());
        }
    
        public Class<?> getClass(String interfacesName) {
            if (TextUtils.isEmpty(interfacesName)) {
                return null;
            }
            return mClazzs.get(interfacesName);
        }
    
        public Method getMethod(Class<?> clazz, String methodName) {
            HashMap<String, Method> methods = mMethods.get(clazz);
            return methods == null ? null : methods.get(methodName);
        }
    
        private void addObject(String className, Object object) {
            mInstance.put(className, new WeakReference<>(object));
        }
    
        private void removeObject(String className) {
            mInstance.remove(className);
        }
    
        public Object getObject(String className) {
            return mInstance.containsKey(className) ? mInstance.get(className).get() : null;
        }
    }
    

    3.动态代理[以client为例]

    通过动态代理,将方法名、参数等封装成IPCRequest发送到service。

        public <T> T get(Class<T> inter) {
            if (null == mIpcService) {
                return null;
            }
            // 获取处理客户端请求的对象的Key,以此在服务端找出对应的处理者
            return (T) Proxy.newProxyInstance(getClass().getClassLoader(),
                    new Class[]{inter},
                    new ClientInvocationHandler(inter.getName()));
        }
    
    public class ClientInvocationHandler implements InvocationHandler {
    
        private Gson mGson;
        private String interfacesName;
    
        public ClientInvocationHandler(String interfacesName) {
            this.interfacesName = interfacesName;
            mGson = new Gson();
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            /*
              当代理对象执行方法时,会走到这里
              然后构造请求
              转发给服务端
             */
            IPCRequest ipcRequest = new IPCRequest();
            ipcRequest.setInterfacesName(interfacesName);
            ipcRequest.setMethodName(method.getName());
            ipcRequest.setParameters(ParamsConvert.serializationParams(args));
            IPCResponse ipcResponse = ClientManager.getInstance().sendRequest(ipcRequest);
            if (ipcResponse != null && ipcResponse.isSuccess()) {
                Class<?> returnType = method.getReturnType();
                if (returnType != void.class && returnType != Void.class) {
                    return mGson.fromJson(ipcResponse.getResult(), returnType);
                }
            }
            return null;
        }
    }
    

    4.实现双向通信方法[以service端为例]

    提供根据请求IPCRequest中携带的接口名、方法名以及参数,找到对应的实现类对象和Method,通过反射调用执行方法,并将结果返回。

            @Override
            public IPCResponse sendRequest(IPCRequest request) throws RemoteException {
                try {
                    Class<?> aClass = ServiceManager.getInstance().getClass(request.getInterfacesName());
                    Object object = ServiceManager.getInstance().getObject(aClass.getName());
                    Method me = ServiceManager.getInstance().getMethod(aClass, request.getMethodName());
    
                    Object[] params = ParamsConvert.unSerializationParams(request.getParameters());
                    Object result = me.invoke(object, params);
                    String r = ParamsConvert.mGson.toJson(result);
                    return new IPCResponse(r, "执行方法成功", true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
    

    Demo链接

    https://github.com/LongAgoLong/AIDL-IPC

    相关文章

      网友评论

          本文标题:【Android最最简单】AIDL飞升(动态代理实现跨进程方法调

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