Error/Loading processing when using Retrofit+LiveData

At present, there are many projects that use Retrofit2+LiveData for API requests. Unlike RxJava, LiveData can easily process Error. Therefore, we want to encapsulate a tool class based on LiveData to uniformly process Error/Loading in API requests


open class HttpManager<T>(context: Context, serviceClass: Class<T>) {

    private val BASE_URL: String = context.getString(R.string.wallet_api_url)
    private var isLoading: MutableLiveData<Boolean>? = null
    private var error: MutableLiveData<ErrorResponse>? = null

    private val service = getRetrofit(

    fun init(
            isLoading: MutableLiveData<Boolean>? = null,
            error: MutableLiveData<ErrorResponse>? = null
    ):T {
        this.isLoading = isLoading
        this.error = error
        return service

    private fun createHeader(context: Context, request: Request): Request {
        return request.newBuilder()
                .addHeader("Accept", "application/json")

    private fun getHttpClient(context: Context) : OkHttpClient {
        val interceptor = Interceptor { chain ->
            val response =  chain.proceed(createHeader(context, chain.request()))
            if(response.code() != ErrorCode.HTTP_OK_200.httpErrorCode) {
                try {
                    val source =  response.body()?.source()
                    val bodyString = source?.buffer?.clone()?.readString(Charset.forName("UTF-8")).toString()
                    val errorBase = Gson().fromJson(bodyString,
                    error?.postValue(ErrorResponse(response.code(), errorBase))
                } catch (e: Exception) {
                    error?.postValue(ErrorResponse(response.code(), ErrorBase("Unknown Error", null)))

        return OkHttpClient.Builder()
                .eventListener(object: EventListener(){
                    override fun callStart(call: Call) {

                    override fun callEnd(call: Call) {
                .readTimeout(5, TimeUnit.SECONDS)
                .connectTimeout(5, TimeUnit.SECONDS)

    private fun getLoggingInterceptor(): HttpLoggingInterceptor {
        val logging = HttpLoggingInterceptor()
        if (BuildConfig.DEBUG) {
            logging.level = HttpLoggingInterceptor.Level.BODY
        } else {
            logging.level = HttpLoggingInterceptor.Level.NONE
        return logging

    private fun getRetrofit(serviceClass: Class<T>, baseUrl: String, httpClient: OkHttpClient) : T {
        val retrofit: Retrofit = Retrofit.Builder()
        return retrofit.create(serviceClass)

    private fun getConverter() : Converter.Factory {
        return GsonConverterFactory.create(GsonBuilder()

The main tasks of HttpManager class are as follows:

  1. Create Retofit based on apiService,
  2. Handling loading status through EventListener
  3. Handling Error through Interceptor


class ExampleApi(context: Context ) : HttpManager< ExampleApi. ExampleService (
    context, {
    interface ExampleService {
        fun setting(): Call<ExampleResponse>

    fun setting(responseLiveData: MutableLiveData<ExampleResponse>, 
                isLoading: MutableLiveData<Boolean>?, error: MutableLiveData<ErrorResponse>?) {
        GlobalScope.launch {
            init(isLoading, error)
                    .enqueue(object: Callback<ExampleResponse> {
                        override fun onFailure(call: Call<ExampleResponse>,
                                                 t: Throwable) {
                             //HttpManager is responsible for error handling, so nothing to do here
                        override fun onResponse(call: Call<ExampleResponse>,
                                            response: retrofit2.Response<ExampleResponse>) {
                            if (response.isSuccessful) {

    data class ExampleResponse(
            val hoge: Boolean,
            val fuga: String

The main work of ExampleApi:

  1. Provide the calling method of the API request and accept the LiveData parameter
  2. Inherit HttpManager and make Api request through Retrofit


val response = MutableLiveData<ExampleApi.ExampleResponse>()
val error = MutableLiveData<ErrorResponse>()
val isLoading = MutableLiveData<Boolean>()

WalletUserApi(this).setting(response, isLoading, error)

response.observeForever { result ->
    Toast.makeText(this, "response: $result", Toast.LENGTH_SHORT ).show()

error.observeForever { result ->
    Toast.makeText(this, "response: $result", Toast.LENGTH_SHORT ).show()

isLoading.observeForever { 
    //"Data binding" means "data binding" means "feeling"

Activity does simple things. Just call Api class and pass in LiveData.

LIveData can be put into View Model management. All fragments or customized views within the scope of Activity can easily and timely subscribe to the latest status of Api request process.

Published 12 original articles, won praise 1, visited 304
Private letter follow

Tags: Retrofit Java JSON

Posted on Wed, 29 Jan 2020 11:12:25 -0500 by burnside