跨进程架构HermesEventBus原理分析到手写实现<三>

发送请求到服务端:

继续接着上一次https://www.cnblogs.com/webor2006/p/12196171.html的代码继续来编写,上一次在SecondAcitity中完成了远程服务的连接了:

接下来则应该是发送消息给主进程,然后在MainActivity中进行消息的接收处理了,我们知道在主进程中我们注册了一个UserManager单例对象:

那如果我们在SecondActivity改变这个单例对象的值:

很明显在MainActivity的主进程中再获取的话是收不到在子进程更改的数据变化的:

因为这俩是不在一个进程了,那接下来就以这个为例子,如果能实现跨进程的情况下也能达到相互数据及时感知,那其实也就实现了Hermes框架的效果了,当然要实现肯定得借助于AIDL了,不过这里还得借助于动态代理来达成,之前咱们在分析Hermes的原理时也看到了动态代理隐藏其中, 下面则来开始实现,稍麻烦一点,坚持:

为了能够清楚的知道咱们要实现它的具体步骤,跟着图来:

也就是客户端要通过aidl来告诉服务端需要给客户端返回服务端的UserManager单例对象,好,下面实现一下,先修改一下布局:

具体来实现一下,由于目前在子进程无法拿到父进程的UserManager对象实例,所以这里需要定义一个接口来将这个类中的行为抽象出来:

而这个接口对应的是哪个类这里用注解标注一下,以便到时反射时进行动态代理时好知道最终要代理的对象的类名:

然后让UserManager实现这个接口:

刚才不是说最终要用到动态代理技术么,最终对象的生成则就是采用动态代理来实现的,所以我们看到Hermes框架也是这么搞的,瞅一下:

 

而它也是实现了一个接口:

接口中则都是抽象的行为:

此时咱们获取对像的代码就可以这样写的:

接下来则集中精力来实现这个方法,实现了跨进程发消息的问题就基本就解决了:

而通过aidl发送的方法是Request,它里面只有一个String属性:

所以,咱们应该对所有的参数信息进行对象封装一下,最终再转换成一个Json串并生成咱们的Request对象,所以接下来新建一个JavaBean:

好,接下来具体实现一下:

public class MyHermes {
    private static final MyHermes ourInstance = new MyHermes();
    private static final Gson GSON = new Gson();
    private Context context;
    private MyTypeCenter typeCenter;
    private ServiceConnectionManager serviceConnectionManager;


    public static MyHermes getDefault() {
        return ourInstance;
    }

    private MyHermes() {
        typeCenter = MyTypeCenter.getInstance();
        serviceConnectionManager = ServiceConnectionManager.getInstance();
    }

    public void init(Context context) {
        context = context.getApplicationContext();
    }

    public void register(Class<?> clazz) {
        typeCenter.register(clazz);
    }

    public void connectApp(Context context, Class<? extends HermesService> service) {
        connectApp(context, null, service);
    }

    public void connectApp(Context context, String packageName, Class<? extends HermesService> service) {
        init(context);
        serviceConnectionManager.bind(context.getApplicationContext(), packageName, service);
    }

    //获取另一个进程的对象
    public <T> T getInstance(Class<T> clazz, Object... parameters) {
        Response responce = sendRequest(HermesService.class, clazz, null, parameters);
        return null;
    }

    private <T> Response sendRequest(Class<HermesService> hermesServiceClass
            , Class<T> clazz, Method method, Object[] parameters) {
        RequestBean requestBean = new RequestBean();

        String className = null;
        if (clazz.getAnnotation(ClassId.class) == null) {
            //有注解
            requestBean.setClassName(clazz.getName());
            requestBean.setResultClassName(clazz.getName());
        } else {
            //木有注解时返回类型的全类名
            requestBean.setClassName(clazz.getAnnotation(ClassId.class).value());
            requestBean.setResultClassName(clazz.getAnnotation(ClassId.class).value());
        }
        if (method != null) {
            //方法名 统一传 方法名+参数名  getInstance(java.lang.String)
            requestBean.setMethodName(TypeUtils.getMethodId(method));
        }

        RequestParameter[] requestParameters = null;
        if (parameters != null && parameters.length > 0) {
            requestParameters = new RequestParameter[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Object parameter = parameters[i];
                String parameterClassName = parameter.getClass().getName();
                String parameterValue = GSON.toJson(parameter);

                RequestParameter requestParameter = new RequestParameter(parameterClassName, parameterValue);
                requestParameters[i] = requestParameter;
            }
        }

        if (requestParameters != null) {
            requestBean.setRequestParameter(requestParameters);
        }

        return null;
    }
}

好,接下来则将上面的这个封装bean利用gson转换成json串,最后再生成咱们要发送的Request对象,再通过aidl进行发送,这里在发送Request其实是有两种类型的,所以咱们定义两个常量值,并给Request增加一个类型字段:

