• 跨进程架构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时还有个崩溃:

  • 相关阅读:
    Comparable与Comparator
    【源码】String类、jdk1.6subString内存泄漏、字符串拼接几种区别、
    JAVA整型包装类的缓存策略
    通过tomcat把项目http请求转为https请求
    git rebase总结及git使用规范
    记一次对象序列化不打印value值为null的属性问题
    Layui-Tables+PHP分页
    Python操作字符串-截取IP地址
    命令行启动VMware虚拟机
    bat批处理备份桌面所有文档
  • 原文地址:https://www.cnblogs.com/webor2006/p/12199133.html
Copyright © 2020-2023  润新知