Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/235.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 从另一个片段/视图模型执行插入时,从文件室返回的Kotlin流不会更新_Android_Kotlin_Android Room_Kotlin Coroutines_Kotlin Flow - Fatal编程技术网

Android 从另一个片段/视图模型执行插入时,从文件室返回的Kotlin流不会更新

Android 从另一个片段/视图模型执行插入时,从文件室返回的Kotlin流不会更新,android,kotlin,android-room,kotlin-coroutines,kotlin-flow,Android,Kotlin,Android Room,Kotlin Coroutines,Kotlin Flow,我有一个房间数据库,它返回一个对象流。当我向数据库中插入一个新项时,如果插入是从同一片段/视图模型执行的,则流的collect函数才会触发 我已经录制了一段视频,展示了这个问题: 以下是我对相关文件的代码设置: 成就dao.kt: @Dao interface AchievementDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(achievement: Achievement) @Q

我有一个房间数据库,它返回一个对象流。当我向数据库中插入一个新项时,如果插入是从同一片段/视图模型执行的,则流的collect函数才会触发

我已经录制了一段视频,展示了这个问题:

以下是我对相关文件的代码设置:

成就dao.kt:

@Dao
interface AchievementDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(achievement: Achievement)

    @Query("SELECT * FROM achievement")
    fun getAllAchievements(): Flow<List<Achievement>>
}
@Database(entities = [Achievement::class], version = 1, exportSchema = false)
abstract class AppDB : RoomDatabase() {

    abstract fun achievementDao(): AchievementDao
}
@AndroidEntryPoint
class HomeFragment : Fragment() {

    private val viewModel: HomeViewModel by viewModels()

    private lateinit var homeText: TextView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        bindViews()
        subscribeObservers()
    }

    private fun bindViews() {
        homeText = requireView().findViewById(R.id.txt_home)
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement).setOnClickListener {
            AddAchievementBottomSheet().show(parentFragmentManager, "AddAchievementDialog")
        }
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement_same_fragment).setOnClickListener {
            viewModel.add()
        }
    }

    private fun subscribeObservers() {
        viewModel.count.observe(viewLifecycleOwner, { count ->
            if(count != null) {
                homeText.text = count.toString()
            } else {
                homeText.text = resources.getString(R.string.app_name)
            }
        })
    }
}
class HomeViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    private val _count = MutableLiveData<Int>(null)
    val count = _count as LiveData<Int>

    init {
        viewModelScope.launch {
            achievementRepository.getAllAchievements()
                .collect { values ->
                    // FIXME this is only called when inserting from the same Fragment
                    _count.postValue(values.count())
                }
        }
    }

    fun add() {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
        }
    }
}
@AndroidEntryPoint
class AddAchievementBottomSheet : BottomSheetDialogFragment() {

    private val viewModel: AddAchievementViewModel by viewModels()
    private lateinit var addButton: MaterialButton

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.dialog_add_achievement, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        addButton = requireView().findViewById(R.id.btn_add_achievement)
        addButton.setOnClickListener {
            viewModel.add(::close)
        }
    }

    private fun close() {
        dismiss()
    }
}
class AddAchievementViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    fun add(closeCallback: () -> Any) {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
            closeCallback()
        }
    }
}
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

