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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
将这段代码修改成轮播图效果需要引入第三方库或者自己手轮播图的 JS 代码。以下是手轮播图的示例代码: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title></title> <style type="text/css"> .slider { width: 600px; height: 300px; margin: 0 auto; overflow: hidden; position: relative; } .slider ul { width: 3000px; height: 300px; position: absolute; left: 0; top: 0; } .slider li { float: left; width: 600px; height: 300px; } .slider img { width: 100%; height: 100%; } .slider .prev, .slider .next { width: 50px; height: 50px; background-color: black; color: white; text-align: center; line-height: 50px; font-size: 20px; position: absolute; top: 50%; margin-top: -25px; cursor: pointer; } .slider .prev { left: 0; } .slider .next { right: 0; } </style> </head> <body> <div class="slider"> <ul> <li><img src="../images/1.jpg" alt=""></li> <li><img src="../images/2.jpg" alt=""></li> <li><img src="../images/3.jpg" alt=""></li> <li><img src="../images/4.jpg" alt=""></li> <li><img src="../images/5.jpg" alt=""></li> <li><img src="../images/6.jpg" alt=""></li> </ul> <span class="prev">❮</span> <span class="next">❯</span> </div> <script src="../jquery-3.7.0.js"></script> <script type="text/javascript"> $(function () { var index = 0; var timer = null; var len = $(".slider li").length; function showImg(index) { $(".slider li").eq(index).fadeIn().siblings().fadeOut(); } function autoPlay() { timer = setInterval(function () { index++; if (index == len) { index = 0; } showImg(index); }, 2000); } $(".slider").hover( function () { clearInterval(timer); }, function () { autoPlay(); } ); $(".prev").click(function () { index--; if (index == -1) { index = len - 1; } showImg(index); }); $(".next").click(function () { index++; if (index == len) { index = 0; } showImg(index); }); autoPlay(); }); </script> </body> </html> ``` 这段代码实现了一个简单的轮播图,通过点击左右箭头或者自动播放来切换图片。其中,`.slider` 是轮播图的容器,`.slider ul` 是所有图片的容器,`.slider li` 是每张图片的容器,`.slider img` 是每张图片本身。点击左右箭头时,通过改变 `index` 的值来切换图片,`showImg()` 函数用于切换图片的动画效果。自动播放时,通过 `setInterval` 来循环切换图片。同时,也实现了当鼠标移到轮播图上时停止自动播放,鼠标移开后继续自动播放的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webor2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值