Mobile Development 10 min read

Kotlin Network Request Wrappers with LiveData: Two Approaches, Code Samples, and Comparison

This article explains two Kotlin‑based network request wrappers for Android that reduce boilerplate by using LiveData, Retrofit, OkHttp and coroutines, compares their design trade‑offs, provides complete Activity, ViewModel, Repository and BaseRepository code samples, and shows how to handle loading, success, error and multiple data sources in a clean MVVM architecture.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Kotlin Network Request Wrappers with LiveData: Two Approaches, Code Samples, and Comparison

Purpose

Provide a simple way to invoke network requests with minimal repetitive code, without relying on third‑party libraries (only Retrofit + OkHttp + coroutines), and make it usable even for developers unfamiliar with coroutines.

Implementation 1

Less code, request automatically shows a loading indicator, and does not require manual loading handling.

However, it tightly couples the LiveData to the whole request chain, making it harder to decouple UI and data sources.

Activity Code Example

mViewModel.wxArticleLiveData.observe(this, object : IStateObserver
>() {
    override fun onSuccess(data: List
?) { }
    override fun onError() { }
})

mViewModel.wxArticleLiveData.observeState(this) {
    onSuccess { data -> Log.i("wutao", "网络请求的结果是:$data") }
    onError { }
}

ViewModel Code Example

class MainViewModel {
    private val repository by lazy { WxArticleRepository() }
    val wxArticleLiveData = StateLiveData
>()
    fun requestNet() {
        viewModelScope.launch {
            repository.fetchWxArticle(wxArticleLiveData)
        }
    }
}

Repository Code Example

class WxArticleRepository : BaseRepository() {
    private val mService by lazy { RetrofitClient.service }
    suspend fun fetchWxArticle(stateLiveData: StateLiveData
>) {
        executeResp(stateLiveData, mService::getWxArticle)
    }
    interface ApiService {
        @GET("wxarticle/chapters/json")
        suspend fun getWxArticle(): BaseResponse
>
    }
}

Implementation 2

Eliminates the LiveData‑driven request chain, separates loading from the request, and returns results directly from the repository, making the architecture more decoupled and suitable for multiple data sources.

Activity Code Example

mViewModel.login("username", "password")

mViewModel.userLiveData.observeState(this) {
    onSuccess { data -> mBinding.tvContent.text = data.toString() }
    onComplete { dismissLoading() }
}

ViewModel Code Example

class MainViewModel {
    val userLiveData = StateLiveData
()
    fun login(username: String, password: String) {
        viewModelScope.launch {
            userLiveData.value = repository.login(username, password)
        }
    }
}

Repository Code Example

suspend fun login(username: String, password: String): ApiResponse
{
    return executeHttp { mService.login(username, password) }
}

BaseRepository – Unified HTTP Handling

open class BaseRepository {
    suspend fun
executeHttp(block: suspend () -> ApiResponse
): ApiResponse
{
        runCatching { block.invoke() }
            .onSuccess { data -> return handleHttpOk(data) }
            .onFailure { e -> return handleHttpError(e) }
        return ApiEmptyResponse()
    }
    private fun
handleHttpError(e: Throwable): ApiErrorResponse
{ /* ... */ }
    private fun
handleHttpOk(data: ApiResponse
): ApiResponse
{ /* ... */ }
    private fun
getHttpSuccessResponse(response: ApiResponse
): ApiResponse
{ /* ... */ }
}

LiveData and Observer Extensions

abstract class IStateObserver
: Observer
> {
    override fun onChanged(apiResponse: ApiResponse
) {
        when (apiResponse) {
            is ApiSuccessResponse -> onSuccess(apiResponse.response)
            is ApiEmptyResponse -> onDataEmpty()
            is ApiFailedResponse -> onFailed(apiResponse.errorCode, apiResponse.errorMsg)
            is ApiErrorResponse -> onError(apiResponse.throwable)
        }
        onComplete()
    }
    abstract fun onSuccess(data: T)
    abstract fun onError(e: Throwable)
    abstract fun onDataEmpty()
    abstract fun onFailed(errorCode: Int?, errorMsg: String?)
    abstract fun onComplete()
}

class StateLiveData
: MutableLiveData
>() {
    fun observeState(owner: LifecycleOwner, listenerBuilder: ListenerBuilder.() -> Unit) {
        val listener = ListenerBuilder().also(listenerBuilder)
        val observer = object : IStateObserver
() {
            override fun onSuccess(data: T) { listener.mSuccessListenerAction?.invoke(data) }
            override fun onError(e: Throwable) { listener.mErrorListenerAction?.invoke(e) ?: toast("Http Error") }
            override fun onDataEmpty() { listener.mEmptyListenerAction?.invoke() }
            override fun onFailed(errorCode: Int?, errorMsg: String?) { listener.mFailedListenerAction?.invoke(errorCode, errorMsg) }
            override fun onComplete() { listener.mCompleteListenerAction?.invoke() }
        }
        super.observe(owner, observer)
    }
}

Conclusion

Implementation 1 reduces code size and provides built‑in loading, but its tight coupling to UI limits flexibility and makes handling multiple data sources cumbersome.

Implementation 2 achieves better decoupling, works independently of UI modules, and handles multiple data sources more cleanly, at the cost of requiring explicit loading management.

Choose the approach that best fits your project’s needs; both aim to simplify network requests in Android MVVM architecture.

androidNetworkKotlinCoroutinesMVVMLiveDataRetrofit
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.