从 ViewModel 采集 Flow 和直接在 Compose 采集 Flow 应该选哪个

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

代码 A 在 ViewModel, 中收集 Flow 并使用 MutableStateFlow 将 Flow 转换为热流,然后在 Compose 中使用 collectAsState()

代码B在Compose中收集Flow,直接在Compose中使用collectAsState

代码A和代码B哪个更好?

在我看来,代码A在ViewModel中将一个Flow转换为hot Flow,它会保留内存并浪费资源,这是一个好方法吗?

代码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>>> {
  ...
}

代码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>>> {      
        ...
    }
}

代码 A 中,您要保持流向视图模型。因此,该流程将在用户旋转设备的情况下被保存。此外,如果 BookListScreen.

中的重组,则不会调用 loadBooks

代码 B 中,每次发生重组时都会调用 listRecord,因此每次重绘屏幕时,您都在进行数据库调用。由于您的数据库是本地的,因此损坏看起来并没有那么严重,但是如果您使用的是远程数据库(如 Firebase 或 REST Web 服务),此代码将在每次重组时执行网络调用,包括当您旋转设备或返回时到这个屏幕。

检查一下this Playlist from Android Developers Channel. In particular this video(更侧重于您的问题)。