半天学会Retrofit的简单使用(基于RxJava)

2018-06-12  本文已影响0人  reaiya

参考文章:https://www.jianshu.com/p/7b839b7c5884

1. 准备

安装依赖

    compile 'com.squareup.retrofit2:converter-gson:2.4.0'
    compile 'com.squareup.retrofit2:retrofit:2.4.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'

注意:最好装最新的版本,adapter才提供对RxJava2的支持

2. 使用

创建RetrofitService接口,申明搜索数据的方法,请求方式是GET,传递的参数有“q”、“tag”、“start”和“count”

public interface RetrofitService {

    // 根据条件搜索豆瓣书籍数据
    @GET("book/search")
    Observable<Book> getSearchBook(@Query("q") String name,@Query("tag") String tag,@Query("start") int start,@Query("count") int count);
    
}

创建实体类Book,安装GsonFormat插件,使用该插件根据搜索结果自动生成Book类

public class Book {

    /**
     * count : 1
     * start : 0
     * total : 577
     * books : [{"rating":{"max":10,"numRaters":4746,"average":"8.6","min":0},"subtitle":"张竹坡批评第一奇书",
     * "author":["兰陵笑笑生"],"pubdate":"1991","tags":[{"count":2414,"name":"金瓶梅","title":"金瓶梅"},{"count":1314,
     * "name":"古典文学","title":"古典文学"},{"count":988,"name":"兰陵笑笑生","title":"兰陵笑笑生"},{"count":856,"name":"小说",
     * "title":"小说"},{"count":772,"name":"中国古典文学","title":"中国古典文学"},{"count":521,"name":"中国文学","title":"中国文学"},
     * {"count":432,"name":"古典","title":"古典"},{"count":413,"name":"中国","title":"中国"}],"origin_title":"(明)兰陵笑笑生",
     * "image":"https://img1.doubanio.com/view/subject/m/public/s10069398.jpg","binding":"","translator":[],
     * "catalog":"\n      ","pages":"","images":{"small":"https://img1.doubanio.com/view/subject/s/public/s10069398
     * .jpg","large":"https://img1.doubanio.com/lpic/s10069398.jpg","medium":"https://img1.doubanio
     * .com/view/subject/m/public/s10069398.jpg"},"alt":"https://book.douban.com/subject/1456692/","id":"1456692",
     * "publisher":"齐鲁出版社","isbn10":"7533300815","isbn13":"9787533300814","title":"金瓶梅","url":"https://api.douban
     * .com/v2/book/1456692","alt_title":"(明)兰陵笑笑生","author_intro":"","summary":"本书由王汝梅与李昭恂、于凤树校点。",
     * "series":{"id":"4279","title":"明代四大奇书"},"price":"268.00元"}]
     */

    private int count;
    private int start;
    private int total;
    private List<BooksBean> books;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    public List<BooksBean> getBooks() {
        return books;
    }

    public void setBooks(List<BooksBean> books) {
        this.books = books;
    }

    public String toString(){
        return "搜索结果序列化";
    }

    public static class BooksBean {
        /**
         * rating : {"max":10,"numRaters":4746,"average":"8.6","min":0}
         * subtitle : 张竹坡批评第一奇书
         * author : ["兰陵笑笑生"]
         * pubdate : 1991
         * tags : [{"count":2414,"name":"金瓶梅","title":"金瓶梅"},{"count":1314,"name":"古典文学","title":"古典文学"},
         * {"count":988,"name":"兰陵笑笑生","title":"兰陵笑笑生"},{"count":856,"name":"小说","title":"小说"},{"count":772,
         * "name":"中国古典文学","title":"中国古典文学"},{"count":521,"name":"中国文学","title":"中国文学"},{"count":432,"name":"古典",
         * "title":"古典"},{"count":413,"name":"中国","title":"中国"}]
         * origin_title : (明)兰陵笑笑生
         * image : https://img1.doubanio.com/view/subject/m/public/s10069398.jpg
         * binding :
         * translator : []
         * catalog :

         * pages :
         * images : {"small":"https://img1.doubanio.com/view/subject/s/public/s10069398.jpg","large":"https://img1
         * .doubanio.com/lpic/s10069398.jpg","medium":"https://img1.doubanio.com/view/subject/m/public/s10069398.jpg"}
         * alt : https://book.douban.com/subject/1456692/
         * id : 1456692
         * publisher : 齐鲁出版社
         * isbn10 : 7533300815
         * isbn13 : 9787533300814
         * title : 金瓶梅
         * url : https://api.douban.com/v2/book/1456692
         * alt_title : (明)兰陵笑笑生
         * author_intro :
         * summary : 本书由王汝梅与李昭恂、于凤树校点。
         * series : {"id":"4279","title":"明代四大奇书"}
         * price : 268.00元
         */

