半天学会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数据返回给业务层,那么我们可以这样做:
- 自定义一个Convert.Factory,替换掉以上.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))代码中的GsonConverterFactory
- 我们还可以替换掉以上.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