Skip to content

Retrofit

kirtan403 edited this page Feb 23, 2018 · 3 revisions

K4Kotlin's retrofit module provides classes and methods to help write easy, efficient and understandable code.

Scope retrofit call to Activity/Fragment

Many of times when you call any services that are bound to show some results on the screen are limited to the current activity/fragment you are calling from. For this purpose, we should call the cancel method to the Retrofit's Call instance for cancelling the request when the activity/fragment is destroyed.

To achieve this, you can pass the activity/fragment (or any instance implementing LifecycleOwner interface) to the enqueue method:

retrofitInstance.getPosts().enqueue(this@MainActivity, Callback<PostResp> {
   // handle callback
})

Note: Recent release of support library implements the lifecycler owner interface to AppCompatActivity and Fragment. Make sure to use that. You activity extending Activity will not work.

Rich Callback Handling with RetrofitCallback

RetrofitCallback is the class implementing Retrofit's Callback<T> interface and provide much more rich handling of different types of responses. It also uses DSL-like syntax for easy writing callbacks with lambdas.

There are many callback types available. Make sure you learn to understand them so that you don't mix it with duplicates. Don't worry they are easy to understand.

  • onCompleted(call, response, throwable)

    • Called irrespective of the result (onResponse or onFailure)
    • Helpful when you need to handle something like hiding a progress bar when call is complete
  • onResponseCallback(call, response)

    • Called after onCompleted callback and the same as Retrofit's own onResponse
  • onFailureCallback(call, response)

    • Called after onCompleted callback and the same as Retrofit's own onFailure
  • onCancelled(call, throwable) and onFailureNotCancelled(call, throwable)

    • Called after onFailureCallback when there is a onFailure callback from the Retrofit.
    • If the call is cancelled by calling call.cancel(), onCancelled callback will be called. Else, onFailureNotCancelled will be called.
  • on2xxSuccess , on3xxRedirection , on4xxClientError, on5xxServerError

    • Called after onResponseCallback when there is a onResponse callback from Retrofit.
    • The one method from these 4 will be called based on the response code. For example, on2xxSuccess will be called when response code is in between 200-299 including both the numbers. Same will be for others.
  • onUnsuccessfulResponse

    • This will be called if response is received and it is not successful (Not between 200..299).
  • onUnsuccessfulResponseOrFailure

    • This will be called if response is unsuccessful or onFailure is called from Retrofit.
    • To understand simply, this will be called if response code is not between 200..299.
  • onUnsuccessfulResponseOrFailureNotCancelled

    • This will be called if (response is unsuccessful) or (onFailure is called from Retrofit and is not a cancelled call)
  • Callbacks for specific response code for all HTTP reponse codes

    • This will be called after any group response callbacks like on2xxSuccess

    • There are methods for all the HTTP response codes. The list is:

      • on200Ok,on201Created,on202Accepted,on203NonAuthoritativeInformation,on204NoContent,on205ResetContent,on206PartialContent,on207MultiStatus,on208AlreadyReported,on226ImUsed,on300MultipleChoices,on301MovedPermanently,on302Found,on303SeeOther,on304NotModified,on305UseProxy,on306SwitchProxy,on307TemporaryRedirect,on308PermanentRedirect,on400BadRequest,on401Unauthorized,on402PaymentFailed,on403Forbidden,on404NotFound,on405MethodNotAllowed,on406NotAcceptable,on407ProxyAuthenticationRequired,on408RequestTimeout,on409Conflict,on410Gone,on411LengthRequired,on412PreconditionFailed,on413PayloadTooLarge,on414UriTooLong,on415UnsupportedMediaType,on416RangeNotSatisfiable,on417ExpectationFailed,on418ImaTeapot,on421MisdirectedRequest,on422UnprocessableEntity,on423Locked,on424FailedDependency,on426UpgradeRequired,on428PreconditionRequired,on429TooManyRequests,on431RequestHeaderFieldsTooLarge,on451UnavailableForLegalReasons,on500InternalServerError,on501NotImplemented,on502BadGateway,on503ServiceUnavailable,on504GatewayTimeout,on505HttpVersionNotSupported,on506VariantAlsoNegotiates,on507InsufficientStorage,on508LoopDetected,on510NotExtended,on511NetworkAuthenticationRequired

There methods are called based on the order they are mentioned above.