        private RatingBean rating;
        private String subtitle;
        private String pubdate;
        private String origin_title;
        private String image;
        private String binding;
        private String catalog;
        private String pages;
        private ImagesBean images;
        private String alt;
        private String id;
        private String publisher;
        private String isbn10;
        private String isbn13;
        private String title;
        private String url;
        private String alt_title;
        private String author_intro;
        private String summary;
        private SeriesBean series;
        private String price;
        private List<String> author;
        private List<TagsBean> tags;
        private List<?> translator;

        public RatingBean getRating() {
            return rating;
        }

        public void setRating(RatingBean rating) {
            this.rating = rating;
        }

        public String getSubtitle() {
            return subtitle;
        }

        public void setSubtitle(String subtitle) {
            this.subtitle = subtitle;
        }

        public String getPubdate() {
            return pubdate;
        }

        public void setPubdate(String pubdate) {
            this.pubdate = pubdate;
        }

        public String getOrigin_title() {
            return origin_title;
        }

        public void setOrigin_title(String origin_title) {
            this.origin_title = origin_title;
        }

        public String getImage() {
            return image;
        }

        public void setImage(String image) {
            this.image = image;
        }

        public String getBinding() {
            return binding;
        }

        public void setBinding(String binding) {
            this.binding = binding;
        }

        public String getCatalog() {
            return catalog;
        }

        public void setCatalog(String catalog) {
            this.catalog = catalog;
        }

        public String getPages() {
            return pages;
        }

        public void setPages(String pages) {
            this.pages = pages;
        }

        public ImagesBean getImages() {
            return images;
        }

        public void setImages(ImagesBean images) {
            this.images = images;
        }

        public String getAlt() {
            return alt;
        }

        public void setAlt(String alt) {
            this.alt = alt;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getPublisher() {
            return publisher;
        }

        public void setPublisher(String publisher) {
            this.publisher = publisher;
        }

        public String getIsbn10() {
            return isbn10;
        }

        public void setIsbn10(String isbn10) {
            this.isbn10 = isbn10;
        }

        public String getIsbn13() {
            return isbn13;
        }

        public void setIsbn13(String isbn13) {
            this.isbn13 = isbn13;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getAlt_title() {
            return alt_title;
        }

        public void setAlt_title(String alt_title) {
            this.alt_title = alt_title;
        }

        public String getAuthor_intro() {
            return author_intro;
        }

        public void setAuthor_intro(String author_intro) {
            this.author_intro = author_intro;
        }

        public String getSummary() {
            return summary;
        }

        public void setSummary(String summary) {
            this.summary = summary;
        }

        public SeriesBean getSeries() {
            return series;
        }

        public void setSeries(SeriesBean series) {
            this.series = series;
        }

        public String getPrice() {
            return price;
        }

        public void setPrice(String price) {
            this.price = price;
        }

        public List<String> getAuthor() {
            return author;
        }

        public void setAuthor(List<String> author) {
            this.author = author;
        }

        public List<TagsBean> getTags() {
            return tags;
        }

        public void setTags(List<TagsBean> tags) {
            this.tags = tags;
        }

        public List<?> getTranslator() {
            return translator;
        }

        public void setTranslator(List<?> translator) {
            this.translator = translator;
        }

        public static class RatingBean {
            /**
             * max : 10
             * numRaters : 4746
             * average : 8.6
             * min : 0
             */

            private int max;
            private int numRaters;
            private String average;
            private int min;

            public int getMax() {
                return max;
            }

            public void setMax(int max) {
                this.max = max;
            }

            public int getNumRaters() {
                return numRaters;
            }

            public void setNumRaters(int numRaters) {
                this.numRaters = numRaters;
            }

            public String getAverage() {
                return average;
            }

            public void setAverage(String average) {
                this.average = average;
            }

            public int getMin() {
                return min;
            }

            public void setMin(int min) {
                this.min = min;
            }
        }

        public static class ImagesBean {
            /**
             * small : https://img1.doubanio.com/view/subject/s/public/s10069398.jpg
             * large : https://img1.doubanio.com/lpic/s10069398.jpg
             * medium : https://img1.doubanio.com/view/subject/m/public/s10069398.jpg
             */

            private String small;
            private String large;
            private String medium;

            public String getSmall() {
                return small;
            }

            public void setSmall(String small) {
                this.small = small;
            }

            public String getLarge() {
                return large;
            }

            public void setLarge(String large) {
                this.large = large;
            }

            public String getMedium() {
                return medium;
            }

            public void setMedium(String medium) {
                this.medium = medium;
            }
        }

        public static class SeriesBean {
            /**
             * id : 4279
             * title : 明代四大奇书
             */

            private String id;
            private String title;

            public String getId() {
                return id;
            }

            public void setId(String id) {
                this.id = id;
            }

            public String getTitle() {
                return title;
            }

            public void setTitle(String title) {
                this.title = title;
            }
        }

        public static class TagsBean {
            /**
             * count : 2414
             * name : 金瓶梅
             * title : 金瓶梅
             */

            private int count;
            private String name;
            private String title;

            public int getCount() {
                return count;
            }

            public void setCount(int count) {
                this.count = count;
            }

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public String getTitle() {
                return title;
            }

            public void setTitle(String title) {
                this.title = title;
            }
        }
    }
}

创建service

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.douban.com/v2/")
                .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支持RxJava
                .build();
        service = retrofit.create(RetrofitService.class);

绑定观察者和被观察者

