[优化] 优化性能

This commit is contained in:
HapeLee
2026-04-26 15:13:50 +08:00
parent 64374fd4e7
commit a3674ac5d9
6 changed files with 129 additions and 29 deletions
@@ -4,7 +4,6 @@ import io.legado.app.data.AppDatabase
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.rule.ExploreKind
import io.legado.app.help.book.isNotShelf
import io.legado.app.help.source.exploreKinds
import io.legado.app.model.webBook.WebBook
import kotlinx.coroutines.Dispatchers
@@ -26,9 +25,19 @@ class ExploreRepositoryImpl(
) : ExploreRepository {
override fun getBookshelfItems(): Flow<List<SearchBook>> {
return appDb.bookDao.flowAll().map { books ->
return appDb.bookDao.flowBookShelf().map { books ->
books.filterNot { it.isNotShelf }
.map { it.toSearchBook() }
.map {
SearchBook(
bookUrl = it.bookUrl,
name = it.name,
author = it.author,
originName = it.originName,
type = it.type,
coverUrl = it.coverUrl,
latestChapterTitle = it.latestChapterTitle
)
}
}
}
@@ -56,4 +65,4 @@ class ExploreRepositoryImpl(
override suspend fun saveSearchBooks(books: List<SearchBook>) {
appDb.searchBookDao.insert(*books.toTypedArray())
}
}
}
@@ -24,6 +24,7 @@ object AppFreezeMonitor {
}
private var registeredReceiver = false
private var monitorRunnable: Runnable? = null
@SuppressLint("UnspecifiedRegisterReceiverFlag")
fun init(context: Context) {
@@ -32,6 +33,10 @@ object AppFreezeMonitor {
registeredReceiver = false
context.unregisterReceiver(screenStatusReceiver)
}
monitorRunnable?.let {
handler.removeCallbacks(it)
monitorRunnable = null
}
return
}
@@ -40,6 +45,7 @@ object AppFreezeMonitor {
context.registerReceiver(screenStatusReceiver, screenStatusReceiver.filter)
}
if (monitorRunnable != null) return
var previous = SystemClock.uptimeMillis()
val runnable = object : Runnable {
@@ -56,9 +62,12 @@ object AppFreezeMonitor {
if (AppConfig.recordLog) {
handler.postDelayed(this, 3000)
} else {
monitorRunnable = null
}
}
}
monitorRunnable = runnable
handler.postDelayed(runnable, 3000)
}
@@ -428,28 +428,87 @@ object BookHelp {
var ret = true
val op = BitmapFactory.Options()
op.inJustDecodeBounds = true
getContent(book, bookChapter)?.let {
val matcher = AppPattern.imgPattern.matcher(it)
while (matcher.find()) {
val src = matcher.group(1)!!
val image = getImage(book, src)
if (!image.exists()) {
ret = false
continue
}
BitmapFactory.decodeFile(image.absolutePath, op)
if (op.outWidth < 1 && op.outHeight < 1) {
if (SvgUtils.getSize(image.absolutePath) != null) {
continue
}
ret = false
image.delete()
forEachImageSrc(book, bookChapter) { src ->
val image = getImage(book, src)
if (!image.exists()) {
ret = false
return@forEachImageSrc
}
BitmapFactory.decodeFile(image.absolutePath, op)
if (op.outWidth < 1 && op.outHeight < 1) {
if (SvgUtils.getSize(image.absolutePath) != null) {
return@forEachImageSrc
}
ret = false
image.delete()
}
}
return ret
}
private inline fun forEachImageSrc(
book: Book,
bookChapter: BookChapter,
action: (String) -> Unit
) {
val file = downloadDir.getFile(
cacheFolderName,
book.getFolderName(),
bookChapter.getFileName()
)
if (file.exists()) {
forEachImageSrc(file, action)
return
}
getContent(book, bookChapter)?.let { content ->
val matcher = AppPattern.imgPattern.matcher(content)
while (matcher.find()) {
action(matcher.group(1) ?: continue)
}
}
}
private inline fun forEachImageSrc(file: File, action: (String) -> Unit) {
val chars = CharArray(8192)
val buffer = StringBuilder()
file.bufferedReader().use { reader ->
while (true) {
val readSize = reader.read(chars)
if (readSize < 0) break
buffer.append(chars, 0, readSize)
consumeImageSrcMatches(buffer, action)
trimImageScanBuffer(buffer)
}
consumeImageSrcMatches(buffer, action)
}
}
private inline fun consumeImageSrcMatches(
buffer: StringBuilder,
action: (String) -> Unit
) {
val matcher = AppPattern.imgPattern.matcher(buffer)
var lastEnd = 0
while (matcher.find()) {
action(matcher.group(1) ?: continue)
lastEnd = matcher.end()
}
if (lastEnd > 0) {
buffer.delete(0, lastEnd)
}
}
private fun trimImageScanBuffer(buffer: StringBuilder) {
val maxBufferSize = 16 * 1024
if (buffer.length <= maxBufferSize) return
val partialImgStart = buffer.lastIndexOf("<img")
val keepFrom = when {
partialImgStart >= 0 -> partialImgStart
else -> max(buffer.length - 1024, 0)
}
buffer.delete(0, keepFrom)
}
private fun checkImage(bytes: ByteArray): Boolean {
val op = BitmapFactory.Options()
op.inJustDecodeBounds = true
@@ -103,6 +103,8 @@ object CacheBook {
private val _downloadErrorFlow =
MutableStateFlow<Pair<String, Set<Int>>>("" to emptySet())
val downloadErrorFlow = _downloadErrorFlow.asStateFlow()
@Volatile
private var lastQueueStats = QueueStats(0, 0)
val successDownloadSet = ConcurrentHashMap.newKeySet<String>()
val errorDownloadMap = ConcurrentHashMap<String, Int>()
@@ -123,7 +125,10 @@ object CacheBook {
}
private fun updateSummary() {
_downloadSummaryFlow.value = downloadSummary
val stats = collectQueueStats()
lastQueueStats = stats
_downloadSummaryFlow.value =
"正在下载:${stats.downloadingCount}|等待中:${stats.waitingCount}|失败:${errorDownloadMap.size}|成功:${successDownloadSet.size}"
}
@Synchronized
@@ -213,12 +218,7 @@ object CacheBook {
}
val isRun: Boolean
get() {
cacheBookMap.forEach { (_, model) ->
if (model.isRun()) return true
}
return false
}
get() = lastQueueStats.waitingCount > 0 || lastQueueStats.downloadingCount > 0
private fun onTaskQueuesChanged(bookUrl: String) {
updateSummary()
@@ -21,6 +21,7 @@ import io.legado.app.model.CacheBook
import io.legado.app.help.config.LocalConfig
import io.legado.app.service.ExportBookService
import io.legado.app.ui.config.cacheConfig.CacheConfig
import io.legado.app.ui.main.bookshelf.toLightBook
import io.legado.app.utils.cnCompare
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
@@ -219,8 +220,8 @@ class CacheViewModel(
private fun observeBooks(groupId: Long) {
booksJob?.cancel()
booksJob = viewModelScope.launch {
bookDao.flowByGroup(groupId).map { books ->
val booksDownload = books.filter { !it.isAudio }
bookDao.flowBookShelfByGroup(groupId).map { books ->
val booksDownload = books.filter { !it.isAudio }.map { it.toLightBook() }
when (cacheConfig.getBookSortByGroupId(groupId)) {
1 -> booksDownload.sortedByDescending { it.latestChapterTime }
2 -> booksDownload.sortedWith { o1, o2 -> o1.name.cnCompare(o2.name) }
@@ -2,6 +2,7 @@ package io.legado.app.ui.main.bookshelf
import androidx.compose.runtime.Stable
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book
import kotlin.math.max
@Stable
@@ -40,3 +41,24 @@ data class BookShelfItem(
fun getUnreadChapterNum() = max(totalChapterNum - durChapterIndex - 1, 0)
}
fun BookShelfItem.toLightBook() = Book(
bookUrl = bookUrl,
originName = originName,
name = name,
author = author,
coverUrl = coverUrl,
customCoverUrl = customCoverUrl,
latestChapterTitle = latestChapterTitle,
latestChapterTime = latestChapterTime,
lastCheckCount = lastCheckCount,
totalChapterNum = totalChapterNum,
durChapterTitle = durChapterTitle,
durChapterIndex = durChapterIndex,
durChapterPos = durChapterPos,
durChapterTime = durChapterTime,
type = type,
group = group,
order = order,
canUpdate = canUpdate
)