There is also 2 properties to control the progress bar (or similar view) while the call is in progress.

  • progressView : This will be shown when you set this and will hide itself when the call is complete
  • lazyProgressView: This view will not be shown automatically when you initialize callback (Useful is cases when callback instance is declared before it may actually called). But, will hide itself when call is finished.

Example:

Retrofit callback can be created in DSL like syntax.

retrofit.getPosts().enqueue(this@MainActivity, RetrofitCallback {

  // this will be shown on callback init and hide itself when call is complete
  progressView = mBinding.progressBar

  onCompleted { call, response, thorwable ->
    // managing something when call is complete by any mean
  }

  on2xxSuccess { call, response ->
    // yay, successful call
  }
  
  on4xxClientError { call, response -> 
   	// something is wrong from client side 
  }
  
  on200Ok { call, response ->
  	// The perfect request. nice
  }
  
  onFailureNotCancelled { call, t -> 
  	// something went wrong, opps!
  }
  
  // and so on.. 
  
})

Notes: There may be multiple callbacks based on the methods you have defined. Like, if you have defined on2xxSuccess and on200Ok methods, and response code is 200, then both will be called. But the order will be first on2xxSuccess and then on200Ok.

Retrofit Callbacks with Coroutines [Experimental]

You can call the retrofit enqueue function in various ways with Coroutines. There are currently 3 functions you can call with coroutines which enables you to write some kind of synchronous API calls without blocking UI thread.

enqueueAwait ( lifecycleOwner [Optional], callback [Optional]):
async(UI) {
  	val result = ApiClient.service.getUserDetails().enqueueAwait(
       			this@RetrofitActivity,
       			RetrofitCallback {
                    progressView = mBinding.progressBar

                    onResponseCallback { call, response ->
                        mBinding.tvInfo.append("Response Received \n")
                 }

             })
	Log.d(TAG, "Result is here: " + result)
}

OR

async(UI) {
	val result = ApiClient.service.getUserDetails().enqueueAwait()
	Log.d(TAG, "Result is here: " + result)
}

You can optionally pass the lifecycleOwner to scope the Retrofit calls and RetrofitCallback to handle or check for any response type or error validation.

This typically make the Retrofit call and wait for the response. After response is received, it returns the response body (In result , in above example).

enqueueDeferred ( lifecycleOwner [Optional], callback [Optional]):
async(UI) {
  	val result = ApiClient.service.getUserDetails().enqueueDeferred(
       			this@RetrofitActivity,
       			RetrofitCallback {
                    progressView = mBinding.progressBar

                    onResponseCallback { call, response ->
                        mBinding.tvInfo.append("Response Received \n")
                 }

             })
     // do some other things you want to do before waiting for the result to come
	Log.d(TAG, "Result is here: " + result.await())
}

OR

async(UI) {
	val result = ApiClient.service.getUserDetails().enqueueDeferred()
	// do some other things you want to do before waiting for the result to come
	Log.d(TAG, "Result is here: " + result.await())
}

You can optionally pass the lifecycleOwner to scope the Retrofit calls and RetrofitCallback to handle or check for any response type or error validation.

This typically make the Retrofit call and return its deferred type immediately. You can do whatever the task you want to perform and then call await() on it whenever you want the result. It will return the value as soon as the value will be available and continue execution from that point.

enqueueDeferredResponse ( lifecycleOwner [Optional], callback [Optional]):
async(UI) {
  	val result = ApiClient.service.getUserDetails().enqueueDeferredResponse(
       			this@RetrofitActivity,
       			RetrofitCallback {
                    progressView = mBinding.progressBar

                    onResponseCallback { call, response ->
                        mBinding.tvInfo.append("Response Received \n")
                 }

             })
     // do some other things you want to do before waiting for the result to come
	Log.d(TAG, "Result is here: " + result.await().body())
}

OR

async(UI) {
	val result = ApiClient.service.getUserDetails().enqueueDeferredResponse()
	// do some other things you want to do before waiting for the result to come
	Log.d(TAG, "Result is here: " + result.await().body())
}

You can optionally pass the lifecycleOwner to scope the Retrofit calls and RetrofitCallback to handle or check for any response type or error validation.

This is same as enqueueDeferred(), but the only difference is, it returns the response instance and the body of the response.

Note: You can find more examples in the sample app from the repository