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

前言

在上一篇文章【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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容