20240215
为了便于开发和维护,一般来说应用应当有界面层和数据层,界面层用于在屏幕上显示应用数据,数据层包含应用的业务逻辑并公开应用数据。
界面层也由两部分,一部分呈现界面和数据,一部分处理数据和逻辑。简单的用我已知的知识理解,就是看得见的HTML CSS与看不见的JavaScript,虽然也常常用JavaScript产生看的见的效果。
而数据层类似之前写的后端,数据源当然也可以是其他网络上部署的东西,或者我这里使用的本地数据库。
比如说考虑数据从用户界面到本地的数据库应该是怎样的,以简易的记账本App为例
//App.kt
val inputName = viewModel.inputName
val inputMoney = viewModel.inputMoney
TextField(
value = inputName.value,
onValueChange = { inputName.value = it },
label = { Text("Name") },
)
TextField(
value = inputMoney.value,
onValueChange = { inputMoney.value = it },
label = { Text("Money") },
)
Button(
onClick = { viewModel.submitData() },
) {
Text("Submit")
}
在呈现界面的地方有两个TextField来填写数据,每次值改变就会更新。inputName
来自viewModel。
在ViewModel中
//ViewModel.kt
private var db:AppDatabase = Room.databaseBuilder(
ExpenseTrackerApplication.context,
AppDatabase::class.java, "app-database"
).allowMainThreadQueries().build()
private var expenseRecordDao: ExpenseRecordDao = db.expenseRecordDao()
val inputName = mutableStateOf("")
val inputMoney = mutableStateOf("")
fun submitData() {
val expenseRecord = ExpenseRecord(0,inputName.value,"",inputMoney.value.toDouble(), Instant.now())
expenseRecordDao.insertAll(expenseRecord)
}
这里的insertAll方法来自数据访问对象,使用这些方法,就能对应用的数据库抽象访问。这里实例化了数据库,为了性能不允许主线程访问,但是为了暂时方便可以.allowMainThreadQueries()
。
//Dao.kt
interface ExpenseRecordDao {
@Insert
fun insertAll(vararg record: ExpenseRecord)
@Update
fun updateRecords(vararg record: ExpenseRecord)
@Delete
fun delete(record: ExpenseRecord)
@Query("SELECT * FROM expense_record ORDER BY uid DESC")
fun getAll(): List<ExpenseRecord>
}
//AppDatabase.kt
@Database(entities = [ExpenseRecord::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase(){
abstract fun expenseRecordDao(): ExpenseRecordDao
}
这里使用了自定义的数据转换器,因为sqlite并不支持Instant类型存储的时间,需要将它转换为Long来存储。
//Converters.kt
class Converters {
@TypeConverter
fun instantToLong(value: Instant): Long {
return value.toEpochMilli()
}
@TypeConverter
fun longToInstant(value: Long): Instant {
return Instant.ofEpochMilli(value)
}
}
用来存储的数据模型
//model.kt
@Entity(tableName = "expense_record")
data class ExpenseRecord (
@PrimaryKey(autoGenerate = true) val uid:Int,
var name: String,
val description: String,
val money: Double,
val time: Instant
)
这样,当你按下Submit按钮的时候,数据就会进入数据库。