美文网首页
移动架构<第十三篇>:跨进程通信架构

移动架构<第十三篇>:跨进程通信架构

作者: NoBugException | 来源:发表于2021-08-07 22:21 被阅读0次

    https://github.com/Xiaofei-it/Hermes/blob/master/README-ZH-CN.md

    AIDL是Android跨进程通信的重要手段,其中核心思想就是“动态代理”。
    动态代理:利用Java的反射技术,在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类(Abstract Class)。

    1、代理模式

    (1)静态代理

    假设有一个对象, 定义为:

    public interface SpeakInterface {
    
        // 说英语
        void speakEnglist();
    
        // 说中文
        void speakChinese();
    
    }
    
    
    public class Speak implements SpeakInterface {
    
        @Override
        public void speakEnglist() {
            System.out.println("说英语");
        }
    
        @Override
        public void speakChinese() {
            System.out.println("说中文");
        }
    
    }
    

    Speak对象的给出的能力就是speakEnglistspeakChinese,我们可以直接使用如下代码直接执行该能力

    // 上转型对象,使用多态特性
    SpeakInterface speak = new Speak();
    speak.speakEnglist();
    speak.speakChinese();
    

    在某些特定的场景下,需要一个其它类代为执行speakEnglistspeakChinese,代码如下:

    public class SpeakProxy implements SpeakInterface {
    
        private SpeakInterface speak = new Speak();
    
        @Override
        public void speakEnglist() {
            speak.speakEnglist();
        }
    
        @Override
        public void speakChinese() {
            speak.speakChinese();
        }
    }
    

    SpeakProxy就是代理对象,使用代理对象实现Speak的能力

    // 上转型对象,使用多态特性
    SpeakInterface speak = new SpeakProxy();
    speak.speakEnglist();
    speak.speakChinese();
    

    这就是静态代理

    静态代理的特性:

    • 委托对象(Speak)和代理对象(SpeakProxy)必须实现相同的接口(SpeakInterface);
    • 代理对象(SpeakProxy)需要拿到委托对象(Speak),由委托对象(Speak)来实现原有的能力;

    (2)动态代理

    静态代理需要手动定义代理类(SpeakProxy),然而动态代理是通过

    Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    

    来创建的,其底层是通过反射机制来获取代理对象, 代码实现如下:

        SpeakInterface speakInterface = new Speak();
        SpeakHandler handler = new SpeakHandler(speakInterface);
        SpeakInterface speak = (SpeakInterface) Proxy.newProxyInstance(speakInterface.getClass().getClassLoader(), speakInterface.getClass().getInterfaces(), handler);
        speak.speakChinese();
    
    
    private static class SpeakHandler implements InvocationHandler {
    
        private SpeakInterface speakInterface;
    
        public SpeakHandler(SpeakInterface speakInterface) {
            this.speakInterface = speakInterface;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(speakInterface, args);
        }
    }
    

    Proxy.newProxyInstance的核心代码是利用反射机制,获取类、接口、方法,并通过invoke方法执行对应的方法。

    既然是反射,那么可以尽量使用反射机制完善下代码:

        // 从远程传递过来的className字符串
        String className = Speak.class.getName();
        Class<SpeakInterface> clazz = null;
        Object object = null;
        try {
            clazz = (Class<SpeakInterface>) Class.forName(className);
            Constructor constructor = clazz.getDeclaredConstructor();
            object = (SpeakInterface) constructor.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        if (clazz == null) {
            return;
        }
        if (object == null) {
            return;
        }
        SpeakHandler handler = new SpeakHandler(object);
        SpeakInterface speak = (SpeakInterface) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), handler);
        speak.speakChinese();
    
    private static class SpeakHandler implements InvocationHandler {
    
        private Object object;
    
        public SpeakHandler (Object object) {
            this.object = object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(object, args);
        }
    }
    
    2、封装AIDL

    AIDL是Android自带的跨进程通信架构,为了实现更加优雅的跨进程通信实现方式,需要对AIDL进行重新封装。

    【第一步】 依赖

    通信双方需要添加依赖

    在项目根目录的build.gradle中引入maven

    allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }
    

    在对应模块的build.gradle中引入依赖

    implementation 'com.github.NoBugException:IPCBusDemo:1.0.1'
    

    【第二步】 初始化上下文

        // 初始化
        IPCBus.getDefault().init(getApplicationContext(), "com.ipcbus.ipcbusservice");
        IPCBus.getDefault().init(getApplicationContext(), "com.nobug.ipcbusservice2");
        IPCBus.getDefault().init(getApplicationContext(), getPackageName());
    

    初始化的操作最好放在自定义Application中的onCreate中。
    除了指定上下文之外,还需要指定通信目标的包名

    /**
     *  初始化
     * @param mContext mContext
     */
    public void init(Context mContext, String servicePackageName) {
        this.mContext = mContext;
        // 连接
        connect(null, servicePackageName);
    }
    

    初始化的时候传递包名是为了连接远程服务,保证通信的通道是连通的。

    【第三步】 发送和响应

                Request request = new Request();
                request.setRequestName("subtraction");
                JSONObject jsonObject = new JSONObject();
                try {
                    jsonObject.put("parameter1", 5);
                    jsonObject.put("parameter2", 4);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                request.setParamJson(jsonObject.toString());
                // 发送消息
                Responce responce = IPCBus.getDefault().send(request);
                if (responce != null) {
                    Toast.makeText(MainActivity2.this, "减法结果:" + responce.getData(), Toast.LENGTH_SHORT).show();
                }
    

    对远程服务发送相关数据,远程服务根据这些数据响应结果。

    【第四步】 可以在适当的释放资源

        // 断开连接
        IPCBus.getDefault().unConnected(getPackageName());
    

    【第五步】 远程进程需要注册Service

        <!--定义服务-->
        <service android:name="com.noexception.ipcbus.service.IPCBusService" />
    

    需要注意的是,name="com.noexception.ipcbus.service.IPCBusService"是固定写法,它被封装在依赖中。

    【第六步】 远程进程还需要注册接收类

        // 注册接收类
        IPCBus.getDefault().regist(MyReceive.class);
    

    这个操作最好在自定义Application的onCreate中,用于接收请求消息。

    /**
     * 服务端侧申明接口的地方
     */
    public class MyReceive implements ICPReceive {
    
        @Override
        public Responce receive(Request request) {
            Responce responce = null;
            if ("addition".equals(request.getRequestName())) { // 加法运算
                responce = new Responce();
                responce.setResultCode(ResultCode.SUCCESS_CODE);
                try {
                    JSONObject jsonObject = new JSONObject(request.getParamJson());
                    int parameter1 = jsonObject.optInt("parameter1");
                    int parameter2 = jsonObject.optInt("parameter2");
                    responce.setData(String.valueOf(parameter1 + parameter2));
                } catch (JSONException e) {
                    responce.setData("数据异常");
                }
            } else if ("subtraction".equals(request.getRequestName())) { // 减法运算
                responce = new Responce();
                responce.setResultCode(ResultCode.SUCCESS_CODE);
                try {
                    JSONObject jsonObject = new JSONObject(request.getParamJson());
                    int parameter1 = jsonObject.optInt("parameter1");
                    int parameter2 = jsonObject.optInt("parameter2");
                    responce.setData(String.valueOf(parameter1 - parameter2));
                } catch (JSONException e) {
                    responce.setData("数据异常");
                }
            } else {
                // 其它
            }
            return responce;
        }
    }
    

    MyReceive的receive方法给发送法响应数据。

    代码的具体细节可以查看项目:

    https://github.com/NoBugException/IPCBusDemo

    [本章完...]

    相关文章

      网友评论

          本文标题:移动架构<第十三篇>:跨进程通信架构

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