Retrofit
用于Android和Java的类型安全HTTP客户端 square.github.io/retrofit/
1.添加依赖
implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'
复制代码
Retrofit至少需要Java 8+或Android API 21+。
2.简介
Retrofit 将您的HTTP API转换为Java接口。
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
复制代码
Retrofit类生成GitHubService接口的实现。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
复制代码
每个来自已创建GitHubService的调用都可以向远程web服务器发出同步或异步的HTTP请求。
Call<List<Repo>> repos = service.listRepos("octocat");
复制代码
使用注释来描述HTTP请求:
- URL参数替换和查询参数支持
- 对象到请求体的转换(例如,JSON,协议缓冲区)
- 多部分请求正文和文件上传
3.API声明
接口方法及其参数上的注释指示将如何处理请求。
3.1声明请求方法
每个方法都必须有一个提供请求方法和相关URL的HTTP注释。有8个内置注释:HTTP、GET、POST、PUT、PATCH、DELETE、OPTIONS和HEAD。资源的相对URL在注释中指定。
@GET("users/list")
复制代码
您还可以在URL中指定查询参数。
@GET("users/list?sort=desc")
复制代码
3.2 URL操作
可以使用方法上的替换块和参数动态更新请求URL。替换块是由{和}包围的字母数字字符串。对应的参数必须使用使用相同字符串的@Path进行注释。
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);
复制代码
也可以添加查询(Query)参数。
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
复制代码
对于复杂的查询参数组合,可以使用Map。
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
复制代码
3.3 设置请求体
可以使用@Body注释指定对象作为HTTP请求体使用。
@POST("users/new")
Call<User> createUser(@Body User user);
复制代码
该对象还将使用Retrofit实例上指定的转换器进行转换。如果没有添加转换器,则只能使用RequestBody。
3.4 表单编码和multipart data。
还可以声明方法来发送表单编码的多部分数据。
当@FormUrlEncoded出现在方法上时,就会发送表单编码的数据。每个键-值对都使用@Field进行注释,其中包含名称和提供值的对象。
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
复制代码
当方法上出现@Multipart时,就会使用Multipart请求。使用@Part注释声明部件。
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
复制代码
Multipart部分使用一个Retrofit的转换器,或者它们可以实现RequestBody来处理它们自己的序列化
3.5 操作请求头
您可以使用@Headers注释为方法设置静态头信息。
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
复制代码
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
复制代码
注意头文件不会相互覆盖。所有具有相同名称的头文件都将包含在请求中。
可以使用@Header注释动态更新请求Header。必须向@Header提供相应的参数。如果值为空,则头文件将被忽略。否则,将对该值调用toString,并使用结果。
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
复制代码
与query 查询参数类似,对于复杂的报头组合,可以使用Map。
@GET("user")
Call<User> getUser(@HeaderMap Map<String, String> headers)
复制代码
需要添加到每个请求的头可以使用OkHttp拦截器指定。
3.6 同步与异步请求调用回调
调用实例可以同步执行,也可以异步执行。每个实例只能使用一次,但是调用clone()将创建一个可以使用的新实例。
在Android上,回调函数将在主线程上执行。在JVM上,回调将发生在执行HTTP请求的同一个线程上。
4.Retrofit 配置
Retrofit是将API接口转换为可调用对象的类。默认情况下,Retrofit将为你的平台提供健全的默认设置,但它允许定制。
4.1转换器
默认情况下,Retrofit只能将HTTP正文反序列化为OkHttp的ResponseBody类型,并且只能接受@Body的RequestBody类型。
可以添加转换器以支持其他类型。为了方便您,六个兄弟模块采用了流行的序列化库。
- Gson:
com.squareup.retrofit2:converter-gson
- Jackson:
com.squareup.retrofit2:converter-jackson
- Moshi:
com.squareup.retrofit2:converter-moshi
- Protobuf:
com.squareup.retrofit2:converter-protobuf
- Wire:
com.squareup.retrofit2:converter-wire
- Simple XML:
com.squareup.retrofit2:converter-simplexml
- JAXB:
com.squareup.retrofit2:converter-jaxb
- Scalars (primitives, boxed, and String):
com.squareup.retrofit2:converter-scalars
下面是一个使用GsonConverterFactory类来生成GitHubService接口的实现的例子,该接口使用Gson进行反序列化。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
复制代码
4.2 自定义类型转换
如果您需要与使用Retrofit不支持开箱即用的内容格式(例如YAML、txt、自定义格式)的API通信,或者您希望使用不同的库来实现现有格式,您可以轻松创建自己的转换器。创建一个扩展Converter的类。工厂类,并在构建适配器时传入一个实例。
5.转换器 Converter
转换器可以转换请求体,转换响应体,转换器定义如下
public interface Converter<F, T> {
@Nullable
T convert(F value) throws IOException;
abstract class Factory {
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
public @Nullable Converter<?, RequestBody> requestBodyConverter(
Type type,
Annotation[] parameterAnnotations,
Annotation[] methodAnnotations,
Retrofit retrofit) {
return null;
}
public @Nullable Converter<?, String> stringConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
}
}
复制代码
5.1 使用注解,转换分发请求体转换器
判断转换器是否需要处理这一次请求,不需要处理的会转发给下一个转换器。
把请求头或其他信息处理后,转发到下一个转换器
public final class ChunkingConverter {
@Target(PARAMETER)
@Retention(RUNTIME)
@interface Chunked {}
/**
* A converter which removes known content lengths to force chunking when {@code @Chunked} is
* present on {@code @Body} params.
*/
static class ChunkingConverterFactory extends Converter.Factory {
@Override
public @Nullable Converter<Object, RequestBody> requestBodyConverter(
Type type,
Annotation[] parameterAnnotations,
Annotation[] methodAnnotations,
Retrofit retrofit) {
boolean isBody = false;
boolean isChunked = false;
for (Annotation annotation : parameterAnnotations) {
isBody |= annotation instanceof Body;
isChunked |= annotation instanceof Chunked;
}
if (!isBody || !isChunked) {
return null;
}
// Look up the real converter to delegate to.
final Converter<Object, RequestBody> delegate =
retrofit.nextRequestBodyConverter(this, type, parameterAnnotations, methodAnnotations);
// Wrap it in a Converter which removes the content length from the delegate's body.
//去掉所有请求头,只保留contentType?
return value -> {
final RequestBody realBody = delegate.convert(value);
return new RequestBody() {
@Override
public MediaType contentType() {
return realBody.contentType();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
realBody.writeTo(sink);
}
};
};
}
}
代码略过
public static void main(String... args) throws IOException, InterruptedException {
代码略过
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl(server.url("/"))
.addConverterFactory(new ChunkingConverterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build();
代码略过
}
}
复制代码
5.1同时支持 Gson,Moshi等转换器
详见官方示例
final class AnnotatedConverters {
public static final class AnnotatedConverterFactory extends Converter.Factory {
private final Map<Class<? extends Annotation>, Converter.Factory> factories;
public static final class Builder {
private final Map<Class<? extends Annotation>, Converter.Factory> factories =
new LinkedHashMap<>();
public Builder add(Class<? extends Annotation> cls, Converter.Factory factory) {
if (cls == null) {
throw new NullPointerException("cls == null");
}
if (factory == null) {
throw new NullPointerException("factory == null");
}
factories.put(cls, factory);
return this;
}
public AnnotatedConverterFactory build() {
return new AnnotatedConverterFactory(factories);
}
}
AnnotatedConverterFactory(Map<Class<? extends Annotation>, Converter.Factory> factories) {
this.factories = new LinkedHashMap<>(factories);
}
@Override
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
for (Annotation annotation : annotations) {
Converter.Factory factory = factories.get(annotation.annotationType());
if (factory != null) {
return factory.responseBodyConverter(type, annotations, retrofit);
}
}
return null;
}
@Override
public @Nullable Converter<?, RequestBody> requestBodyConverter(
Type type,
Annotation[] parameterAnnotations,
Annotation[] methodAnnotations,
Retrofit retrofit) {
for (Annotation annotation : parameterAnnotations) {
Converter.Factory factory = factories.get(annotation.annotationType());
if (factory != null) {
return factory.requestBodyConverter(
type, parameterAnnotations, methodAnnotations, retrofit);
}
}
return null;
}
}
@Retention(RUNTIME)
public @interface Moshi {}
@Retention(RUNTIME)
public @interface Gson {}
@Retention(RUNTIME)
public @interface SimpleXml {}
@Default(value = DefaultType.FIELD)
static final class Library {
@Attribute String name;
}
interface Service {
@GET("/")
@Moshi
Call<Library> exampleMoshi();
@GET("/")
@Gson
Call<Library> exampleGson();
@GET("/")
@SimpleXml
Call<Library> exampleSimpleXml();
@GET("/")
Call<Library> exampleDefault();
}
public static void main(String... args) throws IOException {
MockWebServer server = new MockWebServer();
server.start();
server.enqueue(new MockResponse().setBody("{"name": "Moshi"}"));
server.enqueue(new MockResponse().setBody("{"name": "Gson"}"));
server.enqueue(new MockResponse().setBody("<user name="SimpleXML"/>"));
server.enqueue(new MockResponse().setBody("{"name": "Gson"}"));
com.squareup.moshi.Moshi moshi = new com.squareup.moshi.Moshi.Builder().build();
com.google.gson.Gson gson = new GsonBuilder().create();
MoshiConverterFactory moshiConverterFactory = MoshiConverterFactory.create(moshi);
GsonConverterFactory gsonConverterFactory = GsonConverterFactory.create(gson);
SimpleXmlConverterFactory simpleXmlConverterFactory = SimpleXmlConverterFactory.create();
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl(server.url("/"))
.addConverterFactory(
new AnnotatedConverterFactory.Builder()
.add(Moshi.class, moshiConverterFactory)
.add(Gson.class, gsonConverterFactory)
.add(SimpleXml.class, simpleXmlConverterFactory)
.build())
.addConverterFactory(gsonConverterFactory)
.build();
Service service = retrofit.create(Service.class);
Library library1 = service.exampleMoshi().execute().body();
System.out.println("Library 1: " + library1.name);
Library library2 = service.exampleGson().execute().body();
System.out.println("Library 2: " + library2.name);
Library library3 = service.exampleSimpleXml().execute().body();
System.out.println("Library 3: " + library3.name);
Library library4 = service.exampleDefault().execute().body();
System.out.println("Library 4: " + library4.name);
server.shutdown();
}
}
复制代码
6.自定义 CallAdapter
请求异步执行时,回调用的Callback只有onResponse和onFailure两个回调方法
可以用自定义CallAdapter扩展回调方法
public interface Callback<T> {
void onResponse(Call<T> call, Response<T> response);
void onFailure(Call<T> call, Throwable t);
}
复制代码
6.1.1 定义Callback接口
interface MyCallback<T> {
/** Called for [200, 300) responses. */
void success(Response<T> response);
/** Called for 401 responses. */
void unauthenticated(Response<?> response);
/** Called for [400, 500) responses, except 401. */
void clientError(Response<?> response);
/** Called for [500, 600) response. */
void serverError(Response<?> response);
/** Called for network errors while making the call. */
void networkError(IOException e);
/** Called for unexpected errors while making the call. */
void unexpectedError(Throwable t);
}
复制代码
6.1.2 自定义Call接口
interface MyCall<T> {
void cancel();
void enqueue(MyCallback<T> callback);
MyCall<T> clone();
// Left as an exercise for the reader...
// TODO MyResponse<T> execute() throws MyHttpException;
}
复制代码
6.1.3 实现 自定义Call接口
static class MyCallAdapter<T> implements MyCall<T> {
private final Call<T> call;
private final Executor callbackExecutor;
MyCallAdapter(Call<T> call, Executor callbackExecutor) {
this.call = call;
this.callbackExecutor = callbackExecutor;
}
@Override
public void cancel() {
call.cancel();
}
@Override
public void enqueue(final MyCallback<T> callback) {
call.enqueue(
new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
// TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed
// on that executor by submitting a Runnable. This is left as an exercise for the
// reader.
int code = response.code();
if (code >= 200 && code < 300) {
callback.success(response);
} else if (code == 401) {
callback.unauthenticated(response);
} else if (code >= 400 && code < 500) {
callback.clientError(response);
} else if (code >= 500 && code < 600) {
callback.serverError(response);
} else {
callback.unexpectedError(new RuntimeException("Unexpected response " + response));
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
// TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed
// on that executor by submitting a Runnable. This is left as an exercise for the
// reader.
if (t instanceof IOException) {
callback.networkError((IOException) t);
} else {
callback.unexpectedError(t);
}
}
});
}
@Override
public MyCall<T> clone() {
return new MyCallAdapter<>(call.clone(), callbackExecutor);
}
}
复制代码
6.1.4 继承 CallAdapter和CallAdapter.Factory
public static class ErrorHandlingCallAdapterFactory extends CallAdapter.Factory {
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != MyCall.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalStateException(
"MyCall must have generic type (e.g., MyCall<ResponseBody>)");
}
Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
Executor callbackExecutor = retrofit.callbackExecutor();
return new ErrorHandlingCallAdapter<>(responseType, callbackExecutor);
}
private static final class ErrorHandlingCallAdapter<R> implements CallAdapter<R, MyCall<R>> {
private final Type responseType;
private final Executor callbackExecutor;
ErrorHandlingCallAdapter(Type responseType, Executor callbackExecutor) {
this.responseType = responseType;
this.callbackExecutor = callbackExecutor;
}
@Override
public Type responseType() {
return responseType;
}
@Override
public MyCall<R> adapt(Call<R> call) {
return new MyCallAdapter<>(call, callbackExecutor);
}
}
}
复制代码
6.5 addCallAdapterFactory和调用
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl("http://httpbin.org")
.addCallAdapterFactory(new ErrorHandlingCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build();
复制代码
ip.enqueue(
new MyCallback<Ip>() {
@Override
public void success(Response<Ip> response) {
System.out.println("SUCCESS! " + response.body().origin);
}
@Override
public void unauthenticated(Response<?> response) {
System.out.println("UNAUTHENTICATED");
}
@Override
public void clientError(Response<?> response) {
System.out.println("CLIENT ERROR " + response.code() + " " + response.message());
}
@Override
public void serverError(Response<?> response) {
System.out.println("SERVER ERROR " + response.code() + " " + response.message());
}
@Override
public void networkError(IOException e) {
System.err.println("NETWORK ERROR " + e.getMessage());
}
@Override
public void unexpectedError(Throwable t) {
System.err.println("FATAL ERROR " + t.getMessage());
}
});
复制代码
6.2 Rxjava中CallAdapter与线程切换
示例,在安卓中,在主线程观察结果
static final class ObserveOnMainCallAdapterFactory extends CallAdapter.Factory {
final Scheduler scheduler;
ObserveOnMainCallAdapterFactory(Scheduler scheduler) {
this.scheduler = scheduler;
}
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Observable.class) {
return null; // Ignore non-Observable types.
}
// Look up the next call adapter which would otherwise be used if this one was not present.
//noinspection unchecked returnType checked above to be Observable.
final CallAdapter<Object, Observable<?>> delegate =
(CallAdapter<Object, Observable<?>>) retrofit.nextCallAdapter(this, returnType, annotations);
return new CallAdapter<Object, Object>() {
@Override
public Object adapt(Call<Object> call) {
// Delegate to get the normal Observable...
Observable<?> o = delegate.adapt(call);
// ...and change it to send notifications to the observer on the specified scheduler.
return o.observeOn(scheduler);
}
@Override
public Type responseType() {
return delegate.responseType();
}
};
}
}
复制代码
7.在拦截器里获取一些请求时的参数
示例,在响应后,打印请求路和参数和响应内容一起输出
public interface Browse {
@GET("/robots.txt")
Call<ResponseBody> robots();
@GET("/favicon.ico")
Call<ResponseBody> favicon();
@GET("/")
Call<ResponseBody> home();
@GET
Call<ResponseBody> page(@Url String path);
}
static final class InvocationLogger implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startNanos = System.nanoTime();
Response response = chain.proceed(request);
long elapsedNanos = System.nanoTime() - startNanos;
Invocation invocation = request.tag(Invocation.class);
if (invocation != null) {
System.out.println("Invocation info");
System.out.printf(
"%s.%s %s HTTP %s (%.0f ms)%n",
//请求的接口类,如上面定义的public interface Browse {}
invocation.method().getDeclaringClass().getSimpleName(),
//请求的方法名称
invocation.method().getName(),
//请求参数 List<?>
invocation.arguments(),
//响应内容
response.code(),
elapsedNanos / 1_000_000.0);
}
return response;
}
}
复制代码
8. retrofit和kotlin协程
retrofit最新版本原生支持协程
普通Call返回值
@POST("{keyword}")
@Headers("Content-Type: application/json")
fun search(@Path("keyword") keyword: String): Call<Response<String>>
复制代码
原生支持 suspend fun
可以直接转成自定义的类型数据
@POST("{keyword}")
@Headers("Content-Type: application/json")
suspend fun search(@Path("keyword") keyword: String): Response或者自定义的类型
复制代码
suspend fun可以直接在协程作用域里以同步的方式调用异步请求
8.1 retrofit和Flow一起使用。
方式1.suspend fun
直接与flow{}
或者flowOf()
方式一起配合使用
flow<InstallationRecord> {
val result = xxxService.instance.search("kotlin")
emit(i)
}.catch { exception ->
LogUtils.e("exception:$exception")
}.collect {
LogUtils.d("result:$it")
}
flowOf(xxxService.instance.search("kotlin"))
.catch { exception ->
LogUtils.e("exception:$exception")
}.collect {
LogUtils.d("result:$it")
}
复制代码
exception
的类型是 HttpException
,只要响应的不是200范围(200..299)的都算是异常响应,都会走到catch
里。但是这样使用的时候,响应的错误内容不太好取到。
8.2 retrofit和CompletableFuture一起使用。
方式2: 非suspend fun
方法返回类型定义成CompletableFuture<T>
,然后把CompletableFuture
转换成Flow
或者Deferred
@GET("{keyword}")
fun queryByKeyword(@Path("keyword") keyword: String): CompletableFuture<自定义的类型>
复制代码
定义CompletableFuture类的扩展方法
fun <T> CompletableFuture<T>.asFlow(): Flow<T> {
return flow {
val result = await()
emit(result)
}
}
@RequiresApi(Build.VERSION_CODES.N)
fun <T> CompletableFuture<T>.asDeferred(): Deferred<T> {
return thenApply {
it
}.asDeferred()
}
复制代码
转换成flow或者Deferred调用。
xxxService.instance.queryByKeyword("kotlin")
.asFlow()
.catch { exception ->
LogUtils.e("exception:$exception")
}
.collect {
LogUtils.d("queryByObjectId result:$it")
}
复制代码
此方案需要引入依赖
api "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:${kotlinxCoroutinesJdk8}"
8.3 自定义Flow或者Deferred的工厂类 CallAdapter.Factory
可以参考CompletableFutureCallAdapterFactory
定义一个FlowCallAdapterFactory
和DeferredCallAdapterFactory
。
class FlowCallAdapterFactory() : CallAdapter.Factory() {
@Nullable
override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
if (getRawType(returnType) != Flow::class.java) {
return null
}
if (returnType !is ParameterizedType) {
throw IllegalStateException("Flow return type must be parameterized as Flow<Foo> or Flow<? extends Foo>")
}
val innerType = getParameterUpperBound(0, returnType)
if (getRawType(innerType) != Response::class.java) {
// Generic type is not Response<T>. Use it for body-only adapter.
return FlowBodyCallAdapter<Any?>(innerType)
}
// Generic type is Response<T>. Extract T and create the Response version of the adapter.
if (innerType !is ParameterizedType) {
throw IllegalStateException(
"Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>"
)
}
val responseType = getParameterUpperBound(0, innerType)
return FlowResponseCallAdapter<Any?>(responseType)
}
companion object {
val INSTANCE: CallAdapter.Factory = FlowCallAdapterFactory()
}
}
private class FlowBodyCallAdapter<R>(val responseType: Type) : CallAdapter<R, Flow<R?>> {
override fun responseType(): Type {
return responseType
}
override fun adapt(call: Call<R?>): Flow<R?> {
return flow<R?> {
val response = call.awaitResponse()
if (response.isSuccessful) {
emit(response.body())
} else {
throw HttpErrorBodyException(response)
}
}
}
}
private class FlowResponseCallAdapter<R>(val responseType: Type) : CallAdapter<R, Flow<Response<R>>> {
override fun responseType(): Type {
return responseType
}
override fun adapt(call: Call<R>): Flow<Response<R>> {
return flow<Response<R>> {
val response = call.awaitResponse()
if (response.isSuccessful) {
emit(response)
} else {
throw HttpErrorBodyException(response)
}
}
}
}
复制代码
9.retrofit和LiveData配合使用
@GET("search/repositories")
fun searchRepos(@Query("q") query: String): LiveData<ApiResponse<RepoSearchResponse>>
复制代码
class LiveDataCallAdapterFactory : Factory() {
override fun get(
returnType: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): CallAdapter<*, *>? {
if (getRawType(returnType) != LiveData::class.java) {
return null
}
val observableType = getParameterUpperBound(0, returnType as ParameterizedType)
val rawObservableType = getRawType(observableType)
if (rawObservableType != ApiResponse::class.java) {
throw IllegalArgumentException("type must be a resource")
}
if (observableType !is ParameterizedType) {
throw IllegalArgumentException("resource must be parameterized")
}
val bodyType = getParameterUpperBound(0, observableType)
return LiveDataCallAdapter<Any>(bodyType)
}
}
复制代码
class LiveDataCallAdapter<R>(private val responseType: Type) :
CallAdapter<R, LiveData<ApiResponse<R>>> {
override fun responseType() = responseType
override fun adapt(call: Call<R>): LiveData<ApiResponse<R>> {
return object : LiveData<ApiResponse<R>>() {
private var started = AtomicBoolean(false)
override fun onActive() {
super.onActive()
if (started.compareAndSet(false, true)) {
call.enqueue(object : Callback<R> {
override fun onResponse(call: Call<R>, response: Response<R>) {
postValue(ApiResponse.create(response))
}
override fun onFailure(call: Call<R>, throwable: Throwable) {
postValue(ApiResponse.create(throwable))
}
})
}
}
}
}
}
复制代码