android {
    compileSdkVersion 30

    defaultConfig {
        applicationId "com.marcdonald.achievementtracker"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    // Kotlin
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
    implementation 'androidx.core:core-ktx:1.3.2'

    // Android
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation "androidx.activity:activity-ktx:1.1.0"
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

    // Navigation
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'

    // Testing
    testImplementation 'junit:junit:4.13.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    // Dagger Hilt
    implementation 'com.google.dagger:hilt-android:2.29.1-alpha'
    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
    kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
    kapt 'com.google.dagger:hilt-android-compiler:2.29.1-alpha'

    // Timber for logging
    implementation 'com.jakewharton.timber:timber:4.7.1'

    // Room
    implementation 'androidx.room:room-runtime:2.2.5'
    implementation 'androidx.room:room-ktx:2.2.5'
    kapt 'androidx.room:room-compiler:2.2.5'
    androidTestImplementation 'androidx.room:room-testing:2.2.5'
}
buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.0-alpha16'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.29.1-alpha'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
成就存储.kt

class AchievementRepository @Inject constructor(appDB: AppDB) {

    private val achievementDao = appDB.achievementDao()

    suspend fun insert(achievement: Achievement) {
        withContext(Dispatchers.IO) {
            achievementDao.insert(achievement)
        }
    }

    fun getAllAchievements() = achievementDao.getAllAchievements()
}
HomeFragment.kt:

@Dao
interface AchievementDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(achievement: Achievement)

    @Query("SELECT * FROM achievement")
    fun getAllAchievements(): Flow<List<Achievement>>
}
@Database(entities = [Achievement::class], version = 1, exportSchema = false)
abstract class AppDB : RoomDatabase() {

    abstract fun achievementDao(): AchievementDao
}
@AndroidEntryPoint
class HomeFragment : Fragment() {

    private val viewModel: HomeViewModel by viewModels()

    private lateinit var homeText: TextView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        bindViews()
        subscribeObservers()
    }

    private fun bindViews() {
        homeText = requireView().findViewById(R.id.txt_home)
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement).setOnClickListener {
            AddAchievementBottomSheet().show(parentFragmentManager, "AddAchievementDialog")
        }
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement_same_fragment).setOnClickListener {
            viewModel.add()
        }
    }

    private fun subscribeObservers() {
        viewModel.count.observe(viewLifecycleOwner, { count ->
            if(count != null) {
                homeText.text = count.toString()
            } else {
                homeText.text = resources.getString(R.string.app_name)
            }
        })
    }
}
class HomeViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    private val _count = MutableLiveData<Int>(null)
    val count = _count as LiveData<Int>

    init {
        viewModelScope.launch {
            achievementRepository.getAllAchievements()
                .collect { values ->
                    // FIXME this is only called when inserting from the same Fragment
                    _count.postValue(values.count())
                }
        }
    }

    fun add() {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
        }
    }
}
@AndroidEntryPoint
class AddAchievementBottomSheet : BottomSheetDialogFragment() {

    private val viewModel: AddAchievementViewModel by viewModels()
    private lateinit var addButton: MaterialButton

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.dialog_add_achievement, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        addButton = requireView().findViewById(R.id.btn_add_achievement)
        addButton.setOnClickListener {
            viewModel.add(::close)
        }
    }

    private fun close() {
        dismiss()
    }
}
class AddAchievementViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    fun add(closeCallback: () -> Any) {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
            closeCallback()
        }
    }
}
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

android {
    compileSdkVersion 30

    defaultConfig {
        applicationId "com.marcdonald.achievementtracker"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    // Kotlin
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
    implementation 'androidx.core:core-ktx:1.3.2'

    // Android
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation "androidx.activity:activity-ktx:1.1.0"
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

    // Navigation
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'

    // Testing
    testImplementation 'junit:junit:4.13.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    // Dagger Hilt
    implementation 'com.google.dagger:hilt-android:2.29.1-alpha'
    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
    kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
    kapt 'com.google.dagger:hilt-android-compiler:2.29.1-alpha'

    // Timber for logging
    implementation 'com.jakewharton.timber:timber:4.7.1'

    // Room
    implementation 'androidx.room:room-runtime:2.2.5'
    implementation 'androidx.room:room-ktx:2.2.5'
    kapt 'androidx.room:room-compiler:2.2.5'
    androidTestImplementation 'androidx.room:room-testing:2.2.5'
}
buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.0-alpha16'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.29.1-alpha'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
添加成就Bottomsheetviewmodel.kt:

@Dao
interface AchievementDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(achievement: Achievement)

    @Query("SELECT * FROM achievement")
    fun getAllAchievements(): Flow<List<Achievement>>
}
@Database(entities = [Achievement::class], version = 1, exportSchema = false)
abstract class AppDB : RoomDatabase() {