public class Request implements Parcelable {
    private String data;
    //请求对象的类型
    private int type;

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    protected Request(Parcel in) {
        data = in.readString();
        type = in.readInt();
    }

    public Request(String data, int type) {
        this.data = data;
        this.type = type;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(data);
        dest.writeInt(type);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<Request> CREATOR = new Creator<Request>() {
        @Override
        public Request createFromParcel(Parcel in) {
            return new Request(in);
        }

        @Override
        public Request[] newArray(int size) {
            return new Request[size];
        }
    };
}

其aidl的发送则转由ServiceConnectionManager负责了,所以接下来实现一下发送逻辑:

此时就通过AIDL将请求发送到了Service了,目前咱们的Service还木有实现:

所以接下来咱们来处理它。

服务端消息处理:

接下来则到这一步了:

这里则需要根据不同的requesttype来生成不同的response,这里需要用到策略模式了,其实Hermes框架也是类似,瞅一下:

其中对于第个Receiver进行了一些抽象:

所以咱们也校仿一下,先建立一个抽象的Response生成对象:

public abstract class ResponceMake {
    //UserManage  的Class
    protected Class<?> reslutClass;
    // getInstance()  参数数组
    protected Object[] mParameters;

    Gson GSON = new Gson();

    protected MyTypeCenter typeCenter = MyTypeCenter.getInstance();


    public Response makeResponce(Request request) {
        RequestBean requestBean = GSON.fromJson(request.getData(), RequestBean.class);
        reslutClass = typeCenter.getClassType(requestBean.getResultClassName());
        //参数还原
        RequestParameter[] requestParameters = requestBean.getRequestParameter();
        if (requestParameters != null && requestParameters.length > 0) {
            mParameters = new Object[requestParameters.length];
            for (int i = 0; i < requestParameters.length; i++) {
                RequestParameter requestParameter = requestParameters[i];
                Class<?> clazz = typeCenter.getClassType(requestParameter.getParameterClassName());
                mParameters[i] = GSON.fromJson(requestParameter.getParameterValue(), clazz);
            }
        } else {
            mParameters = new Object[0];
        }

        setMethod(requestBean);
        Object resultObject = invokeMethod();
        //TODO 需要转换成Response
        return null;
    }

    protected abstract Object invokeMethod();

    protected abstract void setMethod(RequestBean requestBean);
}

然后咱们来处理具体的子类,目前先来处理单例UserManager的获取:

其中先来实现setMethod(),也就是根据方法的参数信息最终来生成一个Method对象,如下:

其中method就是调用UserManager中的getInstance()方法:

比如好理解,直接贴出代码了,接着再来实现invokeMethod():

 

好,此时再回到抽象ResponseMake类,再处理Response结果转换的逻辑:

此时就又需要借助于一个封装的bean类进行转换,如下:

 

其中Respnse需要再增加一个构造,直接对里面的data赋值:

最后咱们在Service中来使用一下:

好,再回到主流程来,目前Response已经从服务端拿到之后,则需要根据Response通过动态代理来调用对象中的具体方法了,也就是流程来到了这:

对应代码为:

 

这里则就是需要产生一个代理对象了,具体代码如下:

接下来就是来实现这个InvocationHandler的代码了,先来从调用角度来看一下,当我们获得了UserManager的代理对象之后,则会如此调用了:

而每个方法的调用很显然都得有一个AIDL的过程,也就是要通知到服务端来进行调用,那既然每个方法都需要有这么一个过程,那用动态代理的拦截机制不正好么,所以接睛来咱们在拦截处进行方法的远程调用:

上面发送代码基本跟之前获取getInstance的差不多,可以对其进行封装一下,这里就不追求优质代码了, 重点是理解背后的原理。

好,此时已经将setStudent()的方法请求发送到了服务端了,接着服务端需要对其进行处理,回到主服务的代码:

 

跟之前的类似使用策略模式,先建议一个子类,然后再使用之则可:

其中TypeCenter中需要增加一个getMethod()方法:

其中可以看到都是从之前我们主进程进行注册时的缓存中拿的方法,所以性能也是比较好的。

好,准备了这个Response之后下面则来使用一下:

至此!!!关于得到UserManager单例对象以及调用它里面的setStudent()方法的整个底层的aidl逻辑都写法了,说实话,真的好麻烦呀~~还是挺佩服饿了么开源的工程师,那写了这么多代码到底好不好使呢?下面来应用一下:

好,编译运行,在SecondActivity获取UserManager单例时报错了,错误信息如下:

呃,好吧,粗心了,确实不是接口:

咱们修复一下:

好,再次运行:

嗯,完美的实现了我们想要的跨进程的功能,当然目前代码不是太完善,但是木有关系,实现了核心的功能对于这些完善都是可以自己再弄的,再说如果商用也不可以是真的用手写的框架,一般都会用三方成熟的开源组件,但是从学习的角度只有这样才能让自己学到东东,最后在返回app时还有个崩溃:

原文地址:https://www.cnblogs.com/webor2006/p/12199133.html