项目作者: jaychang0917

项目描述 :
A configurable api client based on Retrofit2 and RxJava2 for android
高级语言: Kotlin
项目地址: git://github.com/jaychang0917/SimpleApiClient.git
创建时间: 2017-09-30T13:07:25Z
项目社区:https://github.com/jaychang0917/SimpleApiClient

开源协议:Apache License 2.0

下载


SimpleApiClient

Download
Android Weekly

A configurable api client based on Retrofit2 and RxJava2 for android

Table of Contents

Installation

In your app level build.gradle :

  1. dependencies {
  2. implementation 'com.jaychang:simpleapiclient:2.3.0'
  3. // if you use gson
  4. implementation 'com.jaychang:simpleapiclient-jsonparser-gson:2.3.0'
  5. // if you use moshi
  6. implementation 'com.jaychang:simpleapiclient-jsonparser-moshi:2.3.0'
  7. }

Basic Usage

Step 1

Config the api client and use it to create your api.

  1. interface GithubApi {
  2. companion object {
  3. fun create() : GithubApi =
  4. SimpleApiClient.create {
  5. baseUrl = "https://api.github.com"
  6. defaultParameters = mapOf()
  7. defaultHeaders = mapOf()
  8. connectTimeout = TimeUnit.MINUTES.toMillis(1)
  9. readTimeout = TimeUnit.MINUTES.toMillis(1)
  10. writeTimeout = TimeUnit.MINUTES.toMillis(1)
  11. logLevel = LogLevel.BASIC // default NONE
  12. isMockResponseEnabled = true // default false
  13. certificatePins = listOf(
  14. CertificatePin(hostname = "api.foo.com", sha1PublicKeyHash = "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"),
  15. CertificatePin(hostname = "api.bar.com", sha256PublicKeyHash = "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9")
  16. )
  17. interceptors = listOf()
  18. networkInterceptors = listOf()
  19. httpClient = OkHttpClient.Builder().build() // your own http client
  20. jsonParser = MoshiJsonParser()
  21. errorClass = ApiError::class // should be conformed to SimpleApiError
  22. errorMessageKeyPath = "meta.message"
  23. errorHandler = { error ->
  24. // you can centralize the handling of general error here
  25. when (error) {
  26. is AuthenticationError -> {...}
  27. is ClientError -> {...}
  28. is ServerError -> {...}
  29. is NetworkError -> {...}
  30. is SSLError -> {...}
  31. }
  32. }
  33. }
  34. }
  35. @GET("/search/users")
  36. fun getUsers(@Query("q") query: String): Single<List<User>>
  37. }
  38. `

Step 2

Use observe() to enqueue the call, do your stuff in corresponding parameter block. All blocks are run on android main thread by default and they are optional.

  1. githubApi.getUsers("google")
  2. .observe(
  3. onStart = { println("show loading") },
  4. onEnd = { println("hide loading") },
  5. onSuccess = { println(it) },
  6. onError = { println(it.message) }
  7. )

Unwrap Response by KeyPath

Sometimes the api response includes metadata that we don’t need, but in order to map the response we create a wrapper class and make the function return that wrapper class.
This approach leaks the implementation of service to calling code.

Assuming the response json looks like the following:

  1. {
  2. total_count: 33909,
  3. incomplete_results: false,
  4. foo: {
  5. bar: {
  6. items: [
  7. {
  8. login: "jaychang0917",
  9. ...
  10. }
  11. ...
  12. ]
  13. }
  14. }
  15. }

And you only want the items part, use @KeyPathResponse("keypath") annotation to indicate which part of response you want.

  1. @GET("/search/users")
  2. @KeyPathResponse("foo.bar.items")
  3. fun getUsers(@Query("q") query: String): Single<List<User>>

Similarly, unwrap the error response by setting the errorMessageKeyPath of SimpleApiClient.Config

Unwrap Response by Wrapper Class

An alternative solution is that you can create a wrapper class that conforming SimpleApiResult<T>, and use @WrappedResponse(class) to indicate that you want an unwrapped response of that wrapper class.

  1. class ApiResult<T: Any>: SimpleApiResult<T> {
  2. ...
  3. }
  4. @GET("/search/users")
  5. @WrappedResponse(ApiResult::class)
  6. fun getUsers(@Query("q") query: String): Single<List<User>>

Serial / Concurrent Calls

Serial

  1. githubApi.foo()
  2. .then { foo -> githubApi.bar(foo.name) }
  3. .observe(...)

Serial then Concurrent

  1. githubApi.foo()
  2. .then { foo -> githubApi.bar(foo.name) }
  3. .thenAll( bar ->
  4. githubApi.baz(bar.name),
  5. githubApi.qux(bar.name)
  6. )
  7. .observe(...)

Concurrent

  1. SimpleApiClient.all(
  2. githubApi.foo(),
  3. githubApi.bar()
  4. ).observe(...)

Concurrent then Serial

  1. SimpleApiClient.all(
  2. githubApi.foo(),
  3. githubApi.bar()
  4. ).then { array -> // the return type is Array<Any>, you should cast them, e.g. val users = array[0] as List<User>
  5. githubApi.baz()
  6. }.observe(...)

Retry Interval / Exponential backoff

  1. githubApi.getUsers("google")
  2. .retryInterval(maxRetryCount = 3, delaySeconds = 5) // retry up to 3 times, each time delays 5 seconds
  3. .retryExponential(maxRetryCount = 3, delaySeconds = 5) // retry up to 3 times, each time delays 5^n seconds, where n = {1,2,3}
  4. .observe(...)

Call Cancellation

Auto Call Cancellation

The api call will be cancelled automatically in corresponding lifecycle callback. For instance, an api call is made in onStart(), it be will cancelled automatically in onStop.

  1. githubApi.getUsers("google")
  2. .autoDispose(this)
  3. .observe(...)

Cancel call manually

  1. val call = githubApi.getUsers("google").observe(...)
  2. call.dispose()

Mock Response

To enable response mocking, set SimpleApiClient.Config.isMockResponseEnabled to true.

Mock sample json data

To make the api return a successful response with provided json

  1. @GET("/repos/{user}/{repo}")
  2. @MockResponse(R.raw.get_repo)
  3. fun getRepo(@Path("user") user: String, @Path("repo") repo: String): Single<Repo>

Mock status

To make the api return a client side error with provided json

  1. @GET("/repos/{user}/{repo}")
  2. @MockResponse(json = R.raw.get_repo_error, status = Status.CLIENT_ERROR)
  3. fun getRepo(@Path("user") user: String, @Path("repo") repo: String): Single<Repo>

json parameter of MockResponse is optional, you can set the status only, then you receive empty string.

Possible Status values:

  1. enum class Status {
  2. SUCCESS, AUTHENTICATION_ERROR, CLIENT_ERROR, SERVER_ERROR, NETWORK_ERROR, SSL_ERROR
  3. }

To mock a response with success status only, you should return Completable.

  1. @DELETE("/repo/{id}}")
  2. @MockResponse(status = Status.SUCCESS)
  3. fun deleteRepo(@Path("id") id: String): Completable

License

  1. Copyright 2017 Jay Chang
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.