网络框架-Retrofit(三)

简单实现Retrofit(替代Okhttp)

1.定义注解参数

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {
    String value();
}
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface Get {
    String value() default "";
}
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface Post {

    String value() default "";
}

2.实现回调接口Call和Callback
MyCall .class

public interface MyCall {

     //执行网络请求(同步:在当前网络)
     String execute() throws Exception;

    //在子线程请求网络
    void enqueue(MyCallback callback);
}

MyCallback .class

/**
 * 网络请求回调接口
 */
public interface MyCallback {
  void onResponse( String response);

  void onFailure( Exception e);
}

SystemHttpCall .class

/**
 * Created by Xionghu on 2017/8/14.
 * Desc: 具体的实现类发送请求
 */

public class SystemHttpCall implements MyCall {
    private MyServiceMethod serviceMethod;

    public SystemHttpCall(MyServiceMethod serviceMethod) {
        this.serviceMethod = serviceMethod;
    }

    @Override
    public String execute() throws Exception {
        if (serviceMethod.isPost()) {

            return HttpUtils.post(serviceMethod.baseUrl(), serviceMethod.getParam());
        } else {
            return HttpUtils.get(serviceMethod.baseUrl());
        }
    }

    @Override
    public void enqueue(MyCallback callback) {
        HttpTask httpTask = new HttpTask(this, callback);
        httpTask.execute();
    }

    public class HttpTask extends AsyncTask<Void, Void, String> {

        private SystemHttpCall httpCall;
        private MyCallback callback;

        private HttpTask(SystemHttpCall httpCall, MyCallback callback) {
            this.httpCall = httpCall;
            this.callback = callback;
        }

        @Override
        protected String doInBackground(Void... params) {
            try {
                return httpCall.execute();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String result) {
            if (this.callback != null) {
                if (result != null) {
                    this.callback.onResponse(result);
                } else {
                    this.callback.onFailure(new Exception("网络异常!"));
                }
            }


        }
    }
}

3.解析注解方法获取相应参数

public class MyServiceMethod {
    private Builder builder;

    public MyServiceMethod(Builder builder) {
        this.builder = builder;
    }

    public boolean isPost() {
        return this.builder.isPost;
    }

    public Map<String, Object> getParam() {
        return this.builder.paramMap;
    }

    public String baseUrl() {
        StringBuffer buffer = new StringBuffer(
                this.builder.retrofit.getBaseUrl() + this.builder.relativeUrl);
        if (!isPost()) {
            // 如果你不是Post请求,是get请求,需要拼接参数地址
            if (this.builder.paramMap != null) {
                Set<String> keySet = this.builder.paramMap.keySet();
                if (keySet.size() > 0) {
                    buffer.append("?");
                }
                for (String key : keySet) {
                    Object value = this.builder.paramMap.get(key);
                    buffer.append(key);
                    buffer.append("=");
                    buffer.append(value);
                    buffer.append("&");
                }
                buffer.deleteCharAt(buffer.length() - 1);
            }
        }
        return buffer.toString();
    }

    static final class Builder {
        final MyRetrofit retrofit;
        final Method method;
        final Annotation[] methodAnnotations;
        final Annotation[][] parameterAnnotationsArray;

        String relativeUrl;
        // 参数集合
        Map<String, Object> paramMap;
        Object[] args;

        boolean isPost;

        public Builder(MyRetrofit retrofit, Method method, Object[] args) {
            this.retrofit = retrofit;
            this.method = method;
            // 方法注解列表(相当于我们的LoginService中的: @POST和@FormUrlEncoded......)
            this.methodAnnotations = method.getAnnotations();
            // 方法参数注解列表(相当于我们的LoginService中的: @Field......)
            this.parameterAnnotationsArray = method.getParameterAnnotations();
            this.args = args;

            this.paramMap = new HashMap<String, Object>();
        }