    abstract fun achievementDao(): AchievementDao
}
@AndroidEntryPoint
class HomeFragment : Fragment() {

    private val viewModel: HomeViewModel by viewModels()

    private lateinit var homeText: TextView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        bindViews()
        subscribeObservers()
    }

    private fun bindViews() {
        homeText = requireView().findViewById(R.id.txt_home)
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement).setOnClickListener {
            AddAchievementBottomSheet().show(parentFragmentManager, "AddAchievementDialog")
        }
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement_same_fragment).setOnClickListener {
            viewModel.add()
        }
    }

    private fun subscribeObservers() {
        viewModel.count.observe(viewLifecycleOwner, { count ->
            if(count != null) {
                homeText.text = count.toString()
            } else {
                homeText.text = resources.getString(R.string.app_name)
            }
        })
    }
}
class HomeViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    private val _count = MutableLiveData<Int>(null)
    val count = _count as LiveData<Int>

    init {
        viewModelScope.launch {
            achievementRepository.getAllAchievements()
                .collect { values ->
                    // FIXME this is only called when inserting from the same Fragment
                    _count.postValue(values.count())
                }
        }
    }

    fun add() {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
        }
    }
}
@AndroidEntryPoint
class AddAchievementBottomSheet : BottomSheetDialogFragment() {

    private val viewModel: AddAchievementViewModel by viewModels()
    private lateinit var addButton: MaterialButton

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.dialog_add_achievement, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        addButton = requireView().findViewById(R.id.btn_add_achievement)
        addButton.setOnClickListener {
            viewModel.add(::close)
        }
    }

    private fun close() {
        dismiss()
    }
}
class AddAchievementViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    fun add(closeCallback: () -> Any) {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
            closeCallback()
        }
    }
}
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

android {
    compileSdkVersion 30

    defaultConfig {
        applicationId "com.marcdonald.achievementtracker"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    // Kotlin
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
    implementation 'androidx.core:core-ktx:1.3.2'

    // Android
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation "androidx.activity:activity-ktx:1.1.0"
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

    // Navigation
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'

    // Testing
    testImplementation 'junit:junit:4.13.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    // Dagger Hilt
    implementation 'com.google.dagger:hilt-android:2.29.1-alpha'
    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
    kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
    kapt 'com.google.dagger:hilt-android-compiler:2.29.1-alpha'

    // Timber for logging
    implementation 'com.jakewharton.timber:timber:4.7.1'

    // Room
    implementation 'androidx.room:room-runtime:2.2.5'
    implementation 'androidx.room:room-ktx:2.2.5'
    kapt 'androidx.room:room-compiler:2.2.5'
    androidTestImplementation 'androidx.room:room-testing:2.2.5'
}
buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.0-alpha16'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.29.1-alpha'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
build.gradle(应用程序):

@Dao
interface AchievementDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(achievement: Achievement)

    @Query("SELECT * FROM achievement")
    fun getAllAchievements(): Flow<List<Achievement>>
}
@Database(entities = [Achievement::class], version = 1, exportSchema = false)
abstract class AppDB : RoomDatabase() {

    abstract fun achievementDao(): AchievementDao
}
@AndroidEntryPoint
class HomeFragment : Fragment() {

    private val viewModel: HomeViewModel by viewModels()

