Skip to content
rongc edited this page Jul 10, 2021 · 1 revision

公开网络状态

数据的请求是一个耗时过程,比较友好的做法是在开始请求时显示加载提醒弹窗,结束后在关闭弹窗。在Resource中请求的状态分为以下3种:

  1. 请求中(LOADING);
  2. 请求成功(SUCCESS);
  3. 请求失败(ERROR);

整个远程数据源的请求过程都发生在NetworkBoundResource 中,NetworkBoundResource中有一个名为result的LiveData,类型为Resource,在请求过程中状态变更时都将通过result及时向外发出通知。

一、LOADING

在每个请求开始之初到结果返回前,请求的状态都是LOADING。

 init {
    result.value = Resource.loading(null)
    ...
 }

二、ApiResponse结果封装

  1. Http 相关配置中,除了向HttpProvider提供的CallAdapterFactory外,内部还添加了支持LiveData作为数据源的CallAdapter,在定义后端接口时可以使用LiveData作为方法返回值:
interface WanService {

    @GET("banner/json")
    fun getBanner(): LiveData<ApiResponse<List<WanBanner>>>
}

但LiveData的直接泛型是ApiResponse而不是List,是因为我们还需要处理异常的情况。

ApiResponse密封类中定义了以下三种情况:

class ApiEmptyResponse<T> : ApiResponse<T>()

data class ApiSuccessResponse<T>(val body: T) : ApiResponse<T>()

data class ApiErrorResponse<T>(val error: Throwable) : ApiResponse<T>()
  • ApiEmptyResponse:HTTP 204或无法获取到非空的结果。
  • ApiSuccessResponse(body: T):请求正常。
  • ApiErrorResponse(error: Throwable):请求发生错误。

在得到请求结果后,LiveDataCallAdapter会把获取到的请求结果按情况封装成上述三种状态并通过LiveData发送出来,结果将会在NetworkBoundResource接收并做进一步转换。

三、SUCCESS/ERROR

NetworkBoundResource在接收到LiveDataCallAdapter发出请求结果后分析ApiResponse的状态,内容为空和请求成功都视为请求成功,只不过内容为空时使用本地数据。

   when (response) {
       is ApiSuccessResponse<*> -> {
            ... 
            result = Resource.success(response.body)
       }
       is ApiEmptyResponse<*> -> {
            ... 
            result = Resource.success(loadFromDb())
       }
       is ApiErrorResponse<*> -> {
           ...
           result = Resource.error(response.error, dbSource)
       }
   }

四、订阅

class RepoSearchFragment : BaseFragment<FragmentListBinding, RepoSearchViewModel>() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         viewModel.getRepos().observe(viewLifecycleOwner) {
            // 在加载中显示弹窗,结束时自动关闭弹窗
            showProgressIfLoading(it)
            when (it.status) {
               Status.LOADING -> {
                  // 加载中状态, 也可能会有数据(本地的)
                  it.data
               }
               Status.SUCCESS -> { it.data }
               Status.ERROR -> { 
                  // 加载失败, 错误区分业务错误和网络请求错误,具体可看Http
                  val error = it.error as? ServiceExeption
                  if (error?.code == 4403) {
                     "金币不足".toast()
                  }
               }
            }
         }
    }
}

扩展方法:

  1. 忽略加载中状态,只接收结果通知
viewModel.getRepos().ignoreLoading(viewLifecycleOwner) {

}
  1. 只在成功时接收事件
viewModel.getRepos().whenSuccess(viewLifecycleOwner) {
}

Clone this wiki locally