HelloCW
HelloCW

Reputation: 2355

Which one should I choose between collecting Flow from ViewModel or collecting Flow in Compose directly

The code A collect Flow in ViewModel, and convert a Flow as hot Flow using MutableStateFlow, then use collectAsState() in Compose.

The code B collect Flow in Compose, and use collectAsState in Compose directly.

Which one is the better between code A and code B ?

In my mind, the code A convert a Flow as hot Flow in ViewModel, it will retain memeory and waster resources, is it a good way?

Code A

@Composable
fun BookListScreen(
    viewModel: BookListViewModel,
    ...
) {
    val booksListUiState by viewModel.uiState.collectAsState()
    ...
}


@HiltViewModel
class BookListViewModel @Inject constructor(
    private val bookUseCase: BookUseCase
) : ViewModel() {
    private var loadBooksJob: Job? = null
    
    private val _uiState = MutableStateFlow(BookListUiState())
    val uiState = _uiState.asStateFlow()

    init {
        loadBooks()
    }

    fun loadBooks() {
        loadBooksJob?.cancel()
        loadBooksJob = viewModelScope.launch {
            bookUseCase.listBooks().collect { resultState ->
                _uiState.update {
                    it.copy(bookListState = resultState)
                }
            }
        }
    }
    ...
}


override fun loadBooks(): Flow<ResultState<List<Book>>> {
  ...
}

Code B

@Composable
fun Greeting(
    name: String,
    mViewMode:SoundViewModel= viewModel()
) {
    Column(
        
    ) {
     val myResult by mViewMode.listRecord().collectAsState(initial =Result.Error(Exception()) )

     ..
}


@HiltViewModel
class SoundViewModel @Inject constructor(
    private val aSoundMeter: RecordRepository
): ViewModel()
{
    fun listRecord(): Flow<Result<List<MRecord>>> {
        return  aSoundMeter.listRecord()
    }

}


@Dao
interface  RecordDao { 
    @Query("SELECT * FROM record_table ORDER BY createdDate desc")
    fun listRecord():  Flow<List<RecordEntity>>
}



class RecordRepository @Inject constructor(private val mRecordDao:RecordDao): IRecordRepository {
    override fun listRecord(): Flow<Result<List<MRecord>>> {      
        ...
    }
}

Upvotes: 1

Views: 1812

Answers (1)

nglauber
nglauber

Reputation: 24044

In Code A, you're keeping a flow into the view model. Therefore, this flow will be saved in case of the user rotates the device. Also, the loadBooks will not be called in case of a recomposition in BookListScreen.

In Code B, listRecord is called every time a recomposition happens, therefore every time the screen is redrawn you're doing a data base call. Since your DB is local, the damage does not look so bad, but if you're using a remote DB (like Firebase or REST web service) this code will perform a network call in every recomposition, including when you rotate the device or returning to this screen.

Check it out this Playlist from Android Developers Channel. In particular this video (which is more focused in your problem).

Upvotes: 3

Related Questions