使用动态代理撸一个简易Retrofit

作者: fushuang | 来源:发表于2017-07-26 12:17 被阅读127次

扯淡

天哪噜,都上班半个月了,后台还没入职,闲的我简直五脊六兽的╮(╯▽╰)╭,看了一篇关于动态代理的文章,决定把之前写过的一个利用动态代理获取注解信息的Demo拿出来整理一下,缓解一波焦躁情绪...


haha.jpg

简介

原文链接
http://blog.csdn.net/qq_34306963/article/details/52557725

什么是反射

Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法,以及方法的各种参数,返回值,泛型

什么是动态代理

说来话长,个人理解就是在调用一个类或者接口中某个方法的时候,通过代理,动态干一些羞羞的事情,类似于用代码去实现一个方法
具体看这篇文章吧
http://www.jianshu.com/p/2f518a4a4c2b

我可要开始粘代码了

1.首先像Retrofit一样定义一个接口

public interface Apis {
    @UrlString("?id=%s&key=4d43e6b84de9561bb8fda801f2896a23")
    Call<HeathBean> getData(String s);
}

其中的Call这里用的是一个异步任务用来进行网络请求

/**
 * Created by fushuang on 2017/7/25.
 */

public class Call<T> extends AsyncTask<Call.CallBack<T>,Void,Object>{
    
    private  Class<T> tClass;  //用于接受之前 api接口的方法的返回值中的泛型
    public String url;
    private CallBack<T> callBack;

    public interface CallBack<S>{
        void onSuccess(S t);
        void onFail(Exception e);
    }


    public Call(String url, Class<T> tClass) {
        this.url = url;
        this.tClass = tClass;
    }

    @Override
    protected Object doInBackground(CallBack<T>... params) {
        callBack = params[0];
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.setDoInput(true);
            int responseCode = connection.getResponseCode();
            if (responseCode==200){
                InputStream inputStream = connection.getInputStream();
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                int len;
                byte[] bytes = new byte[102400];
                while ((len=inputStream.read(bytes))!=-1){
                    byteArrayOutputStream.write(bytes, 0, len);
                }
                String s = byteArrayOutputStream.toString("UTF-8");
                return new Gson().fromJson(s,tClass);
            }else {
                return new RuntimeException("网络错误,responseCode "+responseCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Object o) {
        if (tClass.isInstance(o)){
            callBack.onSuccess(((T) o));
        }else if (o instanceof Exception){
            callBack.onFail((Exception) o);
        }
    }
}

接口中的注解为自己定义的一个运行时注解,用来传入网址BaseUrl / 后面的某个接口的固定网址部分
Retrofit的参数是利用其它自定义注解代替的,这里我们就不做其它注解了,用String 的占位符%S 来代替,那么%S什么时候被真正的数据代替的呢?(比如id=5 其中的5),那就要用到本文的主角动态代理了。

public class Retrofit  {

    public String base;

    private Retrofit() {
    }

    //在将接口实例化的时候添加代理,动态实现接口
    public  <T> T create(Class<T> tClass){

        final Object o = Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new InvocationHandler() {
            /**
             *
             * @param proxy     代理
             * @param method    要代理的方法,当执行接口里面方法的时候,调用invoke方法,相当于用动态将接口进行了实现
             * @param args      接口里面方法传入的参数,在这里面用来替换注解中的占位符,比如%d
             * @return          接口实体对象
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                UrlString annotation = method.getAnnotation(UrlString.class);
                if (annotation != null) {
                    String url = String.format(Locale.CHINA, base + annotation.value(), args);
                    Class<?> returnType = method.getReturnType();
                    if (returnType.equals(Call.class)) {
                        ParameterizedType genericReturnType = (ParameterizedType) method.getGenericReturnType(); //带有泛型的返回值
                        Class type = (Class) genericReturnType.getActualTypeArguments()[0];  //获得返回值中的泛型
                        return new Call(url,type);
                    }
                }
                return null;
            }
        });

        return ((T) o);
    }
    public static class Builder{

        private Retrofit retrofit=new Retrofit();

        public Builder baseUrl(String baseUrl){
            retrofit.base=baseUrl;
            return this;
        }
        public Retrofit build(){
            return retrofit;
        }
    }
    
}

通过Proxy.newProxyInstance 将接口类装入内存,当调用到接口方法的时候会调用代理invoke()方法,这里需要自己实现InvocationHandler中的invoke方法,在invoke中获取方法的注解(也就是我们自定义的注解),获取到其中的地址, 和args(method参数会传到这里)中参数进行拼接,这时候就拼接好了一个完整的Url请求地址啦~~
最后invoke 的返回值一定是接口声明的返回值,创建出我们需要的网络请求工具就Ok啦~

大功告成

https://github.com/fushuangdage/MyRetrofit.git
贴上Github代码吃饭去啦~~

相关文章

网友评论

    本文标题:使用动态代理撸一个简易Retrofit

    本文链接:https://www.haomeiwen.com/subject/scmnkxtx.html