android retrofit jar,Android 手把手教你使用Retrofit2

Android 手把手教你使用Retrofit2

本文原创,转载请注明出处。欢迎关注我的 简书。

安利一波我写的开发框架:MyScFrame喜欢的话就给个Star

前言:

1.我想通过本文,让大家能更快的上手Retrofit2,跟上时代的步伐。

2.本文主要是写给初级、中级开发人员阅读,所以在这边不会做太多的解释说明,只针对一些坑以及使用方法进行说明。

3.本文只涉及到Retrofit2,并未与RxJava一起使用,RxJava+Retrofit2技术将会在下篇文章中呈现。

4.本人也是刚开始接触Retrofit2,所以可能文章中会有一些错误或者不足,希望大家多多留言探讨,共同进步

介绍个框架给大家:

开启Retrofit2之旅

添加依赖

以下是我用到的一些依赖

compile 'com.squareup.retrofit2:retrofit:2.1.0'

compile 'com.squareup.retrofit2:converter-gson:2.1.0'

compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

//日志拦截器

compile 'com.squareup.okhttp3:logging-interceptor:3.5.0'

compile 'io.reactivex:rxjava:1.2.4'

compile 'io.reactivex:rxandroid:1.2.1'

compile 'org.ligboy.retrofit2:converter-fastjson-android:2.1.0'

注解

retrofit通过使用注解来简化请求,大体分为以下几类:

1.用于标注请求方式的注解

2.用于标记请求头的注解

3.用于标记请求参数的注解

4.用于标记请求和响应格式的注解

请求方法注解

注解

说明

@GET

get请求

@POST

post请求

@PUT

put请求

@DELETE

delete请求

@PATCH

patch请求,该请求是对put请求的补充,用于更新局部资源

@HEAD

head请求

@OPTIONS

option请求

@HTTP

通用注解,可以替换以上所有的注解,其拥有三个属性:method,path,hasBody

请求头注解

注解

说明

@Headers

用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在

@Header

作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头

请求参数注解

名称

说明

@Body

多用于post请求发送非表单数据,比如想要以post方式传递json格式数据

@Filed

多用于post请求中表单字段,Filed和FieldMap需要FormUrlEncoded结合使用

@FiledMap

和@Filed作用一致,用于不确定表单参数

@Part

用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况

@PartMap

用于表单字段,默认接受的类型是Map,可用于实现多文件上传

@Path

用于url中的占位符

@Query

用于Get中指定参数

@QueryMap

和Query使用类似

@Url

指定请求路径

请求和响应格式注解

名称

说明

@FormUrlEncoded

表示请求发送编码表单数据,每个键值对需要使用@Field注解

@Multipart

表示请求发送multipart数据,需要配合使用@Part

@Streaming

表示响应用字节流的形式返回.如果没使用该注解,默认会把数据全部载入到内存中.该注解在在下载大文件的特别有用

使用

这里不对注解进行过多的说明了,只展示几个用法

/**

* Created by caihan on 2017/1/11.

*

* @GET 表明这是get请求

* @POST 表明这是post请求

* @PUT 表明这是put请求

* @DELETE 表明这是delete请求

* @PATCH 表明这是一个patch请求,该请求是对put请求的补充,用于更新局部资源

* @HEAD 表明这是一个head请求

* @OPTIONS 表明这是一个option请求

* @HTTP 通用注解, 可以替换以上所有的注解,其拥有三个属性:method,path,hasBody

* @Headers 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在

* @Header 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头

* @Body 多用于post请求发送非表单数据, 比如想要以post方式传递json格式数据

* @Filed 多用于post请求中表单字段, Filed和FieldMap需要FormUrlEncoded结合使用

* @FiledMap 和@Filed作用一致,用于不确定表单参数

* @Part 用于表单字段, Part和PartMap与Multipart注解结合使用, 适合文件上传的情况

* @PartMap 用于表单字段, 默认接受的类型是Map,可用于实现多文件上传

*

* Part标志上文的内容可以是富媒体形势,比如上传一张图片,上传一段音乐,即它多用于字节流传输。

* 而Filed则相对简单些,通常是字符串键值对。

*

* Part标志上文的内容可以是富媒体形势,比如上传一张图片,上传一段音乐,即它多用于字节流传输。

* 而Filed则相对简单些,通常是字符串键值对。

* @Path 用于url中的占位符,{占位符}和PATH只用在URL的path部分,url中的参数使用Query和QueryMap代替,保证接口定义的简洁

* @Query 用于Get中指定参数

* @QueryMap 和Query使用类似

* @Url 指定请求路径

*/

