簡體   English   中英

Compose 和 Room:使用存儲庫初始化 ViewModel 時出現問題

[英]Compose and Room: Issue with initializing ViewModel with a repository

我目前正在關注帶有 View codelab 的 Android Room,並嘗試將其與 Jetpack Compose 結合使用。 我堅持在 function 中初始化 viewModel。

我得到的錯誤:

None of the following functions can be called with the arguments supplied:

public inline fun <reified VM : ViewModel> viewModel(viewModelStoreOwner: ViewModelStoreOwner = ..., key: String? = ..., factory: ViewModelProvider.Factory? = ...): TypeVariable(VM) defined in androidx.lifecycle.viewmodel.compose

public fun <VM : ViewModel> viewModel(modelClass: Class<TypeVariable(VM)>, viewModelStoreOwner: ViewModelStoreOwner = ..., key: String? = ..., factory: ViewModelProvider.Factory? = ...): TypeVariable(VM) defined in androidx.lifecycle.viewmodel.compose
@Composable
fun WordBookApp() {
    val context = LocalContext.current

    val wordViewModel: WordViewModel by viewModel( // error here - viewModel
        WordViewModelFactory((context.applicationContext as WordsApplication).repository)
    )

    val words: List<Word> by wordViewModel.allWords.observeAsState(listOf())
    ...

視圖 Model 和視圖 Model 工廠:

class WordViewModel(private val repository: WordRepository) : ViewModel() {
    val allWords: LiveData<List<Word>> = repository.allWords.asLiveData()

    fun insert(word: Word) = viewModelScope.launch {
        repository.insert(word)
    }
}

class WordViewModelFactory(private val repository: WordRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(WordViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return WordViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

代碼的其他部分:

class WordRepository(private val wordDao: WordDao) {
    val allWords: Flow<List<Word>> = wordDao.getAlphabetizedWords()

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun insert(word: Word) {
        wordDao.insert(word)
    }
}
class WordsApplication : Application() {
    private val database by lazy { WordRoomDatabase.getDatabase(this) }
    val repository by lazy { WordRepository(database.wordDao()) }
}
@Database(entities = [Word::class], version = 1, exportSchema = false)
public abstract class WordRoomDatabase : RoomDatabase() {

    abstract fun wordDao(): WordDao

    companion object {
        // Singleton prevents multiple instances of database opening at the
        // same time.
        @Volatile
        private var INSTANCE: WordRoomDatabase? = null

        fun getDatabase(context: Context): WordRoomDatabase {
            // if the INSTANCE is not null, then return it,
            // if it is, then create the database
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    WordRoomDatabase::class.java,
                    "word_database"
                ).build()
                INSTANCE = instance
                // return instance
                instance
            }
        }
    }
}

AndroidMenifest.xml

<application
    android:name=".WordsApplication"
    ....

有人可以幫忙嗎? 謝謝!

因為你沒有提到這個問題,我已經開始輸入這個了。 這可能與您的問題無關,但也許您仍然應該閱讀此內容。

如果問題是視圖模型中的值沒有更新, - 在可組合的內部初始化視圖模型,這是一個非常糟糕的主意。

您會看到可組合項經常進行重組,重新執行其中的每一行代碼。 因此,如果您像這樣初始化此可組合項內的視圖模型,它將在每次重新組合時重新初始化。 理論上,即使在幀速率下也可以進行重組(在許多情況下甚至可以)。 因此,這不是在可組合項中聲明變量的方法。

好的, remember可組合項可以幫助您解決這個問題。 如果你用remember包裝初始化語句,它不會在重組時重新初始化。 但是,它有其局限性。 例如,如果可組合項被銷毀,例如,如果您將其滑出屏幕,則remember值將丟失。 remember隨包含它的可組合項的銷毀而被銷毀。

因此,對於像動畫之類的小東西,可以將變量存儲在可組合項中,但對於重要的東西,你不應該相信這個框架。

因此,最好的方法是在您的主要活動中初始化視圖模型,然后將其方法和變量傳遞給可組合項。 您甚至可以傳遞視圖模型本身,但大多數時候不需要。

代碼:

@Composable
fun WordBookApp() {
    val context = LocalContext.current

    val wordViewModel: WordViewModel by remember {
        viewModel( // error here - viewModel
        WordViewModelFactory((context.applicationContext as WordsApplication).repository)
    )
    }

    val words: List<Word> by wordViewModel.allWords.observeAsState(listOf())
    ...

從@MARSK 的回答中得到線索並修復了它。 將視圖 model 的初始化移動到MainActivityonCreate() ,並將其傳遞給可組合的 function。現在一切正常!

這是代碼,如果將來有人需要的話:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        val wordViewModel by viewModels<WordViewModel> {
            WordViewModelFactory((this.applicationContext as WordsApplication).repository)
        }

        setContent {
            WordBookApp(wordViewModel)
        }
    }
}
@Composable
fun WordBookApp(wordViewModel: WordViewModel) {
    val words: List<Word> by wordViewModel.allWords.observeAsState(listOf())
    ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM