Android开发中无处不在的设计模式——动态代理模式

继续更新设计模式系列,写这个模式的主要原因是最近看到了动态代理的代码。

先来回顾一下前5个模式:

- Android开发中无处不在的设计模式——单例模式

- Android开发中无处不在的设计模式——Builder模式

- Android开发中无处不在的设计模式——观察者模式

- Android开发中无处不在的设计模式——原型模式

- Android开发中无处不在的设计模式——策略模式

动态代理模式在Java WEB中的应用简直是随处可见,尤其在Spring框架中大量的用到了动态代理;算是最重要的一个设计模式,也是最难理解的设计模式之一。

那么什么叫动态代理呢

代理类在程序运行前不存在、运行时由程序动态生成的代理方式称为动态代理。

当前的网络请求库多种多样,其中Square公司的OkHttp简直是完美的一个网络请求库,而在其上又封装了一层的Retrofit库,为方便快捷的调用Restful Api提供了一种捷径。如果你用过Retrofit,一定不会忘记有会有这么一个过程:

  • 首先定义一个接口,接口中定义网络请求的具体方法,在方法上通过注解配置host,header,params等信息。
  • 然后新建一个Retrofit对象,通过该对象产生一个你定义的接口对象。
  • 通过接口对象调用具体的方法完成请求。

就像这样子:

> listRepos(@Path("user") String user);

}
" data-snippet-id="ext.fadc3883ecfd2cd1a1ca67e15e7b1971" data-snippet-saved="false" data-csrftoken="5VgX1Wh4-W-l4cPYta7C6PsnrxDn_HUux6Fk">
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);

}

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();

GitHubService service = retrofit.create(GitHubService.class);
> repos = service.listRepos("octocat");
" data-snippet-id="ext.ee6b799b6e05337f344653d3f6028237" data-snippet-saved="false" data-csrftoken="zatspdc2-6xr9Qf-RbSgSei_XQsqIqeqXvQQ">
Call<List<Repo>> repos = service.listRepos("octocat");

那么你有没有想过一个问题,接口是不可以直接new出来的,GitHubService接口的实例是如何产生的呢,retrofit.create方法内部到底做了什么呢。没错,答案就是动态代理。该对象是程序运行期生成的代理对象。

动态代理虽然在Java WEB中大量的用到,但是在客户端,由于考虑到性能的问题,所以用动态代理都会慎重考虑,但是,一旦动态代理用的好,就会产生不一样的效果,就比如这个Retrofit库。下面,我们实现一个Retrofit的最最简易的版本。过一下动态代理的原理。由于是简易版,所以很多东西和Retrofit还是有差距的,自然也没有Retrofit那么方便,这点无视就好了。我们就以实现上面那个例子为例:

首先说明一点,我们的请求是异步的,所以返回值我们使用void,增加一个回调的参数,约定最后一个参数是回调。

 {

    void onSuccess(Object t);

    void onFailed(Exception e);

}
" data-snippet-id="ext.a886f274abbdc51fa06b7d1abed39036" data-snippet-saved="false" data-csrftoken="notDIIJF-5EKuT6X6LWWipbRR2DHvKJcZYxM">

public interface Callback<T> {

    void onSuccess(Object t);

    void onFailed(Exception e);

}

最终的接口定义会是这个样子。

> callback);

    /**

     * 约定最后一个参数是callback

     */

}
" data-snippet-id="ext.f115dcec337f47ddaaf65522ab40a2e0" data-snippet-saved="false" data-csrftoken="hOCzSeOZ-2RNrlcsbRh9aW4trRlUk_l6C1D4">
public interface GithubService {

    @GET("users/{user}/repos")

    void listRepos(@Path("user") String user,Callback<List<Repo>> callback);

    /**

     * 约定最后一个参数是callback

     */

}

用到了两个注解,一个是方法注解,一个是参数注解


@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

public @interface GET {

    String value() default "";

}

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.PARAMETER)

public @interface Path {

    String value();

}

Repo实体类是使用GsonFormat根据json自动生成的。

然后我们编写Retrofit类,这个类应该是一个builder模式,里面可以设置baseUrl,姑且忽略其他所有参数。还有一个create方法,则原型如下:

public class Retrofit {

    private String baseUrl;
    private Retrofit(Builder builder) {
        this.baseUrl = builder.baseUrl;
    }

    public <T> T create(Class<T> clazz) {
        return null
    }

