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

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

作者: 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