美文网首页
【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