{"id":17182215,"url":"https://github.com/wzasd/gin","last_synced_at":"2026-04-12T23:54:01.526Z","repository":{"id":57735249,"uuid":"445032160","full_name":"wzasd/Gin","owner":"wzasd","description":"一个轻量化以及标准化的 Android 网络框架，快速清晰的处理网络相应","archived":false,"fork":false,"pushed_at":"2022-01-11T08:30:45.000Z","size":175,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-13T09:39:08.353Z","etag":null,"topics":["android","api","jetpack","kotlin","network","okhttp3","restful-api","retrofit2"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wzasd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-06T03:49:35.000Z","updated_at":"2023-03-08T06:32:03.000Z","dependencies_parsed_at":"2022-09-26T22:11:00.069Z","dependency_job_id":null,"html_url":"https://github.com/wzasd/Gin","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/wzasd/Gin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzasd%2FGin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzasd%2FGin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzasd%2FGin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzasd%2FGin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wzasd","download_url":"https://codeload.github.com/wzasd/Gin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzasd%2FGin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270579613,"owners_count":24610044,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-15T02:00:12.559Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","api","jetpack","kotlin","network","okhttp3","restful-api","retrofit2"],"created_at":"2024-10-15T00:36:20.919Z","updated_at":"2026-04-12T23:54:01.487Z","avatar_url":"https://github.com/wzasd.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n# Gin\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://android-arsenal.com/api?level=16\"\u003e\u003cimg alt=\"API\" src=\"https://img.shields.io/badge/API-16%2B-brightgreen.svg?style=flat\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/wzasd/Gin/actions\"\u003e\u003cimg alt=\"Build Status\" src=\"https://github.com/wzasd/Gin/actions/workflows/android.yml/badge.svg?branch=main\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## 为什么要使用 Gin?\n\nGin 是用来构建标准化响应接口而开发的，解耦业务和网络框架开发的。框架可以简单直观的处理成功、失败和异常的数据吧，并且有良好的扩展性。所以我们并不需要设计和实现如「Resource」或者「Result」之类的包装类，减少我们的模板代码，只需要关注业务代码。Gin 支持全局的[error response globally](#global-operator)，[Mapper](#mapper)，[Operator](#operator)，而且对[LiveData](#tolivedata)和[Flow](#toflow)兼容。当然，对当前比较流行的协程（[coroutine](#apiresponse-for-coroutines)）还有[flow](#suspendonsuccess-suspendonerror-suspendonexception)都有很好地支持，引入一次，让你用上最新技术~\n\n## Download\n\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.wzasd/gin.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.wzasd%22%20AND%20a:%22gin%22)\n\n### Gradle\n\n将以下代码添加到你根目录的`build.gralde`（不是模块内的）。\n\n```gradle\nallprojects {\n    repositories {\n        mavenCentral()\n    }\n}\n```\n\n讲以下依赖代码复制到你的模块内`bulid.gradle`文件内。\n\n```gradle\ndependencies {\n    implementation \"io.github.wzasd:gin:1.0.0\"\n}\n```\n\n## 目录\n\n- [ApiResponse](https://github.com/wzasd/gin#apiresponse)\n- [onSuccess, onError, onException](https://github.com/wzasd/gin#apiresponse-extensions)\n- [ApiResponse for coroutines](https://github.com/wzasd/gin#apiresponse-for-coroutines)\n- [suspendOnSuccess, suspendOnError, suspendOnException](https://github.com/wzasd/gin#suspendonsuccess-suspendonerror-suspendonexception)\n- [Retrieve success data](https://github.com/wzasd/gin#retrieve-success-data)\n- [Mapper](https://github.com/wzasd/gin#mapper)\n- [Operator](https://github.com/skydoves/sandwich#operator), [Operator for coroutines](https://github.com/wzasd/gin#operator-with-coroutines), [Global Operator](https://github.com/skydoves/sandwich#global-operator)\n- [Merge](https://github.com/wzasd/gin#merge)\n- [toLiveData](https://github.com/skydoves/sandwich#tolivedata). [toFlow](https://github.com/wzasd/gin#toflow)\n- [ResponseDataSource](https://github.com/wzasd/gin#responsedatasource)\n\n## 使用\n\n### ApiResponse\n\n`ApiResponse` 是一个接口，用于根据标准化响应的格式规范。它为处理成功的数据和错误响应提供了灵活的扩展。我们可以使用来自 `Call` 的范围扩展 `request` 来获取 `ApiResponse`。下面的示例是从 `Call` 实例获取`ApiResponse`的基础。\n\n```kotlin\ninterface DisneyService {\n  @GET(\"/\")\n  fun fetchDisneyPosterList(): Call\u003cList\u003cPoster\u003e\u003e\n}\n\nval disneyService = retrofit.create(DisneyService::class.java)\n// fetches a model list from the network and getting [ApiResponse] asynchronously.\ndisneyService.fetchDisneyPosterList().request { response -\u003e\n      when (response) {\n        // handles the success case when the API request gets a successful response.\n        is ApiResponse.Success -\u003e {\n          posterDao.insertPosterList(response.data)\n          livedata.post(response.data)\n        }\n        // handles error cases when the API request gets an error response.\n        // e.g., internal server error.\n        is ApiResponse.Failure.Error -\u003e {\n          // stub error case\n          Timber.d(message())\n\n          // handles error cases depending on the status code.\n          when (statusCode) {\n            StatusCode.InternalServerError -\u003e toastLiveData.postValue(\"InternalServerError\")\n            StatusCode.BadGateway -\u003e toastLiveData.postValue(\"BadGateway\")\n            else -\u003e toastLiveData.postValue(\"$statusCode(${statusCode.code}): ${message()}\")\n          }\n        }\n        // handles exceptional cases when the API request gets an exception response.\n        // e.g., network connection error, timeout.\n        is ApiResponse.Failure.Exception -\u003e {\n          // stub exception case\n        }\n      }\n    }\n```\n\n#### ApiResponse.Success\n\n Retrofit 网络响应的标准成功响应接口。\u003cbr\u003e\n我们可以从 `ApiResponse.Success` 中获取响应的成功主体数据、`StatusCode`、`Headers` 等。\n\n```kotlin\nval data: List\u003cPoster\u003e? = response.data\nval statusCode: StatusCode = response.statusCode\nval headers: Headers = response.headers\n```\n\n#### ApiResponse.Failure.Error\n\n Retrofit 网络响应的异常响应接口。\u003cbr\u003e API 通信错误处理。\n\n```kotlin\nval errorBody: ResponseBody? = response.errorBody\nval statusCode: StatusCode = response.statusCode\nval headers: Headers = response.headers\n```\n\n#### ApiResponse.Failure.Exception \n\n在客户端创建请求或处理响应时发生意外。\n\n### ApiResponse Extensions\n\n当然，我们可以使用扩展方便地处理响应案例。\n\n#### onSuccess, onError, onException\n\n我们可以将这些作用域函数用于 `ApiResponse`，我们在不使用 `if-else/when` 子句的情况下处理响应情况。 \u003cbr\u003e 每个范围将根据 `ApiResponse` 的类型执行或不执行。 （成功、错误、异常）\n\n```kotlin\ndisneyService.fetchDisneyPosterList().request { response -\u003e\n    response.onSuccess {\n     // this scope will be only executed if the request would successful.\n     // handle the success case\n    }.onError {\n      // this scope will be only executed when the request would get errors.\n      // handle the error case\n    }.onException {\n     // this scope will be only executed when the request would get exceptions.\n     // handle the exception case\n    }\n  }\n```\n\n### ApiResponse for coroutines\n\n如果需要使用协程中使用 `suspend` 关键字并获取 `ApiResponse\u003c*\u003e` 作为响应类型。\u003cbr\u003e 使用 `CoroutinesResponseCallAdapterFactory` 调用适配器工厂构建你的改造。\n\n```kotlin\n.addCallAdapterFactory(CoroutinesResponseCallAdapterFactory.create())\n```\n\n我们应该使用 `suspend` 关键字将正常的服务功能作为暂停功能。我们可以得到 `ApiResponse\u003c*\u003e` 作为响应类型。因此，我们可以从 Retrofit 服务调用中获取 `ApiResponse`，并使用扩展立即处理它们。\n\n```kotlin\ninterface DisneyCoroutinesService {\n\n  @GET(\"DisneyPosters.json\")\n  suspend fun fetchDisneyPosterList(): ApiResponse\u003cList\u003cPoster\u003e\u003e\n}\n```\n\n我们可以像下面这样使用协程：\n\n```kotlin\nclass MainCoroutinesViewModel constructor(disneyService: DisneyCoroutinesService) : ViewModel() {\n\n  val posterListLiveData: MutableLiveData\u003cList\u003cPoster\u003e\u003e\n\n  init {\n     val response = disneyService.fetchDisneyPosterList()\n     response.onSuccess {\n       // handles the success case when the API request gets a successful response.\n       posterDao.insertPosterList(data)\n       posterListLiveData.post(data)\n      }.onError {\n       // handles error cases when the API request gets an error response.\n      }.onException {\n       // handles exceptional cases when the API request gets an exception response.\n      }\n    }\n  }\n}\n```\n\n#### suspendOnSuccess, suspendOnError, suspendOnException\n\n我们可以使用暂停扩展来调用作用域内的暂停相关功能。这些扩展在功能上与 `onSuccess`、`onError` 和 `onException` 扩展没有区别。 \u003cbr\u003e\n一般我们可以在[repository pattern](https://github.com/wzasd/Pokedex/blob/main/app/src/main/java/com/skydoves/pokedex/repository/MainRepository.kt) 上使用这种方式。\n\n```kotlin\nflow {\n  val response = disneyService.fetchDisneyPosterList()\n  response.suspendOnSuccess {\n    posterDao.insertPosterList(data)\n    emit(data)\n  }.suspendOnError {\n    // handles error cases\n  }.suspendOnFailure {\n    // handles exceptional cases\n  }\n}.flowOn(Dispatchers.IO)\n```\n\n### Retrieve success data\n\n如果我们想直接从 `ApiResponse` 中检索封装的成功数据，我们可以使用以下功能。\n\n#### getOrNull\n\n如果此实例表示 `ApiResponse.Success`，则返回封装的数据；如果此实例失败，则返回 null。\n\n```kotlin\nval data: List\u003cPoster\u003e? = disneyService.fetchDisneyPosterList().getOrNull()\n```\n\n#### getOrElse\n\n如果此实例表示 `ApiResponse.Success`，则返回封装的数据，如果失败，则返回默认值。\n\n```kotlin\nval data: List\u003cPoster\u003e? = disneyService.fetchDisneyPosterList().getOrElse(emptyList())\n```\n\n#### getOrThrow\n\n如果此实例表示 `ApiResponse.Success`，则返回封装的数据；如果失败，则抛出封装的 `Throwable` 异常。\n\n```kotlin\ntry {\n  val data: List\u003cPoster\u003e? = disneyService.fetchDisneyPosterList().getOrThrow()\n} catch (e: Exception) {\n  e.printStackTrace()\n}\n```\n\n### Mapper\n\n当想要将 `ApiResponse.Success` 或 `ApiResponse.Failure.Error` 转换为 `ApiResponse` 扩展范围中的自定义模型时，映射器很有用。\n\n#### ApiSuccessModelMapper\n\n可以使用 `SuccessPosterMapper\u003cT, R\u003e` 和 `map` 扩展将 `ApiResponse.Success` 模型映射到的自定义模型。\n\n```kotlin\nobject SuccessPosterMapper : ApiSuccessModelMapper\u003cList\u003cPoster\u003e, Poster?\u003e {\n\n  override fun map(apiErrorResponse: ApiResponse.Success\u003cList\u003cPoster\u003e\u003e): Poster? {\n    return apiErrorResponse.data.first()\n  }\n}\n\n// Maps the success response data.\nval poster: Poster? = map(SuccessPosterMapper)\n```\n\n可以使用带有 lambda 的 `map` 扩展。\n\n```kotlin\n// Maps the success response data using a lambda.\nmap(SuccessPosterMapper) { poster -\u003e\n  emit(poster) // we can use the `this` keyword instead of the poster.\n}\n```\n\n如果想在 lambda 中从头开始获取转换后的数据，可以将映射器作为 `onSuccess` 或 `suspendOnSuccess` 的参数。\n\n```kotlin\n.suspendOnSuccess(SuccessPosterMapper) {\n    val poster = this\n}\n```\n\n#### ApiErrorModelMapper\n\n可以使用 `ApiErrorModelMapper\u003cT\u003e` 和 `map` 扩展将 `ApiResponse.Failure.Error` 模型映射到自定义错误模型。\n\n```kotlin\n// Create your custom error model.\ndata class ErrorEnvelope(\n  val code: Int,\n  val message: String\n)\n\n// An error response mapper.\n// Create an instance of your custom model using the `ApiResponse.Failure.Error` in the `map`.\nobject ErrorEnvelopeMapper : ApiErrorModelMapper\u003cErrorEnvelope\u003e {\n\n  override fun map(apiErrorResponse: ApiResponse.Failure.Error\u003c*\u003e): ErrorEnvelope {\n    return ErrorEnvelope(apiErrorResponse.statusCode.code, apiErrorResponse.message())\n  }\n}\n\n// Maps an error response.\nresponse.onError {\n  // Maps an ApiResponse.Failure.Error to a custom error model using the mapper.\n  map(ErrorEnvelopeMapper) {\n     val code = this.code\n     val message = this.message\n  }\n}\n```\n\n如果想在 lambda 中从头开始获取转换后的数据，可以将映射器作为 `onError` 或 `suspendOnError` 的参数。\n\n```kotlin\n.suspendOnError(ErrorEnvelopeMapper) {\n    val message = this.message\n}\n```\n\n### Operator\n\n使用 `operator` 扩展和 `ApiResponseOperator` 委托 `onSuccess`、`onError`、`onException`。如果想要标准地处理 `ApiResponse` 或减少 `ViewModel` 和 `Repository` 的角色，操作符非常有用。这是标准化错误和异常处理的示例。\n\n#### ViewModel\n\n使用 `operate` 扩展委托和操作 `CommonResponseOperator`。\n\n```kotlin\ndisneyService.fetchDisneyPosterList().operator(\n      CommonResponseOperator(\n        success = {\n          emit(data)\n          Timber.d(\"success data: $data\")\n        },\n        application = getApplication()\n      )\n    )\n```\n\n#### CommonResponseOperator\n\n`CommonResponseOperator` 使用 `onSuccess`、`onError`、`onException` 覆盖方法扩展了 `ApiResponseOperator`。它们将根据 `ApiResponse` 的类型执行。\n\n```kotlin\n/** A common response operator for handling [ApiResponse]s regardless of its type. */\nclass CommonResponseOperator\u003cT\u003e constructor(\n  private val success: suspend (ApiResponse.Success\u003cT\u003e) -\u003e Unit,\n  private val application: Application\n) : ApiResponseOperator\u003cT\u003e() {\n\n  // handles error cases when the API request gets an error response.\n  override fun onSuccess(apiResponse: ApiResponse.Success\u003cT\u003e) = success(apiResponse)\n\n  // handles error cases depending on the status code.\n  // e.g., internal server error.\n  override fun onError(apiResponse: ApiResponse.Failure.Error\u003cT\u003e) {\n    apiResponse.run {\n      Timber.d(message())\n      \n      // map the ApiResponse.Failure.Error to a customized error model using the mapper.\n      map(ErrorEnvelopeMapper) {\n        Timber.d(\"[Code: $code]: $message\")\n      }\n    }\n  }\n\n  // handles exceptional cases when the API request gets an exception response.\n  // e.g., network connection error, timeout.\n  override fun onException(apiResponse: ApiResponse.Failure.Exception\u003cT\u003e) {\n    apiResponse.run {\n      Timber.d(message())\n      toast(message())\n    }\n  }\n}\n```\n\n### Operator for coroutines\n\n如果想操作并将一个暂停 lambda 委托给操作员，可以使用 `suspendOperator` 扩展和 `ApiResponseSuspendOperator` 类\n\n#### ViewModel\n\n可以在 `success` lambda 中使用像 `emit` 这样的挂起函数。\n\n```kotlin\nflow {\n  disneyService.fetchDisneyPosterList().suspendOperator(\n      CommonResponseOperator(\n        success = {\n          emit(data)\n          Timber.d(\"success data: $data\")\n        },\n        application = getApplication()\n      )\n    )\n}.flowOn(Dispatchers.IO)\n```\n\n#### CommonResponseOperator\n\n`CommonResponseOperator` 使用挂起覆盖方法扩展了 `ApiResponseSuspendOperator`。\n\n```kotlin\nclass CommonResponseOperator\u003cT\u003e constructor(\n  private val success: suspend (ApiResponse.Success\u003cT\u003e) -\u003e Unit,\n  private val application: Application\n) : ApiResponseSuspendOperator\u003cT\u003e() {\n\n  // handles the success case when the API request gets a successful response.\n  override suspend fun onSuccess(apiResponse: ApiResponse.Success\u003cT\u003e) = success(apiResponse)\n\n  // ... //\n```\n\n### Global operator\n\n也可以使用 `GinInitializer` 在应用程序中对所有 `ApiResponse` 全局操作运算符。所以不需要创建 Operators 的每个实例或使用依赖注入来处理常见的操作。这是一个处理关于 `ApiResponse.Failure.Error` 和 `ApiResponse.Failure.Exception` 的全局运算符的示例。在本例中，我们将手动处理 `ApiResponse.Success`。\n\n#### Application class\n\nWe can initialize the global operator on the `GinInitializer.sandwichOperator`. It is recommended to initialize it in the Application class.\n\n```kotlin\nclass GinDemoApp : Application() {\n\n  override fun onCreate() {\n    super.onCreate()\n    \n    // We will handle only the error and exceptional cases,\n    // so we don't need to mind the generic type of the operator.\n    GinInitializer.sandwichOperator = GlobalResponseOperator\u003cAny\u003e(this)\n\n    // ... //\n```\n\n#### GlobalResponseOperator\n\n`GlobalResponseOperator` 可以扩展任何运算符（`ApiResponseSuspendOperator` 或 `ApiResponseOperator`）\n\n```kotlin\nclass GlobalResponseOperator\u003cT\u003e constructor(\n  private val application: Application\n) : ApiResponseSuspendOperator\u003cT\u003e() {\n\n  // The body is empty, because we will handle the success case manually.\n  override suspend fun onSuccess(apiResponse: ApiResponse.Success\u003cT\u003e) { }\n\n  // handles error cases when the API request gets an error response.\n  // e.g., internal server error.\n  override suspend fun onError(apiResponse: ApiResponse.Failure.Error\u003cT\u003e) {\n    withContext(Dispatchers.Main) {\n      apiResponse.run {\n        Timber.d(message())\n\n        // handling error based on status code.\n        when (statusCode) {\n          StatusCode.InternalServerError -\u003e toast(\"InternalServerError\")\n          StatusCode.BadGateway -\u003e toast(\"BadGateway\")\n          else -\u003e toast(\"$statusCode(${statusCode.code}): ${message()}\")\n        }\n\n        // map the ApiResponse.Failure.Error to a customized error model using the mapper.\n        map(ErrorEnvelopeMapper) {\n          Timber.d(\"[Code: $code]: $message\")\n        }\n      }\n    }\n  }\n\n  // handles exceptional cases when the API request gets an exception response.\n  // e.g., network connection error, timeout.\n  override suspend fun onException(apiResponse: ApiResponse.Failure.Exception\u003cT\u003e) {\n    withContext(Dispatchers.Main) {\n      apiResponse.run {\n        Timber.d(message())\n        toast(message())\n      }\n    }\n  }\n\n  private fun toast(message: String) {\n    Toast.makeText(application, message, Toast.LENGTH_SHORT).show()\n  }\n}\n```\n\n#### ViewModel\n\n不需要使用 `operator` 表达式。全局操作符会自动操作，所以应该只处理 `ApiResponse.Success`。\n\n```kotlin\nflow {\n  disneyService.fetchDisneyPosterList().\n    suspendOnSuccess {\n      emit(data)\n    }\n}.flowOn(Dispatchers.IO).asLiveData()\n```\n\n### Merge\n\n可以根据策略将多个 `ApiResponse` 合并为一个 `ApiResponse`。\u003cbr\u003e\n如果每三个 `ApiResponse` 成功，则下面的示例将三个 `ApiResponse` 合并为一个。\n\n```kotlin\ndisneyService.fetchDisneyPosterList(page = 0).merge(\n   disneyService.fetchDisneyPosterList(page = 1),\n   disneyService.fetchDisneyPosterList(page = 2),\n   mergePolicy = ApiResponseMergePolicy.PREFERRED_FAILURE\n).onSuccess { \n  // handles the success case when the API request gets a successful response.\n}.onError { \n  // handles error cases when the API request gets an error response.\n}\n```\n\n#### ApiResponseMergePolicy\n\n`ApiResponseMergePolicy` 是用于合并响应数据的策略，取决于成功与否。\n\n- IGNORE_FAILURE: 无论合并顺序如何，都会忽略响应中的失败响应。\n- PREFERRED_FAILURE (default): 无论合并顺序如何，在响应中任何失败都会响应。\n\n### toLiveData\n\n如果响应是“ApiResponse.Success”，可以获得包含成功数据的“LiveData”。如果目标只是获得一个保存成功数据的 LiveData，可以发出 `onSuccess` 扩展。\n\n```kotlin\nposterListLiveData = liveData(viewModelScope.coroutineContext + Dispatchers.IO) {\n  emitSource(\n    disneyService.fetchDisneyPosterList()\n     .onError {\n      // handles error cases when the API request gets an error response.\n     }.onException {\n      // handles exceptional cases when the API request gets an exception response.\n     }.toLiveData()) // returns an observable LiveData\n}\n```\n\n如果想要转换原始数据并使用成功数据获取包含转换数据的“LiveData”，如果响应是“ApiResponse.Success”。\n\n```kotlin\nposterListLiveData = liveData(viewModelScope.coroutineContext + Dispatchers.IO) {\n  emitSource(\n   disneyService.fetchDisneyPosterList()\n    .onError {\n      // handles error cases when the API request gets an error response.\n    }.onException {\n      // handles exceptional cases when the API request gets an exception response.\n    }.toLiveData {\n      this.onEach { poster -\u003e poster.date = SystemClock.currentThreadTimeMillis() }\n    }) // returns an observable LiveData\n    }\n```\n\n### toFlow\n\n如果响应是 `ApiResponse.Success` 并且数据不为空，我们可以获得发出成功数据的 `Flow`。\n\n```kotlin\ndisneyService.fetchDisneyPosterList()\n  .onError {\n    // handles error cases when the API request gets an error response.\n  }.onException {\n    // handles exceptional cases when the API request gets an exception response.\n  }.toFlow() // returns a coroutines flow\n  .flowOn(Dispatchers.IO)\n```\n\n如果想要转换原始数据并使用成功数据获取包含转换数据的“流”，如果响应是“ApiResponse.Success”并且数据不为空。\n\n```kotlin\nval response = pokedexClient.fetchPokemonList(page = page)\nresponse.toFlow { pokemons -\u003e\n  pokemons.forEach { pokemon -\u003e pokemon.page = page }\n  pokemonDao.insertPokemonList(pokemons)\n  pokemonDao.getAllPokemonList(page)\n}.flowOn(Dispatchers.IO)\n```\n\n### ResponseDataSource\n\nResponseDataSource 是 `DataSource` 接口的实现。 \u003cbr\u003e\n\n * 异步发送请求。 \n * 来自 REST API 调用的临时响应数据持有者，用于在内存中缓存数据。\n * 可观察到每个响应。 \n * 请求失败时重试获取数据。\n * 连接另一个 `DataSource` 并按顺序请求。\n * 单次执行\n\n #### Combine\n\n结合 `Call` 和 lambda 范围来构造 DataSource。\n\n```kotlin\nval disneyService = retrofit.create(DisneyService::class.java)\n\nval dataSource = ResponseDataSource\u003cList\u003cPoster\u003e\u003e()\ndataSource.combine(disneyService.fetchDisneyPosterList()) { response -\u003e\n    // stubs\n}\n```\n\n#### Request\n\n异步请求 API 网络调用。 \u003cbr\u003e如果请求成功，此数据源将保存成功响应模型。\u003cbr\u003e在成功后的下一个请求中，request() 返回缓存的 API 响应。 \u003cbr\u003e如果我们需要获取新的响应数据或刷新，我们可以使用 `invalidate()`。\n\n```kotlin\ndataSource.request()\n```\n\n#### Retry\n\n如果您的请求失败，请重试获取数据（重新请求）。\n\n```kotlin\n// retry fetching data 3 times with 5000 milli-seconds time interval when the request gets failure.\ndataSource.retry(3, 5000L)\n```\n\n#### ObserveResponse\n\n观察 API 调用请求中的每个响应数据“ApiResponse”。\n\n```kotlin\ndataSource.observeResponse {\n   Timber.d(\"observeResponse: $it\")\n}\n```\n\n#### RetainPolicy\n\n可以限制在临时内部存储上保留数据的策略。\u003cbr\u003e\n默认策略是不保留任何从网络获取的数据，但我们可以使用 `dataRetainPolicy` 方法设置策略。\n\n```kotlin\n// Retain fetched data on the memory storage temporarily.\n// If request again, returns the retained data instead of re-fetching from the network.\ndataSource.dataRetainPolicy(DataRetainPolicy.RETAIN)\n```\n\n#### Invalidate\n\n使缓存（保存）数据无效并重新获取 API 请求\n\n```kotlin\ndataSource.invalidate()\n```\n\n#### Concat\n\n如果 API 调用成功，则连接另一个 `DataSource` 并按顺序请求 API 调用。\n\n```kotlin\nval dataSource2 = ResponseDataSource\u003cList\u003cPosterDetails\u003e\u003e()\ndataSource2.retry(3, 5000L).combine(disneyService.fetchDetails()) {\n    // stubs handling dataSource2 response\n}\n\ndataSource1\n   .request() // request() must be called before concat. \n   .concat(dataSource2) // request dataSource2's API call after the success of the dataSource1.\n   .concat(dataSource3) // request dataSource3's API call after the success of the dataSource2.\n```\n\n#### asLiveData\n\n可以通过 `DataSource` 观察获取的数据作为 `LiveData`。\n\n```kotlin\nval posterListLiveData: LiveData\u003cList\u003cPoster\u003e\u003e\n\ninit {\n    posterListLiveData = disneyService.fetchDisneyPosterList().toResponseDataSource()\n      .retry(3, 5000L)\n      .dataRetainPolicy(DataRetainPolicy.RETAIN)\n      .request {\n        // ... //\n      }.asLiveData()\n}\n```\n\n#### Disposable\n\n可以使用 `joinDisposable` 函数将其作为一次性用品加入到 `CompositeDisposable` 上。它必须在 `request()` 方法之前调用。下面的示例在 ViewModel 中使用。我们可以在 `onCleared()` 覆盖方法中清除 `CompositeDisposable`。\n\n```kotlin\nprivate val disposable = CompositeDisposable()\n\ninit {\n    disneyService.fetchDisneyPosterList().toResponseDataSource()\n      // retry fetching data 3 times with 5000L interval when the request gets failure.\n      .retry(3, 5000L)\n      // joins onto CompositeDisposable as a disposable and dispose onCleared().\n      .joinDisposable(disposable)\n      .request {\n        // ... //\n      }\n}\n\noverride fun onCleared() {\n    super.onCleared()\n    if (!disposable.disposed) {\n      disposable.clear()\n    }\n  }\n```\n\n这是 `MainViewModel` 中的 `ResponseDataSource` 的示例。\n\n```kotlin\nclass MainViewModel constructor(\n  private val disneyService: DisneyService\n) : ViewModel() {\n\n  // request API call Asynchronously and holding successful response data.\n  private val dataSource = ResponseDataSource\u003cList\u003cPoster\u003e\u003e()\n\n  val posterListLiveData = MutableLiveData\u003cList\u003cPoster\u003e\u003e()\n  val toastLiveData = MutableLiveData\u003cString\u003e()\n  private val disposable = CompositeDisposable()\n\n  /** fetch poster list data from the network. */\n  fun fetchDisneyPosters() {\n    dataSource\n      // retry fetching data 3 times with 5000 time interval when the request gets failure.\n      .retry(3, 5000L)\n      // joins onto CompositeDisposable as a disposable and dispose onCleared().\n      .joinDisposable(disposable)\n      // combine network service to the data source.\n      .combine(disneyService.fetchDisneyPosterList()) { response -\u003e\n        // handles the success case when the API request gets a successful response.\n        response.onSuccess {\n          Timber.d(\"$data\")\n          posterListLiveData.postValue(data)\n        }\n          // handles error cases when the API request gets an error response.\n          // e.g. internal server error.\n          .onError {\n            Timber.d(message())\n\n            // handling error based on status code.\n            when (statusCode) {\n              StatusCode.InternalServerError -\u003e toastLiveData.postValue(\"InternalServerError\")\n              StatusCode.BadGateway -\u003e toastLiveData.postValue(\"BadGateway\")\n              else -\u003e toastLiveData.postValue(\"$statusCode(${statusCode.code}): ${message()}\")\n            }\n\n            // map the ApiResponse.Failure.Error to a customized error model using the mapper.\n            map(ErrorEnvelopeMapper) {\n              Timber.d(this.toString())\n            }\n          }\n          // handles exceptional cases when the API request gets an exception response.\n          // e.g. network connection error, timeout.\n          .onException {\n            Timber.d(message())\n            toastLiveData.postValue(message())\n          }\n      }\n      // observe every API request responses.\n      .observeResponse {\n        Timber.d(\"observeResponse: $it\")\n      }\n      // request API network call asynchronously.\n      // if the request is successful, the data source will hold the success data.\n      // in the next request after success, returns the cached API response.\n      // if you want to fetch a new response data, use invalidate().\n      .request()\n  }\n\n  override fun onCleared() {\n    super.onCleared()\n    if (!disposable.disposed) {\n      disposable.clear()\n    }\n  }\n}\n```\n\n### DataSourceCallAdapterFactory\n\n我们可以直接从 Retrofit 服务获取 `DataSource`。 \u003cbr\u003e 将调用适配器工厂 `DataSourceCallAdapterFactory` 添加到您的 Retrofit 构建器。 \u003cbr\u003e 并将服务`Call`的返回类型更改为`DataSource`。\n\n```kotlin\nRetrofit.Builder()\n    ...\n    .addCallAdapterFactory(DataSourceCallAdapterFactory.create())\n    .build()\n\ninterface DisneyService {\n  @GET(\"DisneyPosters.json\")\n  fun fetchDisneyPosterList(): DataSource\u003cList\u003cPoster\u003e\u003e\n}\n```\n\n这里 MainViewModel 中的“DataSource”示例。\n\n```kotlin\nclass MainViewModel constructor(disneyService: DisneyService) : ViewModel() {\n\n  // request API call Asynchronously and holding successful response data.\n  private val dataSource: DataSource\u003cList\u003cPoster\u003e\u003e\n\n    init {\n    Timber.d(\"initialized MainViewModel.\")\n\n    dataSource = disneyService.fetchDisneyPosterList()\n      // retry fetching data 3 times with 5000L interval when the request gets failure.\n      .retry(3, 5000L)\n      .observeResponse(object : ResponseObserver\u003cList\u003cPoster\u003e\u003e {\n        override fun observe(response: ApiResponse\u003cList\u003cPoster\u003e\u003e) {\n          // handle the case when the API request gets a success response.\n          response.onSuccess {\n            Timber.d(\"$data\")\n            posterListLiveData.postValue(data)\n          }\n        }\n      })\n      .request() // must call request()\n```\n\n### CoroutinesDataSourceCallAdapterFactory\n\n可以使用 `suspend` 直接从 Retrofit 服务获取`DataSource`。 \u003cbr\u003e\n\n```kotlin\nRetrofit.Builder()\n    ...\n    .addCallAdapterFactory(CoroutinesDataSourceCallAdapterFactory.create())\n    .build()\n\ninterface DisneyService {\n  @GET(\"DisneyPosters.json\")\n  fun fetchDisneyPosterList(): DataSource\u003cList\u003cPoster\u003e\u003e\n}\n```\n\n这是 MainViewModel 中的 `DataSource` 的示例。\n\n```kotlin\nclass MainCoroutinesViewModel constructor(disneyService: DisneyCoroutinesService) : ViewModel() {\n\n  val posterListLiveData: LiveData\u003cList\u003cPoster\u003e\u003e\n\n  init {\n    Timber.d(\"initialized MainViewModel.\")\n\n    posterListLiveData = liveData(viewModelScope.coroutineContext + Dispatchers.IO) {\n      emitSource(disneyService.fetchDisneyPosterList().toResponseDataSource()\n        // retry fetching data 3 times with 5000L interval when the request gets failure.\n        .retry(3, 5000L)\n        // a retain policy for retaining data on the internal storage\n        .dataRetainPolicy(DataRetainPolicy.RETAIN)\n        // request API network call asynchronously.\n        .request {\n          // handle the case when the API request gets a success response.\n          onSuccess {\n            Timber.d(\"$data\")\n          }.onError { // handle the case when the API request gets a error response.\n              Timber.d(message())\n            }.onException {  // handle the case when the API request gets a exception response.\n              Timber.d(message())\n            }\n        }.asLiveData())\n    }\n  }\n}\n```\n\n#### toResponseDataSource\n\n使用下面的方法从网络调用中获取实例后，我们可以将 `DataSource` 更改为 `ResponseDataSource`。\n\n```kotlin\nprivate val dataSource: ResponseDataSource\u003cList\u003cPoster\u003e\u003e\n\n  init {\n    dataSource = disneyService.fetchDisneyPosterList().toResponseDataSource()\n\n    //...\n  }\n```\n\n\n## Find this library useful? :heart:\n\n# License\n\n```xml\nCopyright 2020 wzasd (Jeffrey wang)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwzasd%2Fgin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwzasd%2Fgin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwzasd%2Fgin/lists"}