Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update UI on async callbacks in Jetpack Compose

(I'm new to Android development, so this might be a basic question. I've searched the best I can.)

I'm relying on a service that updates data asynchronously and provides a callback when there is new data. I'm trying to figure out how to use this to update my UI whenever the data changes.

I've gone through the basic Jetpack Compose tutorials and I can build a UI with a button that updates a count. Based on that knowledge, the best idea I came up with is to create a MutableState in the ComponentActivity's init method. This is working, but I don't know if it's the right way to do this.

Here's my code (obfuscated a bit to remove private details):

class MainActivity : ComponentActivity() {

    // Get the updating value from the async service
    private val asyncService = getAsyncService()
    private val updatingValue = asyncService.getUpdatingValue()

    // Create a MutableState for the UI
    private val mutableValue = mutableStateOf(updatingValue.value)

    init {
        // Update the MutableState whenever the value changes
        updatingValue.onUpdate { newValue ->
            run {
                mutableValue.value = newValue
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ExampleComponent(mutableValue)
        }
    }
}

@Composable
fun ExampleComponent(value: MutableState<String>) {
    Text("The value is : ${value.value}")
}

Is this right or is there a better way to do this?

Update:

I wrote a ViewModel class like this and it works great:

class AsyncDataViewModel : ViewModel() {

    private val asyncService = getAsyncService()

    private val updatingValue = asyncService.getUpdatingValue()
    val value = mutableStateOf(updatingValue.value)

    init {
        updatingValue.onUpdate { updatedValue ->
            run {
                value.value = updatedValue.value
            }
        }
    }

}
like image 314
leros Avatar asked Dec 06 '25 13:12

leros


1 Answers

With Jetpack Compose, you should use a ViewModel to manage the data that is displayed, as explained in this lab. A ViewModel

  • retains the data even if configuration changes (e.g. rotation) occurs
  • provides easy access to a CoroutineScope to execute suspension functions asynchronously
  • allows to expose state to a Composable.

I provided a sample architecture below:

class MyViewModel(
    private val asyncService: AsyncService  // using Koin dependency injection
) : ViewModel() {

    private val _mutableValue = mutableStateOf("")  // for write operations
    val mutableValue: String = _mutableValue // for read operations

    init {
        this.loadValue()
    }

    fun loadValue() {
         viewModelScope.launch(Dispatchers.IO) {
             // You have a CoroutineScope here. You can execute async suspension functions here.
             // call suspension function in AsnycService class
             _mutableValue = asyncService.getUpdatingValue()  
         }
    }
}

Then, you can get your ViewModel in your Composable like this:

@Composable
fun MyScreen(
   amyViewModel: MyViewModel = viewModel()
) {
   Text("The value is : ${myViewModel.mutableValue}")
}

Your service class should use a suspension function. Your ViewModel will call this function as a Coroutine without blocking the UI thread and will update the value and refresh the value automatically:

class AsyncService {
    suspend fun getUpdatingValue(): String {
        delay(5000)  // some long running operation
        return "HELLO"  // value that is returned after operation  
    }
}

This way, you also get rid of specifying callback functions.

like image 133
BenjyTec Avatar answered Dec 08 '25 01:12

BenjyTec



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!