Kotlin:在非活动类/计算中使用 SUM 查询的 Room DB

问题描述

我对 Kotlin 比较陌生,我正在为学校做一个项目。我已经被困在一些我现在无法弄清楚的事情上几天,要么是因为我不仅了解它是如何工作的,要么只是不知道要搜索什么。我正在构建一个用于简单预算跟踪的应用程序,并使用 Room DB 允许用户输入和存储他们的费用。我已经构建并运行了大部分应用程序,并拥有数据库DAORepositoryviewmodel。我已经成功编写了一个通过 Query 返回总和的 LiveData<Double>。我设法通过 Toast 消息和 TextView 中的 MainActivity 显示此总和值(但电视不会在加载时更新,只有在启动首次修改数据库条目的活动)。

如果可能的话,我希望能够获取这个总和并将其存储在我为计算函数编写的单独类中,并在用户数据库中输入或删除某些内容时更新它。或者最好让非活动类在调用类的相关函数调用此总和。我似乎不明白如何从 MainActivity 之外的任何地方获取此值。我搜索和阅读的所有内容都有我认为我理解的代码部分,例如需要应用程序参数的 observeForever,或者它们超出了我的脑海,因为它只是我无法理解的代码片段围绕它们如何组合在一起。

这是我目前所拥有的:

我的实体:

@Entity(tableName = "expenses")
data class Expenses (

    @PrimaryKey(autoGenerate = true)

    val id: Int,val expDesc: String,val expAmount: Double

)

我的 DAO:

@Dao
interface Dao {


    @Insert(onConflict = OnConflictStrategy.IGnorE)
    suspend fun addData(expenses: Expenses)

    @Query("SELECT * FROM expenses ORDER BY id ASC")
    fun readAllData(): LiveData<List<Expenses>>

    @Query("SELECT SUM(expAmount) as expenseSum FROM expenses")
    fun getExpenseSum(): LiveData<Double>


    @Update
    suspend fun updateExpense(expenses: Expenses)


    @Delete
    suspend fun deleteData(expenses: Expenses)


    @Query("DELETE FROM expenses")
    suspend fun deleteallData()

}

我的数据库

@Database(entities = [Expenses::class],version = 1,exportSchema = false)
abstract class Database:RoomDatabase() {

    abstract fun dao(): Dao

    abstract class AppDatabase : RoomDatabase() {
        abstract fun userDao(): Dao?
    }


    companion object{

        @Volatile
        private var INSTANCE: com.example.finalproject.roomDB.Database? = null


        fun getDatabase(context: Context): com.example.finalproject.roomDB.Database {
            val instance = INSTANCE

            if(instance != null){
                return instance
            }

            synchronized(this){
                val instance = Room.databaseBuilder(
                        context.applicationContext,com.example.finalproject.roomDB.Database::class.java,"expenses").build()

                INSTANCE = instance
                return instance
            }
        }
    }
}

我的存储库:

class Repository(private val dao: Dao) {

    val readAllData: LiveData<List<Expenses>> = dao.readAllData()
    val getExpenseSum: LiveData<Double> = dao.getExpenseSum()

    suspend fun addData(expenses: Expenses){
        dao.addData(expenses)
    }

    suspend fun updateData(expenses: Expenses){
        dao.updateExpense(expenses)
    }

    suspend fun deleteData(expenses: Expenses){
        dao.deleteData(expenses)
    }

    suspend fun deleteallData(){
        dao.deleteallData()
    }

}

我的视图模型:

class viewmodel(application: Application): Androidviewmodel(application) {

    val readAllData: LiveData<List<Expenses>>
    val getExpenseSum: LiveData<Double>
    private val repository: Repository

    init{
        val dao = Database.getDatabase(application).dao()
        repository = Repository(dao)
        readAllData = repository.readAllData
        getExpenseSum = repository.getExpenseSum
    }

    fun addData(expenses: Expenses){
        viewmodelScope.launch(dispatchers.IO) {
            repository.addData(expenses)
        }
    }