public interface MyRetrofit2Service {

//使用@Headers添加多个请求头

@Headers({

"User-Agent:android",

"apikey:123456789",

})

@POST()

Call> post(@Url String url, @QueryMap Map map);

@GET("mobile/active")

Call> get(@Header("token") String token, @Query("id") int activeId);

@GET("active/list")

Call> ActiveList();

@POST("api/news/newsList")

Call> post2(@QueryMap Map map);

/**

* 很多情况下,我们需要上传json格式的数据。比如当我们注册新用户的时候,因为用户注册时的数据相对较多,

* 并可能以后会变化,这时候,服务端可能要求我们上传json格式的数据。此时就要@Body注解来实现。

* 直接传入实体,它会自行转化成Json

*

* @param url

* @param post

* @return

*/

@POST("api/{url}/newsList")

Call> login(@Path("url") String url, @Body News post);

/**

* 单张图片上传

* retrofit 2.0的上传和以前略有不同,需要借助@Multipart注解、@Part和MultipartBody实现。

*

* @param url

* @param file

* @return

*/

@Multipart

@POST("{url}")

Call> upload(@Path("url") String url, @Part MultipartBody.Part file);

/**

* 多张图片上传

*

* @param map

* @return

*/

@Multipart

@POST("upload/upload")

Call> upload(@PartMap Map map);

/**

* 图文混传

*

* @param post

* @param map

* @return

*/

@Multipart

@POST("")

Call> register(@Body News post, @PartMap Map map);

/**

* 文件下载

*

* @param fileUrl

* @return

*/

@Streaming

@GET

Call> downloadPicture(@Url String fileUrl);

/**

* 这里需要注意的是如果下载的文件较大,比如在10m以上,那么强烈建议你使用@Streaming进行注解,否则将会出现IO异常.

*

* @param fileUrl

* @return

*/

@Streaming

@GET

Observable> downloadPicture2(@Url String fileUrl);

@POST()

@FormUrlEncoded

Observable> executePost(@FieldMap Map maps);

}

踩坑

1.url被转义

http://api.mydemo.com/api%2Fnews%2FnewsList?

罪魁祸首@Url与@Path注解,我们开发过程中,肯定会需要动态的修改请求地址

两种动态修改方式如下:

@POST()

Call> post(@Url String url, @QueryMap Map map);

@POST("api/{url}/newsList")

Call> login(@Path("url") String url, @Body News post);

第一种是直接使用@Url,它相当于直接替换了@POST()里面的请求地址

第二种是使用@Path("url"),它只替换了@POST("api/{url}/newsList")中的{url}

如果你用下面这样写的话,就会出现url被转义

@POST("{url}")

Call> post(@Path("url") String url);

你如果执意要用@Path,也不是不可以,需要这样写

@POST("{url}")

Call> post(@Path(value = "url", encoded = true) String url);

OkHttpClient

拦截器

addNetworkInterceptor添加的是网络拦截器Network Interfacetor它会在request和response时分别被调用一次;

addInterceptor添加的是应用拦截器Application Interceptor他只会在response被调用一次。

1.日志拦截器

使用addNetworkInterceptor()方法添加到OkHttpClient中

日志拦截器我这有两种创建方式:

一种是使用HttpLoggingInterceptor,需要使用到依赖

compile 'com.squareup.okhttp3:logging-interceptor:3.5.0'

/**

* 创建日志拦截器

*

* @return

*/

public static HttpLoggingInterceptor getHttpLoggingInterceptor() {

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(

new HttpLoggingInterceptor.Logger() {

@Override

public void log(String message) {

Log.e("OkHttp", "log = " + message);

}

});

loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

return loggingInterceptor;

}

另一种是

/**

* 创建日志拦截器

*

* @return

*/

private class LogInterceptor implements Interceptor {

@Override

public okhttp3.Response intercept(Chain chain) throws IOException {

Request request = chain.request();

long t1 = System.nanoTime();

Log.d("OkHttp", "HttpHelper1" + String.format("Sending request %s on %s%n%s",

request.url(), chain.connection(), request.headers()));

okhttp3.Response response = chain.proceed(request);

long t2 = System.nanoTime();

Log.d("OkHttp", "HttpHelper2" + String.format("Received response for %s in %.1fms%n%s",

response.request().url(), (t2 - t1) / 1e6d, response.headers()));

return response;

}

}

2.请求头拦截器

使用addInterceptor()方法添加到OkHttpClient中

我的理解是,请求头拦截器是为了让服务端能更好的识别该请求,服务器那边通过请求头判断该请求是否为有效请求等...

/**

* 拦截器Interceptors

* 使用addHeader()不会覆盖之前设置的header,若使用header()则会覆盖之前的header

*

* @return

*/

public static Interceptor getRequestHeader() {

Interceptor headerInterceptor = new Interceptor() {

@Override

public okhttp3.Response intercept(Chain chain) throws IOException {

Request originalRequest = chain.request();

Request.Builder builder = originalRequest.newBuilder();

builder.addHeader("version", "1");

builder.addHeader("time", System.currentTimeMillis() + "");

Request.Builder requestBuilder = builder.method(originalRequest.method(), originalRequest.body());

Request request = requestBuilder.build();

return chain.proceed(request);

}

};

return headerInterceptor;

}