                Observable<Book> observable = service.getSearchBook(main2Edit1.getText().toString(), null, 0, 1)
                        .filter(new Predicate<Book>() {
                            @Override
                            public boolean test(@NonNull Book book) throws Exception {
                                return book.getCount()>0;
                            }
                        })
                        .debounce(2, TimeUnit.SECONDS);
                observable.observeOn(AndroidSchedulers.mainThread())
                        .doOnNext(new Consumer<Book>() {
                            @Override
                            public void accept(Book b) throws Exception {
                                showProgress();
                            }
                        })
                        .subscribeOn(Schedulers.io())//请求数据的事件发生在io线程
                        .observeOn(AndroidSchedulers.mainThread())//请求完成后在主线程更显UI
                        .subscribe(new Consumer<Book>() {
                            @Override
                            public void accept(Book book) throws Exception {
                                hideProgress();
                                showResult(book);
                            }
                        });

写于2018-4-7

自定义拦截器,绑定公共参数

public final class CommonParamsInterceptor implements Interceptor {

    private RetrofitClient retrofitClient;
    Map<String, String> mNetParas;

    public CommonParamsInterceptor(RetrofitClient retrofitClient, Map<String, String> mNetParas) {
        this.retrofitClient = retrofitClient;
        this.mNetParas = mNetParas;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
            request = processToBody(request);
//        request = processToHeader(request);
        return chain.proceed(request);
    }

    private Request processToHeader(Request request) throws IOException {
        Request.Builder headerBuilder = request.newBuilder();
        Map<String, String> cloneHeaders = new HashMap<>(mNetParas);
        cloneHeaders.put(NetConfig.HEAD_TIMESTAMP, String.valueOf(TimeUtils.timesToServer(System.currentTimeMillis())));
        try {
            Map<String, String> headFinal = HeaderHMacSha256.toHMacSha256(cloneHeaders);
            for (Map.Entry<String, String> entry : headFinal.entrySet()) {
                String value = entry.getValue();
                if (value != null) {
                    headerBuilder.addHeader(entry.getKey(), entry.getValue());
                }
            }
            return headerBuilder.build();
        } catch (Exception e) {
            e.printStackTrace();
            throw new IOException("make header fail-->" + e.getMessage());
        }
    }

    private Request processToBody(Request request) {
        String method = request.method();
        if (method != null) {
            method = method.toLowerCase();
            if (method.contains("get")) {
                request = processUrl(request);
            } else if (method.contains("post")
                    && request.body() != null
                    ) {
                RequestBody requestBody = request.body();
                if (requestBody != null) {
                    String subType = requestBody.contentType() != null ? requestBody.contentType().subtype() : null;
                    if (subType == null) {
                        requestBody = processFormDataRequestBody(requestBody);
                    } else {
                        boolean formPost = isFormPost(subType);
                        boolean jsonPost = isJsonPost(subType);
                        if (formPost) {
                            requestBody = processFormDataRequestBody(requestBody);
                        } else if (jsonPost) {
                            requestBody = processApplicationJsonRequestBody(requestBody);
                        }
                    }

                    if (requestBody != null) {
                        Request.Builder requestBuilder = request.newBuilder();
                        request = requestBuilder
                                .post(requestBody)
                                .build();
                    }
                }

            }
        }
        return request;
    }