        public MyServiceMethod build() {
            // 循环遍历方法注解列表
            for (Annotation annotation : methodAnnotations) {
                parseMethodAnnotation(annotation);
            }

            int parameterCount = parameterAnnotationsArray.length;
            for (int p = 0; p < parameterCount; p++) {
                Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
                parseParameterAnnotation(p, parameterAnnotations);
            }

            return new MyServiceMethod(this);
        }

        /**
         * 解析方法注解
         *
         * @param annotation
         */
        public void parseMethodAnnotation(Annotation annotation) {
            // 首先判断注解类型(解析目的:获取接口名称,用于拼接地址)
            if (annotation instanceof Get) {
                this.relativeUrl = ((Get) annotation).value();
                isPost = false;
            } else if (annotation instanceof Post) {
                this.relativeUrl = ((Post) annotation).value();
                isPost = true;
            }
        }

        /**
         * 解析参数注解
         *
         * @param p
         * @param parameterAnnotations
         */
        private void parseParameterAnnotation(int p,
                                              Annotation[] parameterAnnotations) {
            // 方法参数值
            Object value = args[p];
            // 遍历方法参数注解
            for (Annotation annotation : parameterAnnotations) {
                // 首先需要判断该注解的类型
                if (annotation instanceof Field) {
                    Field field = (Field) annotation;
                    // 参数的名称(接口参数名称,服务器接口规定的)
                    String key = field.value();
                    paramMap.put(key, value);
                }
            }
        }

    }
}

4.定义MyRetrofit框架

public class MyRetrofit {
    // 缓存方法(为了避免重复加载方法注解,从而耗费性能)
    private final Map<Method, MyServiceMethod> serviceMethodCache = new LinkedHashMap<Method, MyServiceMethod>();
    public String getBaseUrl() {
        return baseUrl;
    }

    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    private String baseUrl;

   private MyRetrofit(String baseUrl){
       this.baseUrl = baseUrl;
   }

   public <T> T create(Class<T> service){
       //动态代理实现
       return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() {
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               MyServiceMethod serviceMethod = loadServiceMethod(method, args);
               SystemHttpCall httpCall = new SystemHttpCall(serviceMethod);
               return httpCall;
           }
       });

   }
    // 第一步:解析方法(说白了解析方法注解和方法参数注解)

    // 第二步:实现网络请求

    protected MyServiceMethod loadServiceMethod(Method method, Object[] args) {
        MyServiceMethod result;
        synchronized (serviceMethodCache) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                result = new MyServiceMethod.Builder(this, method, args)
                        .build();
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }

   public static final class Builder{
       private String baseUrl;

       public Builder baseUrl(String baseUrl){
           this.baseUrl = baseUrl;
           return this;
       }

       public MyRetrofit build(){
           if(baseUrl == null){
                throw new IllegalStateException("Base URL required.");
           }
           return new MyRetrofit(baseUrl);
       }
   }

}

5.具体实现网络请求类
定义接口方法类

public interface MyRetrofitLoginService {
@Post("user/login?platform=android&city_id=101&type=pwd&channel=baiduxi&version=3.2.0&os_version=6.0.1&device_id=866622020797175")
    MyCall login(@Field("mobile") String name, @Field("password") String pwd);
}

简单封装请求工具类

public class MyRetrofitTest {
    private static String URL_SERVER = "http://api.cloud.test.haocaisong.cn/v2.0/";
    public static void login(String name, String pwd, final SimpleSystemLogin.OnHttpResultListener onHttpResultListener) {
        MyRetrofit myRetrofit = new MyRetrofit.Builder().baseUrl(URL_SERVER).build();
        MyRetrofitLoginService loginService = myRetrofit.create(MyRetrofitLoginService.class);
        MyCall myCall = loginService.login(name,pwd);
        myCall.enqueue(new MyCallback() {
            @Override
            public void onResponse(String response) {
                onHttpResultListener.onHttpResult(response);
            }

            @Override
            public void onFailure(Exception e) {

            }
        });
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容