    fun updateData(expenses: Expenses){
        viewmodelScope.launch(dispatchers.IO) { repository.updateData(expenses) }
    }

    fun deleteData(expenses: Expenses){
        viewmodelScope.launch(dispatchers.IO) { repository.deleteData(expenses) }
    }

    fun deleteallData(){
        viewmodelScope.launch(dispatchers.IO) { repository.deleteallData() }
    }

}

我当前与 MainActivity 相关的部分:

class MainActivity : AppCompatActivity() {

    private lateinit var viewmodel: viewmodel
    var sumTotal: Double = 0.0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val expenseViewButton = findViewById<Button>(R.id.expViewButton)
        val incomeViewButton = findViewById<Button>(R.id.incViewButton)
        val goalsViewButton = findViewById<Button>(R.id.goalsViewButton)
        val expectedExpAmtView = findViewById<TextView>(R.id.expectedExpAmt)

        viewmodel = viewmodelProvider.AndroidviewmodelFactory(application).create(viewmodel::class.java)

        //This observer successfully casts the LiveData<Double> to a Double and updates whenever changed 
        val sumObserver = Observer<Double> { expSumDbl -> sumTotal = expSumDbl }
        viewmodel.getExpenseSum.observe(this,sumObserver)
        expectedExpAmtView.text = getString(R.string.monthly_expected_ExpAmt,sumTotal.toString())

        expenseViewButton.setonClickListener{
            val myIntent: Intent = Intent(this,ExpenseActivity::class.java)
            startActivity(myIntent)
        }

        incomeViewButton.setonClickListener{
            val myIntent: Intent = Intent(this,IncomeActivity::class.java)
            startActivity(myIntent)
        }

        //This successfully displays the correct sum whenever the button is pressed
        goalsViewButton.setonClickListener{
            Toast.makeText(this@MainActivity,sumTotal.toString(),Toast.LENGTH_SHORT).show()
        }
    }
}

所以 sumTotal 是来自 MainActivity 的值,我想在不同的非活动类*中获得它,用于计算根本不会影响数据库,只会影响文本视图。我还希望正在更新的 TextView 始终保持最新状态,包括应用启动时间。如果有人能帮我弄清楚我做错了什么和/或需要做不同的事情,我将不胜感激。

*特别是目前,我有一个预算课程,可以处理收入、输入 editText 字段以及计算每月转化的金额。我想从数据库条目中取出总和,然后从预算类中的总和中减去它并返回结果。我可能想稍后用总和做其他(未决定的)事情,这就是我想将它存储在变量中的原因。

解决方法

sumTotalMainActivity 的值实际上来自于 Repository.getExpenseSum,因此与其将该变量共享给另一个类,不如从另一个类再次调用存储库方法可能更容易.实际上,我什至不建议在您的 Activity 中使用该变量 sumTotal,最好仅依赖 ViewModel,但这是一个不同的主题。

您的接口 Dao 包含方法:

fun getExpenseSum(): LiveData<Double>

返回一个 LiveData。如果您想观察 LiveData,您需要有一个 LifecycleOwner,以便 LiveData 知道它应该何时开始和停止发出更新(或者您可以 observeForever,但您需要知道自己什么时候停止观察)。

在您的预算课程中,假设它不是片段/活动,您将不会有 LifecycleOwner,这就是为什么对您提出的一个建议是在 Dao 中创建另一种方法:

fun getExpenseSum(): Double

注意缺少 LiveData。每当您同步调用该方法时,该方法都会返回一个双精度值,但它需要在后台线程上执行。您可以在预算类中调用该方法并在那里获取该值。

最后,我认为您不应该在某些“常规”类中调用这些数据库方法,您应该在创建预算类的实例时传递这些变量。当您使用标准 Android 类时,处理 LiveData/background 线程要容易得多,只需将值传递给需要它的其他类,而不是让它们自己查询存储库。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...