    private Request processUrl(Request request) {
        HttpUrl.Builder urlBuilder = request.url().newBuilder();
        for (Map.Entry<String, String> entry : mNetParas.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            urlBuilder.addQueryParameter(key, value);
        }
        request = request.newBuilder().url(urlBuilder.build()).build();
        return request;
    }


    private RequestBody processFormDataRequestBody(RequestBody requestBody) {
        FormBody.Builder formBuilder = new FormBody.Builder();
        for (Map.Entry<String, String> entry : mNetParas.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            formBuilder.add(key, value);
        }
        RequestBody formBody = formBuilder.build();
        String postBodyString = bodyToString(requestBody);
        postBodyString += ((postBodyString.length() > 0) ? "&" : "") + bodyToString(formBody);
        MediaType contentType = requestBody.contentType();
        if (contentType == null) {
            contentType = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
        }
        return RequestBody.create(contentType, postBodyString);
    }

    private RequestBody processApplicationJsonRequestBody(RequestBody requestBody) {
        String customReq = bodyToString(requestBody);
        try {
            JSONObject obj = new JSONObject(customReq);
            for (Map.Entry<String, String> entry : mNetParas.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                obj.put(key, value);
            }
            return RequestBody.create(requestBody.contentType(), obj.toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String bodyToString(final RequestBody request) {
        try {
            final RequestBody copy = request;
            final Buffer buffer = new Buffer();
            if (copy != null)
                copy.writeTo(buffer);
            else
                return "";
            return buffer.readUtf8();
        } catch (final IOException e) {
            return "did not work";
        }
    }

    private boolean isJsonPost(String subType) {
        return subType.contains("json");
    }

    private boolean isFormPost(String subType) {
        return subType.contains("form");
    }
}

同样,还可以在拦截器里做打印日志、统一错误处理等操作。

更新于2018-6-11

我们经常会有统一处理返回结果的需求,比如我们接口的返回结果的格式是这样的:

code:200,
message:"success",
data:{}

我们需要在返回结果前处理code,确认结果正常后再将data数据返回给业务层,那么我们可以这样做:

  1. 自定义一个Convert.Factory,替换掉以上.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))代码中的GsonConverterFactory
  2. 我们还可以替换掉以上.addCallAdapterFactory(RxJava2CallAdapterFactory.create())代码中的RxJava2CallAdapterFactory,实现RxJava并且加上我们的处理逻辑

自定义的BodyConverterFactory:

public final class BodyConverterFactory extends Converter.Factory {
    /**
     * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
     * decoding from JSON (when no charset is specified by a header) will use UTF-8.
     *
     * @param deviceSession
     */
    public static BodyConverterFactory create(Map<String, String> deviceSession, Gson gson) {

        return create(gson, deviceSession);
    }

    /**
     * Create an instance using {@code gson} for conversion. Encoding to JSON and
     * decoding from JSON (when no charset is specified by a header) will use UTF-8.
     */
    @SuppressWarnings("ConstantConditions") // Guarding public API nullability.
    public static MobooConverterFactory create(Gson gson, Map<String, String> deviceSession) {
        if (gson == null) throw new NullPointerException("gson == null");
        return new MobooConverterFactory(gson, deviceSession);
    }

    private final Gson gson;
    
    private BodyConverterFactory(Gson gson, Map<String, String> deviceSession) {
        this.gson = gson;
    }
    
    @Override
    @SuppressWarnings("unchecked")//忽略转换错误
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        for (Annotation annotation : methodAnnotations) {
            if (annotation instanceof WithoutRequestConverter) {
                return new GsonRequestWithoutConverter(this.gson, adapter);
            }
        }
        TypeAdapter adapter = this.gson.getAdapter(TypeToken.get(type));
        return new GsonRequestBodyConverter(this.gson, adapter);
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        for (Annotation annotation : annotations) {
            if (annotation instanceof WithoutResponseConverter) {
                return new GsonResponseWithoutConverter<>(gson, adapter);
            } 
        }
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonResponseBodyConverter<>(gson, adapter);
    }
}
GsonRequestBodyConverter GsonResponseBodyConverter

可以看到,在对RequestBody和ResponseBody转换之前,我们先判断了在方法注解里面有没有without注解,这两个注解是我们自定义的注解,里面没有任何逻辑,只需要在使用时加上就可以了,加上这两个注解后表示我们不需要对RequestBody和ResponseBody进行转换,常用于非系统内接口调用

更新于2018-7-5

上一篇下一篇

猜你喜欢

热点阅读