Suspend more db queries, part of #629

This commit is contained in:
tarsin committed 2023-07-12 09:15:01 +08:00
1 parent 60b77145a9
commit 4694245b51
14 files changed
+83 -125

No files matched your search

+30 -61
View File
@@ -23,7 +23,6 @@ import androidx.paging.PagingSource
import arrow.fx.coroutines.release
import arrow.fx.coroutines.resource
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.dao.BasicDao
import com.hippo.ehviewer.dao.DownloadDirname
import com.hippo.ehviewer.dao.DownloadInfo
import com.hippo.ehviewer.dao.DownloadLabel
@@ -34,10 +33,11 @@ import com.hippo.ehviewer.dao.LocalFavoriteInfo
import com.hippo.ehviewer.dao.QuickSearch
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.util.sendTo
import kotlinx.coroutines.runBlocking
import splitties.arch.room.roomDb
object EhDB {
private val db = roomDb<EhDatabase>("eh.db") { allowMainThreadQueries() }
private val db = roomDb<EhDatabase>("eh.db")
// Fix state
@get:Synchronized
@@ -151,13 +151,11 @@ object EhDB {
dao.delete(raw!!)
}
@Synchronized
fun removeLocalFavorites(gid: Long) {
suspend fun removeLocalFavorites(gid: Long) {
db.localFavoritesDao().deleteByKey(gid)
}
@Synchronized
fun removeLocalFavorites(gidArray: LongArray) {
suspend fun removeLocalFavorites(gidArray: LongArray) {
val dao = db.localFavoritesDao()
for (gid in gidArray) {
dao.deleteByKey(gid)
@@ -167,11 +165,11 @@ object EhDB {
@Synchronized
fun containLocalFavorites(gid: Long): Boolean {
val dao = db.localFavoritesDao()
return dao.contains(gid)
// TODO: remove runBlocking
return runBlocking { dao.contains(gid) }
}
@Synchronized
fun putLocalFavorites(galleryInfo: GalleryInfo) {
suspend fun putLocalFavorites(galleryInfo: GalleryInfo) {
val dao = db.localFavoritesDao()
if (null == dao.load(galleryInfo.gid)) {
val info: LocalFavoriteInfo
@@ -185,46 +183,36 @@ object EhDB {
}
}
@Synchronized
fun putLocalFavorites(galleryInfoList: List<GalleryInfo>) {
suspend fun putLocalFavorites(galleryInfoList: List<GalleryInfo>) {
for (gi in galleryInfoList) {
putLocalFavorites(gi)
}
}
@get:Synchronized
val allQuickSearch: List<QuickSearch>
get() {
val dao = db.quickSearchDao()
return dao.list()
}
suspend fun getAllQuickSearch() = db.quickSearchDao().list()
@Synchronized
fun insertQuickSearch(quickSearch: QuickSearch) {
suspend fun insertQuickSearch(quickSearch: QuickSearch) {
val dao = db.quickSearchDao()
quickSearch.id = null
quickSearch.time = System.currentTimeMillis()
quickSearch.id = dao.insert(quickSearch)
}
@Synchronized
fun importQuickSearch(quickSearchList: List<QuickSearch?>) {
private suspend fun importQuickSearch(quickSearchList: List<QuickSearch?>) {
val dao = db.quickSearchDao()
for (quickSearch in quickSearchList) {
dao.insert(quickSearch!!)
}
}
@Synchronized
fun deleteQuickSearch(quickSearch: QuickSearch?) {
suspend fun deleteQuickSearch(quickSearch: QuickSearch?) {
val dao = db.quickSearchDao()
if (quickSearch != null) {
dao.delete(quickSearch)
}
}
@Synchronized
fun moveQuickSearch(fromPosition: Int, toPosition: Int) {
suspend fun moveQuickSearch(fromPosition: Int, toPosition: Int) {
if (fromPosition == toPosition) {
return
}
@@ -254,8 +242,7 @@ object EhDB {
fun searchLocalFav(keyword: String) = db.localFavoritesDao().listLazy("%$keyword%")
@Synchronized
fun putHistoryInfo(galleryInfo: GalleryInfo?) {
suspend fun putHistoryInfo(galleryInfo: GalleryInfo?) {
val dao = db.historyDao()
val info: HistoryInfo = if (galleryInfo is HistoryInfo) {
galleryInfo
@@ -270,8 +257,7 @@ object EhDB {
}
}
@Synchronized
fun modifyHistoryInfoFavslotNonRefresh(gid: Long, slot: Int) {
suspend fun modifyHistoryInfoFavslotNonRefresh(gid: Long, slot: Int) {
val dao = db.historyDao()
dao.load(gid)?.let {
it.favoriteSlot = slot
@@ -279,8 +265,7 @@ object EhDB {
}
}
@Synchronized
fun putHistoryInfo(historyInfoList: List<HistoryInfo>) {
private suspend fun putHistoryInfo(historyInfoList: List<HistoryInfo>) {
val dao = db.historyDao()
for (info in historyInfoList) {
if (null == dao.load(info.gid)) {
@@ -289,29 +274,20 @@ object EhDB {
}
}
@Synchronized
fun deleteHistoryInfo(info: HistoryInfo?) {
suspend fun deleteHistoryInfo(info: HistoryInfo?) {
val dao = db.historyDao()
dao.delete(info!!)
}
@Synchronized
fun clearHistoryInfo() {
suspend fun clearHistoryInfo() {
val dao = db.historyDao()
dao.deleteAll()
}
@get:Synchronized
val allFilter: List<Filter>
get() = db.filterDao().list()
suspend fun getAllFilter() = db.filterDao().list()
@Synchronized
fun addFilter(filter: Filter): Boolean {
val existFilter: Filter? = try {
db.filterDao().load(filter.text, filter.mode.field)
} catch (e: Exception) {
null
}
suspend fun addFilter(filter: Filter): Boolean {
val existFilter = runCatching { db.filterDao().load(filter.text, filter.mode.field) }.getOrNull()
return if (existFilter == null) {
filter.id = null
filter.id = db.filterDao().insert(filter)
@@ -321,22 +297,15 @@ object EhDB {
}
}
@Synchronized
fun deleteFilter(filter: Filter) {
suspend fun deleteFilter(filter: Filter) {
db.filterDao().delete(filter)
}
@Synchronized
fun triggerFilter(filter: Filter) {
suspend fun triggerFilter(filter: Filter) {
filter.enable = !filter.enable
db.filterDao().update(filter)
}
private fun <T> copyDao(from: BasicDao<T>, to: BasicDao<T>) {
val list = from.list()
for (item in list) to.insert(item)
}
suspend fun exportDB(context: Context, uri: Uri) {
val ehExportName = "eh.export.db"
resource {
@@ -346,13 +315,13 @@ object EhDB {
it.close()
context.deleteDatabase(ehExportName)
} use { newDb ->
copyDao(db.downloadsDao(), newDb.downloadsDao())
copyDao(db.downloadLabelDao(), newDb.downloadLabelDao())
copyDao(db.downloadDirnameDao(), newDb.downloadDirnameDao())
copyDao(db.historyDao(), newDb.historyDao())
copyDao(db.quickSearchDao(), newDb.quickSearchDao())
copyDao(db.localFavoritesDao(), newDb.localFavoritesDao())
copyDao(db.filterDao(), newDb.filterDao())
db.downloadsDao().list().forEach { newDb.downloadsDao().insert(it) }
db.downloadLabelDao().list().forEach { newDb.downloadLabelDao().insert(it) }
db.downloadDirnameDao().list().forEach { newDb.downloadDirnameDao().insert(it) }
db.historyDao().list().forEach { newDb.historyDao().insert(it) }
db.quickSearchDao().list().forEach { newDb.quickSearchDao().insert(it) }
db.localFavoritesDao().list().forEach { newDb.localFavoritesDao().insert(it) }
db.filterDao().list().forEach { newDb.filterDao().insert(it) }
newDb.close()
val dbFile = context.getDatabasePath(ehExportName)
context.contentResolver.openFileDescriptor(uri, "rw")!!.use { toFd ->
@@ -38,7 +38,7 @@ object EhFilter : CoroutineScope {
override val coroutineContext = Dispatchers.IO.limitedParallelism(1)
private fun <R> Filter.launchOps(
callback: ((R) -> Unit)? = null,
ops: Filter.() -> R,
ops: suspend Filter.() -> R,
) = launch { ops().let { callback?.invoke(it) } }
fun Filter.remember(callback: ((Boolean) -> Unit)? = null) = launchOps(callback) {
EhDB.addFilter(this).also { if (it) memorizeFilter(this) }
@@ -56,9 +56,7 @@ object EhFilter : CoroutineScope {
}
}
init {
EhDB.allFilter.forEach(::memorizeFilter)
}
init { launch { EhDB.getAllFilter().forEach(::memorizeFilter) } }
private fun memorizeFilter(filter: Filter) {
when (filter.mode) {
@@ -1,6 +0,0 @@
package com.hippo.ehviewer.dao
interface BasicDao<T> {
fun list(): List<T>
fun insert(t: T): Long
}
@@ -6,5 +6,5 @@ import androidx.room.Query
@Dao
interface CookiesDao {
@Query("SELECT * FROM OK_HTTP_3_COOKIE")
fun list(): List<Cookie>
suspend fun list(): List<Cookie>
}
@@ -6,7 +6,7 @@ import androidx.room.Query
import androidx.room.Update
@Dao
interface DownloadDirnameDao : BasicDao<DownloadDirname> {
interface DownloadDirnameDao {
@Query("SELECT * FROM DOWNLOAD_DIRNAME WHERE GID = :gid")
fun load(gid: Long): DownloadDirname?
@@ -14,14 +14,11 @@ interface DownloadDirnameDao : BasicDao<DownloadDirname> {
fun update(downloadDirname: DownloadDirname)
@Insert
override fun insert(t: DownloadDirname): Long
fun insert(t: DownloadDirname): Long
@Query("DELETE FROM DOWNLOAD_DIRNAME WHERE GID = :gid")
fun deleteByKey(gid: Long)
@Query("DELETE FROM DOWNLOAD_DIRNAME")
fun deleteAll()
@Query("SELECT * FROM DOWNLOAD_DIRNAME")
override fun list(): List<DownloadDirname>
fun list(): List<DownloadDirname>
}
@@ -7,9 +7,9 @@ import androidx.room.Query
import androidx.room.Update
@Dao
interface DownloadLabelDao : BasicDao<DownloadLabel> {
interface DownloadLabelDao {
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY TIME ASC")
override fun list(): List<DownloadLabel>
fun list(): List<DownloadLabel>
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY TIME ASC LIMIT :limit OFFSET :offset")
fun list(offset: Int, limit: Int): List<DownloadLabel>
@@ -21,7 +21,7 @@ interface DownloadLabelDao : BasicDao<DownloadLabel> {
fun update(downloadLabel: DownloadLabel)
@Insert
override fun insert(t: DownloadLabel): Long
fun insert(t: DownloadLabel): Long
@Delete
fun delete(downloadLabel: DownloadLabel)
@@ -7,9 +7,9 @@ import androidx.room.Query
import androidx.room.Update
@Dao
interface DownloadsDao : BasicDao<DownloadInfo> {
interface DownloadsDao {
@Query("SELECT * FROM DOWNLOADS ORDER BY TIME DESC")
override fun list(): List<DownloadInfo>
fun list(): List<DownloadInfo>
@Query("SELECT * FROM DOWNLOADS ORDER BY TIME DESC LIMIT :limit OFFSET :offset")
fun list(offset: Int, limit: Int): List<DownloadInfo>
@@ -24,7 +24,7 @@ interface DownloadsDao : BasicDao<DownloadInfo> {
fun update(downloadInfo: DownloadInfo)
@Insert
override fun insert(t: DownloadInfo): Long
fun insert(t: DownloadInfo): Long
@Delete
fun delete(downloadInfo: DownloadInfo)
@@ -7,19 +7,19 @@ import androidx.room.Query
import androidx.room.Update
@Dao
interface FilterDao : BasicDao<Filter> {
interface FilterDao {
@Query("SELECT * FROM FILTER")
override fun list(): List<Filter>
suspend fun list(): List<Filter>
@Update
fun update(filter: Filter)
suspend fun update(filter: Filter)
@Insert
override fun insert(t: Filter): Long
suspend fun insert(t: Filter): Long
@Delete
fun delete(filter: Filter)
suspend fun delete(filter: Filter)
@Query("SELECT * FROM FILTER WHERE TEXT = :text AND MODE = :mode")
fun load(text: String, mode: Int): Filter?
suspend fun load(text: String, mode: Int): Filter?
}
@@ -8,31 +8,31 @@ import androidx.room.Query
import androidx.room.Update
@Dao
interface HistoryDao : BasicDao<HistoryInfo> {
interface HistoryDao {
@Query("SELECT * FROM HISTORY WHERE GID = :gid")
fun load(gid: Long): HistoryInfo?
suspend fun load(gid: Long): HistoryInfo?
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC")
override fun list(): List<HistoryInfo>
suspend fun list(): List<HistoryInfo>
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC LIMIT :limit OFFSET :offset")
fun list(offset: Int, limit: Int): List<HistoryInfo>
suspend fun list(offset: Int, limit: Int): List<HistoryInfo>
@Query("SELECT * FROM HISTORY ORDER BY TIME DESC")
fun listLazy(): PagingSource<Int, HistoryInfo>
@Update
fun update(historyInfo: HistoryInfo)
suspend fun update(historyInfo: HistoryInfo)
@Insert
override fun insert(t: HistoryInfo): Long
suspend fun insert(t: HistoryInfo): Long
@Delete
fun delete(historyInfo: HistoryInfo)
suspend fun delete(historyInfo: HistoryInfo)
@Delete
fun delete(historyInfo: List<HistoryInfo>)
suspend fun delete(historyInfo: List<HistoryInfo>)
@Query("DELETE FROM HISTORY")
fun deleteAll()
suspend fun deleteAll()
}
@@ -7,31 +7,28 @@ import androidx.room.Insert
import androidx.room.Query
@Dao
interface LocalFavoritesDao : BasicDao<LocalFavoriteInfo> {
interface LocalFavoritesDao {
@Query("SELECT * FROM LOCAL_FAVORITES ORDER BY TIME DESC")
override fun list(): List<LocalFavoriteInfo>
suspend fun list(): List<LocalFavoriteInfo>
@Query("SELECT * FROM LOCAL_FAVORITES ORDER BY TIME DESC")
fun listLazy(): PagingSource<Int, LocalFavoriteInfo>
@Query("SELECT * FROM LOCAL_FAVORITES WHERE TITLE LIKE :title ORDER BY TIME DESC")
fun list(title: String): List<LocalFavoriteInfo>
@Query("SELECT * FROM LOCAL_FAVORITES WHERE TITLE LIKE :title ORDER BY TIME DESC")
fun listLazy(title: String): PagingSource<Int, LocalFavoriteInfo>
@Query("SELECT * FROM LOCAL_FAVORITES WHERE GID = :gid")
fun load(gid: Long): LocalFavoriteInfo?
suspend fun load(gid: Long): LocalFavoriteInfo?
@Query("SELECT EXISTS(SELECT * FROM LOCAL_FAVORITES WHERE GID = :gid)")
fun contains(gid: Long): Boolean
suspend fun contains(gid: Long): Boolean
@Insert
override fun insert(t: LocalFavoriteInfo): Long
suspend fun insert(t: LocalFavoriteInfo): Long
@Delete
fun delete(localFavoriteInfo: LocalFavoriteInfo)
suspend fun delete(localFavoriteInfo: LocalFavoriteInfo)
@Query("DELETE FROM LOCAL_FAVORITES WHERE GID = :gid")
fun deleteByKey(gid: Long)
suspend fun deleteByKey(gid: Long)
}
@@ -7,22 +7,22 @@ import androidx.room.Query
import androidx.room.Update
@Dao
interface QuickSearchDao : BasicDao<QuickSearch> {
interface QuickSearchDao {
@Query("SELECT * FROM QUICK_SEARCH ORDER BY TIME ASC")
override fun list(): List<QuickSearch>
suspend fun list(): List<QuickSearch>
@Query("SELECT * FROM QUICK_SEARCH ORDER BY TIME ASC LIMIT :limit OFFSET :offset")
fun list(offset: Int, limit: Int): List<QuickSearch>
suspend fun list(offset: Int, limit: Int): List<QuickSearch>
@Update
fun update(downloadLabels: List<QuickSearch>)
suspend fun update(downloadLabels: List<QuickSearch>)
@Update
fun update(quickSearch: QuickSearch)
suspend fun update(quickSearch: QuickSearch)
@Insert
override fun insert(t: QuickSearch): Long
suspend fun insert(t: QuickSearch): Long
@Delete
fun delete(quickSearch: QuickSearch)
suspend fun delete(quickSearch: QuickSearch)
}
@@ -7,23 +7,23 @@ import androidx.room.Query
@Dao
interface SearchDao {
@Query("DELETE FROM suggestions WHERE `query` = :query")
fun deleteQuery(query: String)
suspend fun deleteQuery(query: String)
@Query("SELECT * FROM suggestions WHERE `query` LIKE :prefix || '%' ORDER BY date DESC LIMIT :limit")
fun rawSuggestions(prefix: String, limit: Int): Array<Search>
suspend fun rawSuggestions(prefix: String, limit: Int): Array<Search>
@Query("SELECT * FROM suggestions ORDER BY date DESC LIMIT :limit")
fun list(limit: Int): Array<Search>
suspend fun list(limit: Int): Array<Search>
@Insert
fun insert(search: Search)
suspend fun insert(search: Search)
fun addQuery(query: String) {
suspend fun addQuery(query: String) {
deleteQuery(query)
if (query.isBlank()) return
val search = Search(System.currentTimeMillis(), query)
insert(search)
}
fun suggestions(prefix: String, limit: Int) = (if (prefix.isBlank()) list(limit) else rawSuggestions(prefix, limit)).map { it.query }
suspend fun suggestions(prefix: String, limit: Int) = (if (prefix.isBlank()) list(limit) else rawSuggestions(prefix, limit)).map { it.query }
}
@@ -35,6 +35,7 @@ import com.hippo.ehviewer.util.ConcurrentPool
import com.hippo.ehviewer.util.LongList
import com.hippo.ehviewer.util.SimpleHandler
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -227,8 +228,10 @@ object DownloadManager : OnSpiderListener {
// Make sure download is running
ensureDownload()
// Add it to history
EhDB.putHistoryInfo(info)
launchIO {
// Add it to history
EhDB.putHistoryInfo(info)
}
}
}
@@ -619,7 +619,7 @@ class GalleryListScene : SearchBarScene() {
drawerBinding.recyclerViewDrawer.visibility = View.GONE
}
lifecycleScope.launchIO {
mQuickSearchList = EhDB.allQuickSearch as MutableList<QuickSearch>
mQuickSearchList = EhDB.getAllQuickSearch() as MutableList<QuickSearch>
if (mQuickSearchList.isNotEmpty()) {
withUIContext {
drawerBinding.tip.visibility = View.GONE