这个笔记会记录一些比较杂的东西,主要是Retrofit2使用中需要注意的要点。
Synchronous vs. Asynchronous
Call instances can be executed either synchronously or asynchronously. Each instance can only be used once, but calling clone() will create a new instance that can be used.
On Android, callbacks will be executed on the main thread. On the JVM, callbacks will happen on the same thread that executed the HTTP request.
- 以上是官方网站上的一段介绍,大意就是无论是同步的还是异步的请求
Call
的实例只能使用一次,如果想继续使用的话,就需要使用Call
的clone()
方法创建一个新的对象去使用。对于Android平台回调会在main线程也就是UI线程实行,所以可以在回调中更新UI。 - 如果使用ProGuard混淆代码需要加入以下配置。
# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on RoboVM on iOS. Will not be used at runtime.
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions
- 可以通过
client
方法设置Retrofit2的默认OkHttpClient
,在设置这个OkHttpClient
的时候我们还可以做很多工作,我们先看一下代码。
//创建OkHttpClient
OkHttpClient okHttpClient= new OkHttpClient.Builder()
//添加一个网络拦截器
.addNetworkInterceptor( new HttpLoggingInterceptor()
//日志级别
.setLevel(HttpLoggingInterceptor.Level.HEADERS))
//设置cookie管理器
.cookieJar(new NovateCookieManger(context))
.addInterceptor(new BaseInterceptor(mContext))
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(url)
.build();
首先创建OkHttpClient
的时候可以使用addNetworkInterceptor
方法添加一个网络拦截器。这里使用了一个日志拦截器HttpLoggingInterceptor
它可以打印OKHttp的request和response的数据。要使用这个类需要引入依赖。
compile 'com.squareup.okhttp3:logging-interceptor:3.0.1'
简单介绍一下这个拦截器,基本的使用方法就是这样。
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
setLevel
方法设置日志级别,NONE
没有日志、BASIC
基础日志(request 和 response 的信息)、HEADERS
请求头信息(除了 request 和 response 的信息外还有各自的headers)、BODY
请求体信息(除了 request 和 response 的信息外还有各自的headers以及各自的body),我们可以根据自身的需要进行设置。并且在任何时刻都可以通过setLevel
方法重新设置日志级别。如果希望打印的日志做一些本地化的工作,可以在创建HttpLoggingInterceptor
对象的时候可以在构造器中传入一个Logger的实例,方法参考下面代码。
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override public void log(String message) {
Log.d("OkHttp",message);
}
});
这样我们的日志输出都是debug,并且tag都是"OkHttp"。
再来看一下cookieJar
方法,这个方法可以为OkHttpClient
设置一个Cookie的管理者。它接收一个实现CookieJar
接口的类的对象。我们看一下CookieJar
接口的源码:
public interface CookieJar {
/** A cookie jar that never accepts any cookies. */
CookieJar NO_COOKIES = new CookieJar() {
@Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
}
@Override public List<Cookie> loadForRequest(HttpUrl url) {
return Collections.emptyList();
}
};
/**
* Saves {@code cookies} from an HTTP response to this store according to this jar's policy.
*
* <p>Note that this method may be called a second time for a single HTTP response if the response
* includes a trailer. For this obscure HTTP feature, {@code cookies} contains only the trailer's
* cookies.
*/
void saveFromResponse(HttpUrl url, List<Cookie> cookies);
/**
* Load cookies from the jar for an HTTP request to {@code url}. This method returns a possibly
* empty list of cookies for the network request.
*
* <p>Simple implementations will return the accepted cookies that have not yet expired and that
* {@linkplain Cookie#matches match} {@code url}.
*/
List<Cookie> loadForRequest(HttpUrl url);
}
它的内部有一个实现NO_COOKIES
是当当前的请求没有Cookie的时候使用的,剩下两个方法saveFromResponse
和loadForRequest
一看便知,一个是保存Response返回的Cookie一个是向Request请求添加Cookie的。具体的使用可以参考如下代码:
OkHttpClient client = new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url, cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = cookieStore.get(url);
return cookies != null ? cookies : new ArrayList<Cookie>();
}
})
.build();
看OkHttpClient
的源码可以知道,其实cookieJar
方法设置的是OkHttpClient
对象中的一个属性,所以当我们使用这个OkHttpClient
对象作为网络请求客户端的时候,这个Cookie管理者会一直起作用。
我们再来看一下addInterceptor
方法:
public Builder addInterceptor(Interceptor interceptor)
它接收一个实现了Interceptor
接口的对象,看一下Interceptor
源码:
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
其中intercept
方法是拦截器的逻辑实现,我们可以在这个方法里面对请求和应答做处理。请看下面的例子:
/** This interceptor compresses the HTTP request body. Many Webservers can't handle this! */
final class GzipRequestInterceptor implements Interceptor {
@Override public Response interceptor(Interceptor.Chain chain) thorws IOException {
Request originalRequest = chain.request();
if (originalRequest.body() == null || originalRequest.header("Content-Encoding")) {
return chain.proceed(originalRequest);
}
Request comressedRequest = originalRequest.newBuilder() .header("Content-Encoding", "gzip") .method(originalRequest.method(), gzip(originalRequest.body())) .build();
return chain.proceed(compressedRequest);
}
private RequestBody gzip(final RequestBody body) {
return new RequestBody() {
@Override
public MediaType contentType() {
return body.contentType();
}
@Override
public long contentLength() {
return -1;
// We don't know the compressed length in adcance!
}
@Override
public void wirteTo(BufferedSink sink) throws IOException {
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
body.writeTo(gzipSink);
gzipSink.close();
}
};
}
}
我们可以看到比较关键的几个点,可以通过interceptor
方法的入参chain.request()
获取Request
,然后chain.proceed(originalRequest)
方法可以将请求继续执行,并返回应答。我们可以通过这个拦截器对请求和应答进行一些处理。
还有connectTimeout
方法,不用多说这个方法是设置网络连接超时的,还有一个上面没有提到的方法retryOnConnectionFailure(boolean retryOnConnectionFailure)
可以通过这个方法来设置是否在连接失败的时候重试。
------------未完待续