    static class Builder {
        private String baseUrl;
        Builder baseUrl(String host) {
            this.baseUrl = host;
            return this;
        }

        Retrofit build() {
            return new Retrofit(this);
        }
    }
}

最最关键的内容就是create方法的实现了。原理就是先拿到最后一个参数,也就是回调,再拿到方法上的注解,获得具体的值,然后拿到除了回调之外的其他参数,获得参数上的注解,然后根据注解取得对应的值,还有原来的参数值,将方法上的注解的值中进行替换。使用OkHttp构造请求,请求完成后根据将结果解析为回调中的类型。整个过程如下

public <T> T create(Class<T> clazz) {
        /**
         * 缓存中去
         */
        Object o = serviceMap.get(clazz);
        /**
         * 取不到则取构造代理对象
         */
        if (o == null) {
            o = (T) Proxy.newProxyInstance(Retrofit.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    final Callback<?> callback = (Callback<?>) args[args.length - 1];

                    final GET get = method.getAnnotation(GET.class);
                    if (get != null) {
                        /**
                         * 获得GET注解的值
                         */
                        String getValue = get.value();

                        System.out.println(getValue);

                        /**
                         * 获得所有参数上的注解
                         */
                        Annotation[][] methodParameterAnnotationArrays = method.getParameterAnnotations();

                        if (methodParameterAnnotationArrays != null) {
                            int count = methodParameterAnnotationArrays.length;
                            for (int i = 0; i < count; i++) {
                                /**
                                 * 获得单个参数上的注解
                                 */
                                Annotation[] methodParameterAnnotations = methodParameterAnnotationArrays[i];

                                if (methodParameterAnnotations != null) {
                                    for (Annotation methodParameterAnnotation : methodParameterAnnotations) {

                                        /**
                                         * 如果是Path注解
                                         */
                                        if (methodParameterAnnotation instanceof Path) {

                                            /**
                                             * 取得path注解上的值
                                             */
                                            Path path = (Path) methodParameterAnnotation;
                                            String pathValue = path.value();
                                            System.out.println(pathValue);

                                            /**
                                             * 这是对应的参数的值
                                             */
                                            System.out.println(args[i]);

                                            Request.Builder builder = new Request.Builder();

                                            /**
                                             * 使用path注解替换get注解中的值为参数值
                                             */
                                            String result = getValue.replaceAll("\\{" + pathValue + "\\}", (String) args[i]);

                                            System.out.println(result);

                                            /**
                                             * 开始构造请求
                                             */
                                            Request request = builder.get()
                                                    .url(baseUrl + "/" + result)
                                                    .build();

                                            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                                                @Override
                                                public void onFailure(Call call, IOException e) {
                                                    /**
                                                     * 失败则回调失败的方法
                                                     */
                                                    callback.onFailed(e);
                                                }

                                                @Override
                                                public void onResponse(Call call, Response response) throws IOException {
                                                    if (response.isSuccessful()) {
                                                        /**
                                                         * 请求成功
                                                         */
                                                        String body = response.body().string();

                                                        /**
                                                         * 使用fastjson进行zhuan转换
                                                         */
                                                        Type type = callback.getClass().getGenericInterfaces()[0];

                                                        Object o1 = JSON.parse(body);

                                                        /**
                                                         * 回调成功
                                                         */
                                                        callback.onSuccess(o1);
                                                    }
                                                }
                                            });

                                        }
                                    }
                                }

                            }
                        }
                    }

                    return null;
                }
            });
            /**
             * 扔到缓存中
             */
            serviceMap.put(clazz, o);
        }
        return (T) o;
    }

然后我们就可以根据Retrofit那样进行调用了

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com")
        .build();

GithubService githubService = retrofit.create(GithubService.class);

githubService.listRepos("lizhangqu", new Callback<List<Repo>>() {
    @Override
    public void onSuccess(Object t) {
        System.out.println(t);
    }
    @Override
    public void onFailed(Exception e) {
    }
});

这只是Retrofit中最简单的一个模块实现,如果对其他内容感兴趣,可以阅读retrofit的源码。

时间: 2024-12-22 05:26:59

Android开发中无处不在的设计模式——动态代理模式的相关文章

Android开发中无处不在的设计模式——原型模式

