From 7bd764a7f37275ca5215d610951348e7c6639fa6 Mon Sep 17 00:00:00 2001 From: Horis <8674809+821938089@users.noreply.github.com> Date: Mon, 28 Apr 2025 11:11:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/base/BaseService.kt | 4 ++- .../java/io/legado/app/base/BaseViewModel.kt | 9 ++++-- .../java/io/legado/app/data/dao/BookDao.kt | 3 ++ .../io/legado/app/help/coroutine/Coroutine.kt | 18 ++++++++--- .../io/legado/app/model/webBook/WebBook.kt | 18 ++++++----- .../ui/main/bookshelf/BookshelfViewModel.kt | 31 ++++++++++++------- 6 files changed, 55 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/io/legado/app/base/BaseService.kt b/app/src/main/java/io/legado/app/base/BaseService.kt index 698a70505..3f98e01a2 100644 --- a/app/src/main/java/io/legado/app/base/BaseService.kt +++ b/app/src/main/java/io/legado/app/base/BaseService.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.isActive +import kotlinx.coroutines.sync.Semaphore import kotlin.coroutines.CoroutineContext abstract class BaseService : LifecycleService() { @@ -28,8 +29,9 @@ abstract class BaseService : LifecycleService() { context: CoroutineContext = Dispatchers.IO, start: CoroutineStart = CoroutineStart.DEFAULT, executeContext: CoroutineContext = Dispatchers.Main, + semaphore: Semaphore? = null, block: suspend CoroutineScope.() -> T - ) = Coroutine.async(scope, context, start, executeContext, block) + ) = Coroutine.async(scope, context, start, executeContext, semaphore, block) @CallSuper override fun onCreate() { diff --git a/app/src/main/java/io/legado/app/base/BaseViewModel.kt b/app/src/main/java/io/legado/app/base/BaseViewModel.kt index cc9ea933b..92edd1bc0 100644 --- a/app/src/main/java/io/legado/app/base/BaseViewModel.kt +++ b/app/src/main/java/io/legado/app/base/BaseViewModel.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.sync.Semaphore import kotlin.coroutines.CoroutineContext @Suppress("unused") @@ -22,18 +23,22 @@ open class BaseViewModel(application: Application) : AndroidViewModel(applicatio context: CoroutineContext = Dispatchers.IO, start: CoroutineStart = CoroutineStart.DEFAULT, executeContext: CoroutineContext = Dispatchers.Main, + semaphore: Semaphore? = null, block: suspend CoroutineScope.() -> T ): Coroutine { - return Coroutine.async(scope, context, start, executeContext, block) + return Coroutine.async(scope, context, start, executeContext, semaphore, block) } fun executeLazy( scope: CoroutineScope = viewModelScope, context: CoroutineContext = Dispatchers.IO, executeContext: CoroutineContext = Dispatchers.Main, + semaphore: Semaphore? = null, block: suspend CoroutineScope.() -> T ): Coroutine { - return Coroutine.async(scope, context, CoroutineStart.LAZY, executeContext, block) + return Coroutine.async( + scope, context, CoroutineStart.LAZY, executeContext, semaphore, block + ) } fun submit( diff --git a/app/src/main/java/io/legado/app/data/dao/BookDao.kt b/app/src/main/java/io/legado/app/data/dao/BookDao.kt index cc4bb85ef..a809d31ce 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookDao.kt @@ -132,6 +132,9 @@ interface BookDao { @Query("select exists(select 1 from books where bookUrl = :bookUrl)") fun has(bookUrl: String): Boolean + @Query("select exists(select 1 from books where name = :name and author = :author)") + fun has(name: String, author: String): Boolean + @Query( """select exists(select 1 from books where type & ${BookType.local} > 0 and (originName = :fileName or (origin != '${BookType.localTag}' and origin like '%' || :fileName)))""" diff --git a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt index a42af28c0..f2bb9ed08 100644 --- a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt +++ b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.plus +import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import kotlin.coroutines.CoroutineContext @@ -23,10 +24,11 @@ import kotlin.coroutines.CoroutineContext */ @Suppress("unused", "MemberVisibilityCanBePrivate") class Coroutine( - val scope: CoroutineScope, + private val scope: CoroutineScope, context: CoroutineContext = Dispatchers.IO, - val startOption: CoroutineStart = CoroutineStart.DEFAULT, - val executeContext: CoroutineContext = Dispatchers.Main, + private val startOption: CoroutineStart = CoroutineStart.DEFAULT, + private val executeContext: CoroutineContext = Dispatchers.Main, + private val semaphore: Semaphore? = null, block: suspend CoroutineScope.() -> T ) { @@ -39,9 +41,10 @@ class Coroutine( context: CoroutineContext = Dispatchers.IO, start: CoroutineStart = CoroutineStart.DEFAULT, executeContext: CoroutineContext = Dispatchers.Main, + semaphore: Semaphore? = null, block: suspend CoroutineScope.() -> T ): Coroutine { - return Coroutine(scope, context, start, executeContext, block) + return Coroutine(scope, context, start, executeContext, semaphore, block) } } @@ -169,6 +172,7 @@ class Coroutine( block: suspend CoroutineScope.() -> T ): Job { return (scope.plus(executeContext)).launch(start = startOption) { + semaphore?.acquire() try { start?.let { dispatchVoidCallback(this, it) } ensureActive() @@ -185,7 +189,11 @@ class Coroutine( error?.let { dispatchCallback(this, e, it) } } } finally { - finally?.let { dispatchVoidCallback(this, it) } + try { + finally?.let { dispatchVoidCallback(this, it) } + } finally { + semaphore?.release() + } } } } diff --git a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt index 6759fb0ff..7537a33f3 100644 --- a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt @@ -22,7 +22,6 @@ import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ensureActive import kotlinx.coroutines.sync.Semaphore -import kotlinx.coroutines.sync.withPermit import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext @@ -283,12 +282,14 @@ object WebBook { executeContext: CoroutineContext = Dispatchers.Main, semaphore: Semaphore? = null, ): Coroutine { - return Coroutine.async(scope, context, start = start, executeContext = executeContext) { - semaphore?.withPermit { - getContentAwait(bookSource, book, bookChapter, nextChapterUrl, needSave) - } ?: run { - getContentAwait(bookSource, book, bookChapter, nextChapterUrl, needSave) - } + return Coroutine.async( + scope, + context, + start = start, + executeContext = executeContext, + semaphore = semaphore + ) { + getContentAwait(bookSource, book, bookChapter, nextChapterUrl, needSave) } } @@ -360,8 +361,9 @@ object WebBook { name: String, author: String, context: CoroutineContext = Dispatchers.IO, + semaphore: Semaphore? = null, ): Coroutine> { - return Coroutine.async(scope, context) { + return Coroutine.async(scope, context, semaphore = semaphore) { for (s in bookSourceParts) { val source = s.getBookSource() ?: continue val book = preciseSearchAwait(source, name, author).getOrNull() diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt index 4b40d9623..9f8f0c5b5 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt @@ -10,6 +10,7 @@ import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookSourcePart import io.legado.app.exception.NoStackTraceException +import io.legado.app.help.config.AppConfig import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.http.decompressed import io.legado.app.help.http.newCallResponseBody @@ -24,7 +25,8 @@ import io.legado.app.utils.isAbsUrl import io.legado.app.utils.isJsonArray import io.legado.app.utils.printOnDebug import io.legado.app.utils.toastOnUi -import kotlinx.coroutines.isActive +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withPermit import java.io.File import java.io.FileOutputStream import java.io.OutputStreamWriter @@ -154,21 +156,26 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application) private fun importBookshelfByJson(json: String, groupId: Long) { execute { val bookSourceParts = appDb.bookSourceDao.allEnabledPart + val semaphore = Semaphore(AppConfig.threadCount + 1) GSON.fromJsonArray>(json).getOrThrow().forEach { bookInfo -> - if (!isActive) return@execute val name = bookInfo["name"] ?: "" val author = bookInfo["author"] ?: "" - if (name.isNotEmpty() && appDb.bookDao.getBook(name, author) == null) { - WebBook.preciseSearch(this, bookSourceParts, name, author) - .onSuccess { - val book = it.first - if (groupId > 0) { - book.group = groupId - } - book.save() - }.onError { e -> - context.toastOnUi(e.localizedMessage) + if (name.isEmpty() || appDb.bookDao.has(name, author)) { + return@forEach + } + semaphore.withPermit { + WebBook.preciseSearch( + this, bookSourceParts, name, author, + semaphore = semaphore + ).onSuccess { + val book = it.first + if (groupId > 0) { + book.group = groupId } + book.save() + }.onError { e -> + context.toastOnUi(e.localizedMessage) + } } } }.onError {