Search code examples
androidretrofit2android-jetpack

How to send offline data to server once network has been connected?


I'm trying to figure out how to send offline data to the server using a POST request when the device is connected to wifi or data.

How do I implement this process?


Solution

  • For that it will be necessary to monitor when the user will be connected. An easy way is using the ConnectivityManager, but the minSdk must be 24 or higher.

    Note: I'm assuming you're using Kotlin because you didn't specify the language but used the Compose tag before post edit.

    enum class ConnectivityStatus {
        Available, Losing, Lost, Unavailable
    }
    
    class ConnectivityObserver(context: Context) {
        private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
                as ConnectivityManager
    
        fun observe(): Flow<ConnectivityStatus> = callbackFlow {
            val networkCallback = object : ConnectivityManager.NetworkCallback() {
                override fun onAvailable(network: Network) {
                    super.onAvailable(network)
                    launch { send(ConnectivityStatus.Available) }
                }
    
                override fun onLosing(network: Network, maxMsToLive: Int) {
                    super.onLosing(network, maxMsToLive)
                    launch { send(ConnectivityStatus.Losing) }
                }
    
                override fun onLost(network: Network) {
                    super.onLost(network)
                    launch { send(ConnectivityStatus.Lost) }
                }
    
                override fun onUnavailable() {
                    super.onUnavailable()
                    launch { send(ConnectivityStatus.Unavailable) }
                }
            }
    
            connectivityManager.registerDefaultNetworkCallback(networkCallback)
    
            awaitClose {
                connectivityManager.unregisterNetworkCallback(networkCallback)
            }
        }.distinctUntilChanged()
    }
    

    In this code example I was using an enum class with some possible connection states and a class that returns a flow with the respective states according to the user's connection through ConnectivityManager.NetworkCallback.

    Now we can observe the states (preferably inside a ViewModel) and trigger the appropriate actions.

    // class to handle Retrofit methods
    class MyRepository {
        suspend fun doPost() {
            // post something
        }
    }
    
    // data class to hold states
    data class MyState(
        val isLoading: Boolean = false,
        val hasPendingActions: Boolean = false,
        val connectivityStatus: ConnectivityStatus = ConnectivityStatus.Unavailable,
        // and so on...
    )
    
    class MyViewModel(
        private val connectivityObserver: ConnectivityObserver,
        private val myRepository: MyRepository
    ) : ViewModel() {
        var state by mutableStateOf(MyState())
            private set
    
        init {
            observeConnectivity()
        }
    
        // to call on screen
        fun doThings() {
            if (state.connectivityStatus == ConnectivityStatus.Available) {
                state = state.copy(isLoading = true)
                myRepository.doPost()
            } else state = state.copy(hasPendingActions = true)
        }
    
        private fun observeConnectivity() = viewModelScope.launch {
            connectivityObserver.observe().collect { status ->
                when (status) {
                    ConnectivityStatus.Available -> {
                        state = state.copy(connectivityStatus = status)
                        if (state.hasPendingActions) {
                            state = state.copy(isLoading = true, hasPendingActions = false)
                            myRepository.doPost()
                        }
                    }
                    else -> {
                        state = state.copy(connectivityStatus = status)
                    }
                }
            }
        }
    }
    

    Here is a YouTube video where I took the code example using the ConnectivityManager with callbackFlow.