diff --git a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt index e230c53c9..5b6d695c4 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt @@ -1,6 +1,7 @@ package io.legado.app.ui.main import android.app.Application +import androidx.lifecycle.viewModelScope import io.legado.app.base.BaseViewModel import io.legado.app.constant.AppConst import io.legado.app.constant.AppLog @@ -16,8 +17,7 @@ import io.legado.app.model.CacheBook import io.legado.app.model.webBook.WebBook import io.legado.app.utils.postEvent import io.legado.app.utils.printOnDebug -import kotlinx.coroutines.asCoroutineDispatcher -import java.util.concurrent.ConcurrentHashMap +import kotlinx.coroutines.* import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.Executors import kotlin.math.min @@ -26,11 +26,9 @@ class MainViewModel(application: Application) : BaseViewModel(application) { private var threadCount = AppConfig.threadCount private var upTocPool = Executors.newFixedThreadPool(min(threadCount, AppConst.MAX_THREAD)).asCoroutineDispatcher() - val updateList = CopyOnWriteArraySet() - private val bookMap = ConcurrentHashMap() - - @Volatile - private var usePoolCount = 0 + val onUpTocBooks = CopyOnWriteArraySet() + private val waitUpTocBooks = arrayListOf() + private var upTocJob: Job? = null override fun onCleared() { super.onCleared() @@ -46,7 +44,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) { fun upAllBookToc() { execute { - upToc(appDb.bookDao.hasUpdateBooks) + addToWaitUp(appDb.bookDao.hasUpdateBooks) } } @@ -54,64 +52,98 @@ class MainViewModel(application: Application) : BaseViewModel(application) { execute(context = upTocPool) { books.filter { it.origin != BookType.local && it.canUpdate - }.forEach { - bookMap[it.bookUrl] = it - } - for (i in 0 until threadCount) { - if (usePoolCount < threadCount) { - usePoolCount++ - updateToc() - } + }.let { + addToWaitUp(it) } } } @Synchronized - private fun updateToc() { - var update = false - bookMap.forEach { bookEntry -> - if (!updateList.contains(bookEntry.key)) { - update = true - val book = bookEntry.value - synchronized(this) { - updateList.add(book.bookUrl) - postEvent(EventBus.UP_BOOKSHELF, book.bookUrl) - } - appDb.bookSourceDao.getBookSource(book.origin)?.let { bookSource -> - execute(context = upTocPool) { - if (book.tocUrl.isBlank()) { - WebBook.getBookInfoAwait(this, bookSource, book) - } - val toc = WebBook.getChapterListAwait(this, bookSource, book) - appDb.bookDao.update(book) - appDb.bookChapterDao.delByBook(book.bookUrl) - appDb.bookChapterDao.insert(*toc.toTypedArray()) - cacheBook(book) - }.onError(upTocPool) { - AppLog.addLog("${book.name} 更新目录失败\n${it.localizedMessage}", it) - it.printOnDebug() - }.onFinally(upTocPool) { - synchronized(this) { - bookMap.remove(bookEntry.key) - updateList.remove(book.bookUrl) - postEvent(EventBus.UP_BOOKSHELF, book.bookUrl) - upNext() - } + private fun addToWaitUp(books: List) { + books.forEach { book -> + if (!waitUpTocBooks.contains(book.bookUrl) && !onUpTocBooks.contains(book.bookUrl)) { + waitUpTocBooks.add(book.bookUrl) + } + } + if (upTocJob == null) { + startUpTocJob() + } + } + + private fun startUpTocJob() { + upTocJob = viewModelScope.launch(upTocPool) { + while (isActive) { + when { + waitUpTocBooks.isEmpty() -> { + upTocJob?.cancel() + upTocJob = null + } + onUpTocBooks.size < threadCount -> { + updateToc() + } + else -> { + delay(500) } - } ?: synchronized(this) { - bookMap.remove(bookEntry.key) - updateList.remove(book.bookUrl) - postEvent(EventBus.UP_BOOKSHELF, book.bookUrl) - upNext() } - return } } - if (!update) { - usePoolCount-- + } + + @Synchronized + private fun updateToc() { + val bookUrl = waitUpTocBooks.firstOrNull() ?: return + if (onUpTocBooks.contains(bookUrl)) { + waitUpTocBooks.remove(bookUrl) + return + } + val book = appDb.bookDao.getBook(bookUrl) + if (book == null) { + waitUpTocBooks.remove(bookUrl) + return + } + val source = appDb.bookSourceDao.getBookSource(book.origin) + if (source == null) { + waitUpTocBooks.remove(book.bookUrl) + return + } + waitUpTocBooks.remove(book.bookUrl) + onUpTocBooks.add(book.bookUrl) + postEvent(EventBus.UP_BOOKSHELF, book.bookUrl) + execute(context = upTocPool) { + if (book.tocUrl.isBlank()) { + WebBook.getBookInfoAwait(this, source, book) + } + val toc = WebBook.getChapterListAwait(this, source, book) + appDb.bookDao.update(book) + appDb.bookChapterDao.delByBook(book.bookUrl) + appDb.bookChapterDao.insert(*toc.toTypedArray()) + cacheBook(book) + }.onError(upTocPool) { + AppLog.addLog("${book.name} 更新目录失败\n${it.localizedMessage}", it) + it.printOnDebug() + }.onCancel(upTocPool) { + upTocCancel(book.bookUrl) + }.onFinally(upTocPool) { + upTocFinally(book.bookUrl) } } + @Synchronized + private fun upTocCancel(bookUrl: String) { + onUpTocBooks.remove(bookUrl) + waitUpTocBooks.add(bookUrl) + } + + @Synchronized + private fun upTocFinally(bookUrl: String) { + waitUpTocBooks.remove(bookUrl) + onUpTocBooks.remove(bookUrl) + postEvent(EventBus.UP_BOOKSHELF, bookUrl) + } + + /** + * 缓存书籍 + */ private fun cacheBook(book: Book) { val endIndex = min( book.totalChapterNum - 1, @@ -128,14 +160,6 @@ class MainViewModel(application: Application) : BaseViewModel(application) { } - private fun upNext() { - if (bookMap.size > updateList.size) { - updateToc() - } else { - usePoolCount-- - } - } - fun postLoad() { execute { if (appDb.httpTTSDao.count == 0) { diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt index 0ae3e207b..6bef0a120 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt @@ -163,7 +163,7 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), } override fun isUpdate(bookUrl: String): Boolean { - return bookUrl in activityViewModel.updateList + return bookUrl in activityViewModel.onUpTocBooks } @SuppressLint("NotifyDataSetChanged") diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt index cd3dd8076..4647e5ec3 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt @@ -200,7 +200,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), } override fun isUpdate(bookUrl: String): Boolean { - return bookUrl in activityViewModel.updateList + return bookUrl in activityViewModel.onUpTocBooks } override fun getItemCount(): Int {