从0到1打造一款安卓app之7-Retrofit与kotlin协程、Flow、LiveData的封装

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定义一个FlowCallAdapterFactoryDeferredCallAdapterFactory

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))
                        }
                    })
                }
            }
        }
    }
}
复制代码

猜你喜欢

转载自juejin.im/post/7104548843546476551