3.统一请求拦截器

使用addInterceptor()方法添加到OkHttpClient中

统一请求拦截器的功能跟请求头拦截器相类似

/**

* 拦截器Interceptors

* 统一的请求参数

*/

private Interceptor commonParamsInterceptor() {

Interceptor commonParams = new Interceptor() {

@Override

public okhttp3.Response intercept(Chain chain) throws IOException {

Request originRequest = chain.request();

Request request;

HttpUrl httpUrl = originRequest.url().newBuilder().

addQueryParameter("paltform", "android").

addQueryParameter("version", "1.0.0").build();

request = originRequest.newBuilder().url(httpUrl).build();

return chain.proceed(request);

}

};

return commonParams;

}

4.时间拦截器

使用addInterceptor()方法添加到OkHttpClient中

从响应中获取服务器返回的时间

/**

* 拦截器Interceptors

* 通过响应拦截器实现了从响应中获取服务器返回的time

*

* @return

*/

public static Interceptor getResponseHeader() {

Interceptor interceptor = new Interceptor() {

@Override

public okhttp3.Response intercept(Chain chain) throws IOException {

okhttp3.Response response = chain.proceed(chain.request());

String timestamp = response.header("time");

if (timestamp != null) {

//获取到响应header中的time

}

return response;

}

};

return interceptor;

}

缓存

使用okhttp缓存的话,先要创建Cache,然后在创建缓存拦截器

//创建Cache

File httpCacheDirectory = new File(MyApp.getContext().getCacheDir(), "OkHttpCache");

Cache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);

httpClientBuilder.cache(cache);

//设置缓存

httpClientBuilder.addNetworkInterceptor(getCacheInterceptor2());

httpClientBuilder.addInterceptor(getCacheInterceptor2());

缓存拦截器

缓存时间自己根据情况设定

/**

* 在无网络的情况下读取缓存,有网络的情况下根据缓存的过期时间重新请求,

*

* @return

*/

public Interceptor getCacheInterceptor2() {

Interceptor commonParams = new Interceptor() {

@Override

public okhttp3.Response intercept(Chain chain) throws IOException {

Request request = chain.request();

if (!NetworkUtils.isConnected()) {

//无网络下强制使用缓存,无论缓存是否过期,此时该请求实际上不会被发送出去。

request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE)

.build();

}

okhttp3.Response response = chain.proceed(request);

if (NetworkUtils.isConnected()) {//有网络情况下,根据请求接口的设置,配置缓存。

//这样在下次请求时,根据缓存决定是否真正发出请求。

String cacheControl = request.cacheControl().toString();

//当然如果你想在有网络的情况下都直接走网络,那么只需要

//将其超时时间这是为0即可:String cacheControl="Cache-Control:public,max-age=0"

int maxAge = 60 * 60; // read from cache for 1 minute

return response.newBuilder()

// .header("Cache-Control", cacheControl)

.header("Cache-Control", "public, max-age=" + maxAge)

.removeHeader("Pragma")

.build();

} else {

//无网络

int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale

return response.newBuilder()

// .header("Cache-Control", "public,only-if-cached,max-stale=360000")

.header("Cache-Control", "public,only-if-cached,max-stale=" + maxStale)

.removeHeader("Pragma")

.build();

}

}

};

return commonParams;

}

自定义CookieJar

httpClientBuilder.cookieJar(new CookieJar() {

final HashMap> cookieStore = new HashMap<>();

@Override

public void saveFromResponse(HttpUrl url, List cookies) {

cookieStore.put(url, cookies);//保存cookie

//也可以使用SP保存

}

@Override

public List loadForRequest(HttpUrl url) {

List cookies = cookieStore.get(url);//取出cookie

return cookies != null ? cookies : new ArrayList();

}

});

启动Retrofit2

到了这里,基本上准备工作都做好了,可以启动Retrofit2了

retrofit = new Retrofit.Builder()

.client(httpClientBuilder.build())

.baseUrl(BASE_URL)

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

.addConverterFactory(FastJsonConverterFactory.create())

.build();

mApi = retrofit.create(MyRetrofit2Service.class);

界面上通过getEnqueue()方法调用

/**

* 异步调用

*/

public void getEnqueue() {

Call> call = mApi.post(NEWS_URI, params);

call.enqueue(new Callback>() {

@Override

public void onResponse(Call> call, Response> response) {

//处理请求成功

Log.e("OkHttp", "处理成功请求 response = " + response.body().toString());

}

@Override

public void onFailure(Call> call, Throwable t) {

//处理请求失败

Log.e("OkHttp", "处理失败请求");

}

});

// cancelCall(call);

}

结束

相信到这边应该能满足Demo的要求了吧,至少用是可以用了,不过在实际开发中,这还是太糙了点,哎..没时间打磨,慢慢来吧,有兴趣的可以留言讨论下优化方案,大家拓展下思维也是不错的嘛

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值