某些设备的ANR问题

This commit is contained in:
HapeLee committed 2026-05-12 19:16:20 +08:00
1 parent 83a0a794e2
commit 890ab3a502
4 files changed
+58 -38

No files matched your search

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