某些设备的ANR问题

This commit is contained in:
HapeLee
2026-05-12 19:16:20 +08:00
parent 83a0a794e2
commit 890ab3a502
4 changed files with 58 additions and 38 deletions
@@ -63,6 +63,7 @@ class CacheBookModel(
private var isLoading = false
private var isPaused = false
@Synchronized
private fun notifyDownloadSetChanged() {
host.emitDownloadingIndices(book.bookUrl, onDownloadSet.toSet())
if (host.cacheBookMap[book.bookUrl] === this) {
@@ -75,6 +76,7 @@ class CacheBookModel(
}
}
@Synchronized
private fun notifyErrorChanged() {
val errors = host.errorIndices(book.bookUrl)
host.emitDownloadError(book.bookUrl, errors)
@@ -260,41 +262,43 @@ class CacheBookModel(
addDownload(index, index)
}
@Synchronized
private fun onSuccess(chapter: BookChapter) {
onDownloadSet.remove(chapter.index)
chapterTasks.remove(chapter.index)
host.incrementSuccessCount()
retryCountMap.remove(chapter.index)
host.stateStore.markSuccess(book.bookUrl, chapter.index)
synchronized(this) {
onDownloadSet.remove(chapter.index)
chapterTasks.remove(chapter.index)
host.incrementSuccessCount()
retryCountMap.remove(chapter.index)
host.stateStore.markSuccess(book.bookUrl, chapter.index)
}
notifyDownloadSetChanged()
notifyErrorChanged()
host.emitChapterCached(chapter)
}
@Synchronized
private fun onPreError(chapter: BookChapter, error: Throwable) {
waitingRetry = true
if (error !is ConcurrentException) {
retryCountMap[chapter.index] = (retryCountMap[chapter.index] ?: 0) + 1
host.stateStore.markFailed(book.bookUrl, chapter.index)
synchronized(this) {
waitingRetry = true
if (error !is ConcurrentException) {
retryCountMap[chapter.index] = (retryCountMap[chapter.index] ?: 0) + 1
host.stateStore.markFailed(book.bookUrl, chapter.index)
}
onDownloadSet.remove(chapter.index)
chapterTasks.remove(chapter.index)
}
onDownloadSet.remove(chapter.index)
chapterTasks.remove(chapter.index)
}
@Synchronized
private fun onPostError(chapter: BookChapter, error: Throwable) {
val retryCount = retryCountMap[chapter.index] ?: 0
if (retryCount < 3 && !isStopped) {
queue.enqueue(ChapterSelection.Single(chapter.index))
} else {
AppLog.put("下载${book.name}-${chapter.title}失败\n${error.localizedMessage}", error)
synchronized(this) {
val retryCount = retryCountMap[chapter.index] ?: 0
if (retryCount < 3 && !isStopped) {
queue.enqueue(ChapterSelection.Single(chapter.index))
} else {
AppLog.put("下载${book.name}-${chapter.title}失败\n${error.localizedMessage}", error)
}
waitingRetry = false
}
waitingRetry = false
}
@Synchronized
private fun onError(chapter: BookChapter, error: Throwable) {
onPreError(chapter, error)
onPostError(chapter, error)
@@ -302,21 +306,25 @@ class CacheBookModel(
notifyErrorChanged()
}
@Synchronized
private fun onCancel(index: Int, requeue: Boolean = true) {
onDownloadSet.remove(index)
chapterTasks.remove(index)
val wasCanceled = canceledDownloadSet.remove(index)
if (requeue && !isStopped && !wasCanceled) {
queue.enqueue(ChapterSelection.Single(index))
synchronized(this) {
onDownloadSet.remove(index)
chapterTasks.remove(index)
val wasCanceled = canceledDownloadSet.remove(index)
if (requeue && !isStopped && !wasCanceled) {
queue.enqueue(ChapterSelection.Single(index))
}
}
notifyDownloadSetChanged()
}
@Synchronized
private fun onFinally() {
val shouldRemove: Boolean
val bookUrl = book.bookUrl
if (queue.waitingCount() == 0 && onDownloadSet.isEmpty() && pausedChapterSet.isEmpty()) {
synchronized(this) {
shouldRemove = queue.waitingCount() == 0 && onDownloadSet.isEmpty() && pausedChapterSet.isEmpty()
}
if (shouldRemove) {
host.onTaskRemoved(bookUrl)
} else {
host.onTaskQueuesChanged(bookUrl)
@@ -207,8 +207,10 @@ object ImageProvider {
}.getOrDefault(errorBitmap)
}
fun clear() {
bitmapLruCache.evictAll()
suspend fun clear() {
withContext(IO) {
bitmapLruCache.evictAll()
}
}
}
@@ -1108,10 +1108,13 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
preDownloadTask?.cancel()
downloadScope.coroutineContext.cancelChildren()
coroutineContext.cancelChildren()
ImageProvider.clear()
clearExpiredChapterLoadingJob(true)
if (!CacheBookService.isRun) {
CacheBook.close()
// Move expensive cleanup off the main thread
CoroutineScope(SupervisorJob() + IO).launch {
ImageProvider.clear()
if (!CacheBookService.isRun) {
CacheBook.close()
}
}
}
@@ -212,9 +212,16 @@ class BookshelfViewModel(
}
combine(flows) { it.toMap() }
}
}.flowOn(Dispatchers.Default)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap())
private val allGroupBooksImmutableFlow: StateFlow<ImmutableMap<Long, ImmutableList<BookShelfItem>>> =
allGroupBooksFlow.map { map ->
map.mapValues { it.value.toImmutableList() }.toImmutableMap()
}.flowOn(Dispatchers.Default)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), persistentMapOf())
private val visibleBooksFlow = combine(
booksFlow,
searchKeyFlow,
@@ -366,7 +373,7 @@ class BookshelfViewModel(
) { books, groups, allGroups, previews, internal ->
BookshelfDataCore(books, groups, allGroups, previews, internal)
},
allGroupBooksFlow
allGroupBooksImmutableFlow
) { core, allGroupBooks ->
BookshelfDataState(
books = core.books,
@@ -392,7 +399,7 @@ class BookshelfViewModel(
val allGroups: List<BookGroupUi>,
val previews: GroupPreviewState,
val internal: InternalState,
val allGroupBooks: Map<Long, List<BookShelfItem>>
val allGroupBooks: ImmutableMap<Long, ImmutableList<BookShelfItem>>
)
val uiState: StateFlow<BookshelfUiState> = combine(
@@ -453,7 +460,7 @@ class BookshelfViewModel(
currentGroupName = currentGroupName,
draggingBooks = interaction.draggingBooks?.toImmutableList(),
pendingSavedBooks = interaction.pendingSavedBooks?.toImmutableList(),
allGroupBooks = data.allGroupBooks.mapValues { it.value.toImmutableList() }.toImmutableMap()
allGroupBooks = data.allGroupBooks
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), BookshelfUiState())