不知不觉这个系列已经写了三篇了,其实很早之前就想写设计模式了,只不过怕自己误人子弟没有提笔去写.后来在实际开发中,发现设计模式可以让一个开发人员融会贯通所学的知识,为了进一步巩固自己,就写下了这一些列文章.前面介绍了三个模式. Android开发中无处不在的设计模式--单例模式 Android开发中无处不在的设计模式--Builder模式 Android开发中无处不在的设计模式--观察者模式 本篇文章介绍的模式其实很简单,即原型模式,按照惯例,先看定义. 用原型实例指定创建对象的种类,并通过拷贝

Android开发中无处不在的设计模式——策略模式

这个系列停更了好久了,差不多可以重新拿起来更一篇了,这篇文章主要介绍策略模式.在这之前,先温习一下前面介绍的4种模式. 设计模式很重要! 设计模式很重要! 设计模式很重要! 重要的事说三遍!!! Android开发中无处不在的设计模式--单例模式 Android开发中无处不在的设计模式--Builder模式 Android开发中无处不在的设计模式--观察者模式 Android开发中无处不在的设计模式--原型模式 接着看下策略模式的定义 策略模式定义了一些列的算法,并将每一个算法封装起来,而且使它

Android开发中无处不在的设计模式——单例模式

对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次.而在android开发中,必要的了解一些设计模式又是非常有必要的.对于想系统的学习设计模式的同学,这里推荐2本书.一本是Head First系列的Head Hirst Design Pattern,英文好的可以看英文,可以多读几遍.另外一本是大话设计模式. 这篇文章介绍一个模式,就是单例模式,因为个人觉得这个模式理解起来最容易,而且不是太复杂. 首先了解一些什么是单例,从名字中就可以听出来

Android开发中的MVC设计模式

Android开发中的MVC设计模式的理解 1. Android系统中分层的理解: (1).在Android的软件开发工作中,应用程序的开发人员主要是应用Android Application Framework层封装好的Api进行快速开发. (2).在Android框架的四个层次中,下层为上层服务,上层需要下层的支持,上层需要调用下层的服务. (3).这种分层的方式带来极大的稳定性.灵活性和可扩展性,使得不同层的开发人员可以按照规范专心特定层的开发. (4). Android的官方建议应用程序

Android开发中常见的设计模式

对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次.而在android开发中,必要的了解一些设计模式又是非常有必要的.对于想系统的学习设计模式的同学,这里推荐2本书.一本是Head First系列的Head Hirst Design Pattern,英文好的可以看英文,可以多读几遍.另外一本是大话设计模式. 单例模式 首先了解一些单例模式的概念. 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 这样做有以下几个优点 对于

Android开发中常见的设计模式(一)——单例模式

首先了解一些单例模式的概念. 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 这样做有以下几个优点 对于那些比较耗内存的类,只实例化一次可以大大提高性能,尤其是在移动开发中. 保持程序运行的时候该中始终只有一个实例存在内存中 其实单例有很多种实现方式,但是个人比较倾向于其中1种.可以见单例模式 代码如下 public class Singleton { private static volatile Singleton instance = null; private Sin

设计模式 - 动态代理模式

package com.DynamicProxy3; /** * 动态代理: 抽象角色(公共接口) * * @author DW * */ public interface DoSomething { public void working(String work); } package com.DynamicProxy3; /** * 动态代理: 真实角色 * * @author DW * */ public class Man implements DoSomething { private

Android开发中常见的设计模式(三)——观察者模式

先看下这个模式的定义. 定义对象间的一种一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都能得到通知并被自动更新 先来讲几个情景. 情景1:有一种短信服务,比如天气预报服务,一旦你订阅该服务,你只需按月付费,付完费后,每天一旦有天气信息更新,它就会及时向你发送最新的天气信息. 情景2:杂志的订阅,你只需向邮局订阅杂志,缴纳一定的费用,当有新的杂志时,邮局会自动将杂志送至你预留的地址. 观察上面两个情景,有一个共同点,就是我们无需每时每刻关注我们感兴趣的东西,我们只需做的就是订阅感

Android开发中的MVP架构(转)

写在前面,本博客来源于公众号文章:http://mp.weixin.qq.com/s?__biz=MzA3MDMyMjkzNg==&mid=402435540&idx=1&sn=1cd10bd9efaac7083575367a8b4af52f&scene=1&srcid=0910ARzPpBvVYPI1NDBZnixa#wechat_redirect 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解MVP和DDD,但是我们的新项目