1. database
使用 Room Persistence Library 创建的数据库抽象表示。AppDatabase 继承自 RoomDatabase,用于管理数据库的创建和版本管理,以及提供数据访问对象(DAO)的访问点。以下是代码的主要组成部分和功能:
- @Database 注解:
- entities:指定了数据库中包含的实体类,这里是 User、Book、Note 和 ReadingRecord。
- version:指定数据库的版本号为 8。每当数据库结构发生变化(如添加、删除或修改实体类)时,这个版本号应该增加。
- exportSchema:设置为 false 表示不将数据库架构导出到文件中。这通常用于调试或测试,但在生产环境中通常不启用。
- @TypeConverters 注解:
- 指定了用于数据库类型转换的类,这里是 DataConverters 和 BookStatusConverter。这些转换器允- 许 Room 处理那些不能直接存储在 SQLite 数据库中的复杂数据类型。
- 抽象方法:
- userDao():提供 UserDao 的实例,用于访问 User 实体的数据。
- bookDao():提供 BookDao 的实例,用于访问 Book 实体的数据。
- noteDao():提供 NoteDao 的实例,用于访问 Note 实体的数据。
- ReadingRecordDao():提供 ReadingRecordDao 的实例,用于访问 ReadingRecord 实体的数据。
app/src/main/java/com/example/BookRecord/Database.kt:
package com.example.BookRecord
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
@Database(entities = [User::class, Book::class, Note::class,ReadingRecord::class], version = 8, exportSchema = false)//数据库版本为,不导出数据库架构到文件中
@TypeConverters(DataConverters::class,BookStatusConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun bookDao(): BookDao //定义一个抽象方法,用于获取BookDao的实例,这是访问Book表的数据访问对象。
abstract fun noteDao(): NoteDao
abstract fun ReadingRecordDao():ReadingRecordDao
companion object { //这是一个伴生对象,允许AppDatabase类拥有类似于Java静态成员的功能。
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
.fallbackToDestructiveMigration() // 如果出现无法处理的迁移情况,允许数据库重建
.build()
INSTANCE = instance
instance
}
}
}
}
Dao
与 Room 数据库交互的数据访问对象(DAO)。每个接口都包含了一系列方法,用于执行与相应实体相关的数据库操作。
- UserDao:
- insert:插入一个新用户,如果用户已存在则忽略。
- findUserById:根据用户 ID 查询用户。
- getAllUsers:获取所有用户。
- deleteUserById:根据用户 ID 删除用户。
- BookDao:
- getAllBooks:获取特定用户的所有书籍。
- insertBook:插入一本新书,如果书已存在则替换。
- updateBook:更新一本书的信息。
- deleteBook:删除一本书。
- NoteDao:
- insert:插入一条新笔记,如果笔记已存在则替换。
- getAllNotes:获取所有笔记。
- getNotesByBookId:根据书籍 ID 获取所有笔记。
- getNoteCountByBookId:获取特定书籍的笔记数量。
- update:更新一条笔记。
- delete:删除一条笔记。
- deleteById:根据笔记 ID 删除笔记。
- ReadingRecordDao:
- insertReadingRecord:插入一条阅读记录,如果记录已存在则替换。
- getDailyReadPages:查询特定用户在某一天的阅读页数。
- getTotalReadPages:查询特定用户在指定日期范围内的阅读页数总和。
- getPagesReadPerDay:查询特定用户在指定日期范围内每天的阅读页数。
- DailyReading:一个数据类,用于存储日期和当天阅读的总页数。
这些接口定义了与用户、书籍、笔记和阅读记录相关的 CRUD 操作,它们允许应用程序的数据层与数据库进行交互。
app/src/main/java/com/example/BookRecord/Dao.kt
package com.example.BookRecord
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import org.threeten.bp.LocalDate
// 定义 BookDao 接口,用于访问与 Book 实体相关的数据库操作
@Dao
interface UserDao {
// 插入用户,如果用户已存在则忽略
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(user: User)
// 根据UID查询用户
@Query("SELECT * FROM users WHERE uid = :uid")
fun findUserById(uid: String): User?
// 可选:获取所有用户
@Query("SELECT * FROM users")
fun getAllUsers(): List<User>
// 可选:删除用户
@Query("DELETE FROM users WHERE uid = :uid")
fun deleteUserById(uid: String)
}
@Dao
interface BookDao {
@Query("SELECT * FROM books WHERE userId = :userId")
fun getAllBooks(userId: String): LiveData<List<Book>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertBook(book: Book)
@Update
suspend fun updateBook(book: Book)
@Delete
suspend fun deleteBook(book: Book)
}
@Dao
interface NoteDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(note: Note)
@Query("SELECT * FROM note") // 确保表名与你的 Note 实体中定义的一致
fun getAllNotes(): LiveData<List<Note>>
@Query("SELECT * FROM Note WHERE bookId = :bookId")
fun getNotesByBookId(bookId: Int): LiveData<List<Note>>
@Query("SELECT COUNT(*) FROM Note WHERE bookId = :bookId")
fun getNoteCountByBookId(bookId: Int): LiveData<Int>
@Update
suspend fun update(note: Note)
@Delete
suspend fun delete(note: Note)
@Query("DELETE FROM Note WHERE id = :noteId")
suspend fun deleteById(noteId: Int)
}
@Dao
interface ReadingRecordDao {
// 插入阅读记录
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertReadingRecord(readingRecord: ReadingRecord)
// 查询特定用户在某日期的阅读页数
@Query("SELECT SUM(readPages) FROM reading_records WHERE userId = :userId AND date = :date")
fun getDailyReadPages(userId: String, date: LocalDate): LiveData<Int>
// 查询特定用户在指定日期范围内的阅读页数总和
@Query("SELECT SUM(readPages) FROM reading_records WHERE userId = :userId AND date BETWEEN :startDate AND :endDate")
fun getTotalReadPages(userId: String, startDate: LocalDate, endDate: LocalDate): LiveData<Int>
// 查询特定用户在指定日期范围内每天的阅读页数
@Query("SELECT date, SUM(readPages) as totalPages FROM reading_records WHERE userId = :userId AND date BETWEEN :startDate AND :endDate GROUP BY date ORDER BY date")
fun getPagesReadPerDay(userId: String, startDate: LocalDate, endDate: LocalDate): LiveData<List<DailyReading>>
data class DailyReading(
val date: LocalDate,
val totalPages: Int
)
}
3.repository
定义了三个类,它们作为应用程序的数据访问层,负责与数据访问对象(DAO)交互,并提供数据给ViewModel或其他组件。
- BookRepository:
- 负责管理与 Book 实体相关的数据操作。
- 包含一个 currentUserId MutableLiveData,用于跟踪当前登录用户的ID。
- 通过 switchMap 根据当前用户ID获取用户的所有书籍。
- 提供方法来插入、更新和删除书籍。
- NoteRepository:
- 负责管理与 Note 实体相关的数据操作。
- 提供方法来获取书籍的所有笔记、插入新笔记、更新笔记、删除笔记以及根据书籍ID获取笔记数量。
- ReadingRecordRepository:
- 负责管理与 ReadingRecord 实体相关的数据操作。
- 包含一个 currentUserId MutableLiveData,用于跟踪当前登录用户的ID。
- 提供方法来插入新的阅读记录和获取指定日期范围内每天的阅读页数。
这些类通过Firebase Authentication获取当前用户ID,并根据用户ID来过滤和操作数据。它们使用LiveData和协程来异步处理数据,确保UI的流畅性和响应性。这些类作为ViewModel和DAO之间的中间层,简化了数据操作的复杂性,使得ViewModel可以更专注于UI逻辑。
app/src/main/java/com/example/BookRecord/Repository.kt
package com.example.BookRecord
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.switchMap
import com.google.firebase.auth.FirebaseAuth
import kotlinx.coroutines.CoroutineScope
import org.threeten.bp.LocalDate
//这两个类的功能是作为应用的数据访问逻辑与数据访问对象(DAO)之间的中间层,提供清晰的API来处理数据。
class BookRepository(private val bookDao: BookDao, private val scope: CoroutineScope) {
private val currentUserId = MutableLiveData<String?>()
init {
// 添加 FirebaseAuth 状态监听器
FirebaseAuth.getInstance().addAuthStateListener { firebaseAuth ->
// 异步更新当前用户ID
val user = firebaseAuth.currentUser
currentUserId.postValue(user?.uid)
}
// 初始设置当前用户ID
refreshCurrentUser()
}
// 使用 currentUserId 来生成 allBooks 的 LiveData
val allBooks: LiveData<List<Book>> = currentUserId.switchMap { uid ->
if (uid == null) MutableLiveData(emptyList())
else bookDao.getAllBooks(uid)
}
// 用于刷新当前用户的方法,可以在必要时显式调用
fun refreshCurrentUser() {
currentUserId.value = FirebaseAuth.getInstance().currentUser?.uid
}
suspend fun insert(book: Book) {
bookDao.insertBook(book)
}
suspend fun update(book: Book) {
bookDao.updateBook(book)
}
suspend fun delete(book: Book) {
bookDao.deleteBook(book)
}
}
class NoteRepository(private val noteDao: NoteDao) {
fun getNotesByBookId(bookId: Int): LiveData<List<Note>> = noteDao.getNotesByBookId(bookId)
suspend fun insert(note: Note) {
noteDao.insert(note)
}
fun getNoteCountByBookId(bookId: Int): LiveData<Int> {
return noteDao.getNoteCountByBookId(bookId)
}
suspend fun update(note: Note) {
noteDao.update(note)
}
suspend fun delete(note: Note) {
noteDao.delete(note)
}
suspend fun deleteById(noteId: Int) {
noteDao.deleteById(noteId)
}
}
class ReadingRecordRepository(private val readingRecordDao: ReadingRecordDao) {
private val currentUserId = MutableLiveData<String?>()
init {
// 添加 FirebaseAuth 状态监听器
FirebaseAuth.getInstance().addAuthStateListener { firebaseAuth ->
// 异步更新当前用户ID
val user = firebaseAuth.currentUser
currentUserId.postValue(user?.uid)
}
}
// 插入新的阅读记录
suspend fun insertReadingRecord(readingRecord: ReadingRecord) {
readingRecordDao.insertReadingRecord(readingRecord)
}
// 获取特定用户在指定日期范围内每天的阅读页数
fun getPagesReadPerDay(startDate: LocalDate, endDate: LocalDate): LiveData<List<ReadingRecordDao.DailyReading>> = currentUserId.switchMap { uid ->
if (uid == null) MutableLiveData(emptyList())
else readingRecordDao.getPagesReadPerDay(uid, startDate, endDate)
}
}
ps
app的最终页面见和该文章同一专栏下的博文:安卓开发:BookRecord一款专为纸质书爱好者设计的阅读追踪应用