diff --git a/app/src/main/java/io/legado/app/model/webBook/SearchBookModel.kt b/app/src/main/java/io/legado/app/model/webBook/SearchBookModel.kt index 0cb36d1c7..c252c567f 100644 --- a/app/src/main/java/io/legado/app/model/webBook/SearchBookModel.kt +++ b/app/src/main/java/io/legado/app/model/webBook/SearchBookModel.kt @@ -1,86 +1,109 @@ package io.legado.app.model.webBook import io.legado.app.App +import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.SearchBook import io.legado.app.help.AppConfig -import io.legado.app.help.coroutine.Coroutine +import io.legado.app.help.coroutine.CompositeCoroutine import io.legado.app.utils.getPrefString import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher import java.util.concurrent.Executors +import kotlin.math.min class SearchBookModel(private val scope: CoroutineScope, private val callBack: CallBack) { - private var searchPool = - Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() - private var mSearchId = System.currentTimeMillis() + val threadCount = AppConfig.threadCount + private var searchPool: ExecutorCoroutineDispatcher? = null + private var mSearchId = 0L private var searchPage = 1 private var searchKey: String = "" - private var task: Coroutine<*>? = null + private var tasks = CompositeCoroutine() + private var bookSourceList = arrayListOf() + + @Volatile + private var searchIndex = -1 private fun initSearchPool() { - searchPool = - Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() + searchPool = Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher() } fun search(searchId: Long, key: String) { + callBack.onSearchStart() if (searchId != mSearchId) { - task?.cancel() - searchPool.close() - initSearchPool() - mSearchId = searchId - searchPage = 1 if (key.isEmpty()) { + callBack.onSearchCancel() return } else { this.searchKey = key } - } else { - searchPage++ - } - task = Coroutine.async(scope, searchPool) { + if (mSearchId != 0L) { + close() + } + initSearchPool() + mSearchId = searchId + searchPage = 1 val searchGroup = App.INSTANCE.getPrefString("searchGroup") ?: "" - val bookSourceList = if (searchGroup.isBlank()) { - App.db.bookSourceDao().allEnabled + bookSourceList.clear() + if (searchGroup.isBlank()) { + bookSourceList.addAll(App.db.bookSourceDao().allEnabled) } else { - App.db.bookSourceDao().getEnabledByGroup(searchGroup) + bookSourceList.addAll(App.db.bookSourceDao().getEnabledByGroup(searchGroup)) } - for (item in bookSourceList) { - //task取消时自动取消 by (scope = this@execute) - WebBook(item).searchBook( - searchKey, - searchPage, - scope = this, - context = searchPool - ) - .timeout(30000L) - .onSuccess(IO) { - if (searchId == mSearchId) { - callBack.onSearchSuccess(it) - } - } - } - }.onStart { - callBack.onSearchStart() + } else { + searchPage++ + } + for (i in 0 until threadCount) { + search(searchId) } + } - task?.invokeOnCompletion { - if (searchId == mSearchId) { - callBack.onSearchFinish() + private fun search(searchId: Long) { + synchronized(this) { + if (searchIndex >= bookSourceList.lastIndex) { + return } + searchIndex++ + val source = bookSourceList[searchIndex] + val task = WebBook(source).searchBook( + searchKey, + searchPage, + scope = scope, + context = searchPool!! + ).timeout(30000L) + .onSuccess(IO) { + if (searchId == mSearchId) { + callBack.onSearchSuccess(it) + } + } + .onFinally { + synchronized(this) { + if (searchIndex < bookSourceList.lastIndex) { + search(searchId) + } else { + searchIndex++ + } + if (searchIndex >= bookSourceList.lastIndex + min(bookSourceList.size, + threadCount) + ) { + callBack.onSearchFinish() + } + } + } + tasks.add(task) } } fun cancelSearch() { - task?.cancel() - mSearchId = 0 + close() callBack.onSearchCancel() } fun close() { - task?.cancel() - mSearchId = 0 - searchPool.close() + tasks.clear() + searchPool?.close() + mSearchId = 0L } interface CallBack { diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt index 4b50151ef..8efb57093 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt @@ -22,10 +22,11 @@ import kotlinx.coroutines.asCoroutineDispatcher import org.jetbrains.anko.debug import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.Executors +import kotlin.math.min class ChangeSourceViewModel(application: Application) : BaseViewModel(application) { val threadCount = AppConfig.threadCount - private lateinit var searchPool: ExecutorCoroutineDispatcher + private var searchPool: ExecutorCoroutineDispatcher? = null val handler = Handler() val searchStateData = MutableLiveData() val searchBooksLiveData = MutableLiveData>() @@ -106,9 +107,12 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio private fun search() { synchronized(this) { + if (searchIndex >= bookSourceList.lastIndex) { + return + } searchIndex++ val source = bookSourceList[searchIndex] - val task = WebBook(source).searchBook(name, scope = this, context = searchPool) + val task = WebBook(source).searchBook(name, scope = this, context = searchPool!!) .timeout(60000L) .onSuccess(IO) { it.forEach { searchBook -> @@ -133,7 +137,9 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio } else { searchIndex++ } - if (searchIndex >= bookSourceList.lastIndex + threadCount) { + if (searchIndex >= bookSourceList.lastIndex + min(bookSourceList.size, + threadCount) + ) { searchStateData.postValue(false) } } @@ -194,14 +200,14 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio startSearch() } else { tasks.clear() - searchPool.close() + searchPool?.close() searchStateData.postValue(false) } } override fun onCleared() { super.onCleared() - searchPool.close() + searchPool?.close() } fun disableSource(searchBook: SearchBook) { diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt index e06c6f81a..564b2bb3e 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt @@ -30,7 +30,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application), * 开始搜索 */ fun search(key: String) { - if (searchKey != key && key.isNotEmpty()) { + if ((searchKey != key && key.isEmpty()) || key.isNotEmpty()) { searchBookModel.cancelSearch() searchBooks.clear() searchBookLiveData.postValue(searchBooks) @@ -54,6 +54,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application), override fun onSearchStart() { isSearchLiveData.postValue(true) + isLoading = true } override fun onSearchSuccess(searchBooks: ArrayList) {