    private lateinit var homeText: TextView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        bindViews()
        subscribeObservers()
    }

    private fun bindViews() {
        homeText = requireView().findViewById(R.id.txt_home)
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement).setOnClickListener {
            AddAchievementBottomSheet().show(parentFragmentManager, "AddAchievementDialog")
        }
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement_same_fragment).setOnClickListener {
            viewModel.add()
        }
    }

    private fun subscribeObservers() {
        viewModel.count.observe(viewLifecycleOwner, { count ->
            if(count != null) {
                homeText.text = count.toString()
            } else {
                homeText.text = resources.getString(R.string.app_name)
            }
        })
    }
}
class HomeViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    private val _count = MutableLiveData<Int>(null)
    val count = _count as LiveData<Int>

    init {
        viewModelScope.launch {
            achievementRepository.getAllAchievements()
                .collect { values ->
                    // FIXME this is only called when inserting from the same Fragment
                    _count.postValue(values.count())
                }
        }
    }

    fun add() {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
        }
    }
}
@AndroidEntryPoint
class AddAchievementBottomSheet : BottomSheetDialogFragment() {

    private val viewModel: AddAchievementViewModel by viewModels()
    private lateinit var addButton: MaterialButton

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.dialog_add_achievement, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        addButton = requireView().findViewById(R.id.btn_add_achievement)
        addButton.setOnClickListener {
            viewModel.add(::close)
        }
    }

    private fun close() {
        dismiss()
    }
}
class AddAchievementViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    fun add(closeCallback: () -> Any) {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
            closeCallback()
        }
    }
}
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

android {
    compileSdkVersion 30

    defaultConfig {
        applicationId "com.marcdonald.achievementtracker"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    // Kotlin
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
    implementation 'androidx.core:core-ktx:1.3.2'

    // Android
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation "androidx.activity:activity-ktx:1.1.0"
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

    // Navigation
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'

    // Testing
    testImplementation 'junit:junit:4.13.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    // Dagger Hilt
    implementation 'com.google.dagger:hilt-android:2.29.1-alpha'
    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
    kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
    kapt 'com.google.dagger:hilt-android-compiler:2.29.1-alpha'

    // Timber for logging
    implementation 'com.jakewharton.timber:timber:4.7.1'

    // Room
    implementation 'androidx.room:room-runtime:2.2.5'
    implementation 'androidx.room:room-ktx:2.2.5'
    kapt 'androidx.room:room-compiler:2.2.5'
    androidTestImplementation 'androidx.room:room-testing:2.2.5'
}
buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.0-alpha16'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.29.1-alpha'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
build.gradle(项目):

@Dao
interface AchievementDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(achievement: Achievement)

    @Query("SELECT * FROM achievement")
    fun getAllAchievements(): Flow<List<Achievement>>
}
@Database(entities = [Achievement::class], version = 1, exportSchema = false)
abstract class AppDB : RoomDatabase() {

    abstract fun achievementDao(): AchievementDao
}
@AndroidEntryPoint
class HomeFragment : Fragment() {

    private val viewModel: HomeViewModel by viewModels()

    private lateinit var homeText: TextView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        bindViews()
        subscribeObservers()
    }

    private fun bindViews() {
        homeText = requireView().findViewById(R.id.txt_home)
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement).setOnClickListener {
            AddAchievementBottomSheet().show(parentFragmentManager, "AddAchievementDialog")
        }
        requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement_same_fragment).setOnClickListener {
            viewModel.add()
        }
    }

    private fun subscribeObservers() {
        viewModel.count.observe(viewLifecycleOwner, { count ->
            if(count != null) {
                homeText.text = count.toString()
            } else {
                homeText.text = resources.getString(R.string.app_name)
            }
        })
    }
}
class HomeViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    private val _count = MutableLiveData<Int>(null)
    val count = _count as LiveData<Int>

    init {
        viewModelScope.launch {
            achievementRepository.getAllAchievements()
                .collect { values ->
                    // FIXME this is only called when inserting from the same Fragment
                    _count.postValue(values.count())
                }
        }
    }

    fun add() {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
        }
    }
}
@AndroidEntryPoint
class AddAchievementBottomSheet : BottomSheetDialogFragment() {

    private val viewModel: AddAchievementViewModel by viewModels()
    private lateinit var addButton: MaterialButton

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.dialog_add_achievement, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        addButton = requireView().findViewById(R.id.btn_add_achievement)
        addButton.setOnClickListener {
            viewModel.add(::close)
        }
    }

    private fun close() {
        dismiss()
    }
}
class AddAchievementViewModel @ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
        ViewModel() {

    fun add(closeCallback: () -> Any) {
        viewModelScope.launch {
            achievementRepository.insert(Achievement(0, 0, "Test"))
            closeCallback()
        }
    }
}
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

android {
    compileSdkVersion 30

    defaultConfig {
        applicationId "com.marcdonald.achievementtracker"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    // Kotlin
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
    implementation 'androidx.core:core-ktx:1.3.2'

    // Android
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation "androidx.activity:activity-ktx:1.1.0"
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

    // Navigation
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'

    // Testing
    testImplementation 'junit:junit:4.13.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    // Dagger Hilt
    implementation 'com.google.dagger:hilt-android:2.29.1-alpha'
    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
    kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
    kapt 'com.google.dagger:hilt-android-compiler:2.29.1-alpha'

    // Timber for logging
    implementation 'com.jakewharton.timber:timber:4.7.1'

    // Room
    implementation 'androidx.room:room-runtime:2.2.5'
    implementation 'androidx.room:room-ktx:2.2.5'
    kapt 'androidx.room:room-compiler:2.2.5'
    androidTestImplementation 'androidx.room:room-testing:2.2.5'
}
buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.0-alpha16'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.29.1-alpha'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

我不确定是我对Kotlin Flow的理解造成的还是我的设置在某种程度上不正确,但我希望能得到一些帮助。

Flow是一个冷流,这意味着您必须手动调用Flow.collect{}来获取数据

要持续观察数据库中的更改

选项1)将流转换为Livedata

val count: LiveData<Int> = achievementRepository.getAllAchivements().map {
    it.count()
}.asLiveData()
val count:LiveData=achievementRepository.getAllAchivements().map{
it.count()
}.asLiveData()
在Google Codelab中查看解决方案代码,“带视图的Android房间-Kotlin”


选项2)将流转换为StateFlow,这是一个可以通过StateFlow观察到的热流。收集{}

确保使用RoomDatabase的相同实例。在提供AppDB的地方添加一个@Singleton可能会起到作用。

您的调用只会在初始化HomeViewModel后执行。如果希望收到有关相应实体的任何数据库更新的通知,则需要再次调用collect函数。一种解决方案是直接从房间中检索LiveData并观察该LiveData。这样,您将收到该数据库实体或该表发生的所有更改。@Marc您找到任何解决方案了吗?恐怕我无法使用Kotlin Flow找到解决方案,我最终只使用了LiveData。您好,谢谢您的回答,不幸的是,在尝试了这两个选项后,问题仍然存在。目前,我已将DAO返回的流转换为LiveData,类似于您和Codelab所显示的类HomeViewModel。。。{private val\u count=MutableLiveData(null)val count=AchieventRepository.allAchievents.map{it.count()}.asLiveData()}``类AchieventRepository。。。{val allAchievements=achievementDao.getAllAchievements()}`(1/3),但是从对话框添加的条目仍然不会立即显示。如果我从对话框中添加一个,然后紧接着从同一个片段中添加,则数字会增加2。例如:显示1->从同一片段添加->显示2->从对话框添加->显示2->从同一片段添加->显示4(2/3),即使从DAO查询返回LiveData并同时消除流,也会出现相同的问题。(3/3)“如果Lifecycle对象未处于活动状态,则即使值发生更改,也不会调用观察者。”--您可以通过将HomeViewModel类中的init{}块更改为方法getCount()来发出一次性请求,例如,然后在onResume()中调用此方法,HomeFragment。不幸的是,这也不起作用,因为打开对话框时HomeFragment不会更改状态,因此每当对话框关闭且值未更新时,不会调用onResume()方法。这恐怕不起作用,因为数据库已经是单例