优化搜索代码

pull/346/head
gedoor 4 years ago
parent 2442e747d3
commit 757d01a699
  1. 111
      app/src/main/java/io/legado/app/model/webBook/SearchBookModel.kt
  2. 16
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
  3. 3
      app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt

@ -1,86 +1,109 @@
package io.legado.app.model.webBook package io.legado.app.model.webBook
import io.legado.app.App import io.legado.app.App
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.help.AppConfig 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 io.legado.app.utils.getPrefString
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.min
class SearchBookModel(private val scope: CoroutineScope, private val callBack: CallBack) { class SearchBookModel(private val scope: CoroutineScope, private val callBack: CallBack) {
private var searchPool = val threadCount = AppConfig.threadCount
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() private var searchPool: ExecutorCoroutineDispatcher? = null
private var mSearchId = System.currentTimeMillis() private var mSearchId = 0L
private var searchPage = 1 private var searchPage = 1
private var searchKey: String = "" private var searchKey: String = ""
private var task: Coroutine<*>? = null private var tasks = CompositeCoroutine()
private var bookSourceList = arrayListOf<BookSource>()
@Volatile
private var searchIndex = -1
private fun initSearchPool() { private fun initSearchPool() {
searchPool = searchPool = Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher()
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
} }
fun search(searchId: Long, key: String) { fun search(searchId: Long, key: String) {
callBack.onSearchStart()
if (searchId != mSearchId) { if (searchId != mSearchId) {
task?.cancel()
searchPool.close()
initSearchPool()
mSearchId = searchId
searchPage = 1
if (key.isEmpty()) { if (key.isEmpty()) {
callBack.onSearchCancel()
return return
} else { } else {
this.searchKey = key this.searchKey = key
} }
} else { if (mSearchId != 0L) {
searchPage++ close()
} }
task = Coroutine.async(scope, searchPool) { initSearchPool()
mSearchId = searchId
searchPage = 1
val searchGroup = App.INSTANCE.getPrefString("searchGroup") ?: "" val searchGroup = App.INSTANCE.getPrefString("searchGroup") ?: ""
val bookSourceList = if (searchGroup.isBlank()) { bookSourceList.clear()
App.db.bookSourceDao().allEnabled if (searchGroup.isBlank()) {
bookSourceList.addAll(App.db.bookSourceDao().allEnabled)
} else { } else {
App.db.bookSourceDao().getEnabledByGroup(searchGroup) bookSourceList.addAll(App.db.bookSourceDao().getEnabledByGroup(searchGroup))
} }
for (item in bookSourceList) { } else {
//task取消时自动取消 by (scope = this@execute) searchPage++
WebBook(item).searchBook( }
searchKey, for (i in 0 until threadCount) {
searchPage, search(searchId)
scope = this,
context = searchPool
)
.timeout(30000L)
.onSuccess(IO) {
if (searchId == mSearchId) {
callBack.onSearchSuccess(it)
}
}
}
}.onStart {
callBack.onSearchStart()
} }
}
task?.invokeOnCompletion { private fun search(searchId: Long) {
if (searchId == mSearchId) { synchronized(this) {
callBack.onSearchFinish() 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() { fun cancelSearch() {
task?.cancel() close()
mSearchId = 0
callBack.onSearchCancel() callBack.onSearchCancel()
} }
fun close() { fun close() {
task?.cancel() tasks.clear()
mSearchId = 0 searchPool?.close()
searchPool.close() mSearchId = 0L
} }
interface CallBack { interface CallBack {

@ -22,10 +22,11 @@ import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.debug import org.jetbrains.anko.debug
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.min
class ChangeSourceViewModel(application: Application) : BaseViewModel(application) { class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
val threadCount = AppConfig.threadCount val threadCount = AppConfig.threadCount
private lateinit var searchPool: ExecutorCoroutineDispatcher private var searchPool: ExecutorCoroutineDispatcher? = null
val handler = Handler() val handler = Handler()
val searchStateData = MutableLiveData<Boolean>() val searchStateData = MutableLiveData<Boolean>()
val searchBooksLiveData = MutableLiveData<List<SearchBook>>() val searchBooksLiveData = MutableLiveData<List<SearchBook>>()
@ -106,9 +107,12 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
private fun search() { private fun search() {
synchronized(this) { synchronized(this) {
if (searchIndex >= bookSourceList.lastIndex) {
return
}
searchIndex++ searchIndex++
val source = bookSourceList[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) .timeout(60000L)
.onSuccess(IO) { .onSuccess(IO) {
it.forEach { searchBook -> it.forEach { searchBook ->
@ -133,7 +137,9 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
} else { } else {
searchIndex++ searchIndex++
} }
if (searchIndex >= bookSourceList.lastIndex + threadCount) { if (searchIndex >= bookSourceList.lastIndex + min(bookSourceList.size,
threadCount)
) {
searchStateData.postValue(false) searchStateData.postValue(false)
} }
} }
@ -194,14 +200,14 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
startSearch() startSearch()
} else { } else {
tasks.clear() tasks.clear()
searchPool.close() searchPool?.close()
searchStateData.postValue(false) searchStateData.postValue(false)
} }
} }
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
searchPool.close() searchPool?.close()
} }
fun disableSource(searchBook: SearchBook) { fun disableSource(searchBook: SearchBook) {

@ -30,7 +30,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application),
* 开始搜索 * 开始搜索
*/ */
fun search(key: String) { fun search(key: String) {
if (searchKey != key && key.isNotEmpty()) { if ((searchKey != key && key.isEmpty()) || key.isNotEmpty()) {
searchBookModel.cancelSearch() searchBookModel.cancelSearch()
searchBooks.clear() searchBooks.clear()
searchBookLiveData.postValue(searchBooks) searchBookLiveData.postValue(searchBooks)
@ -54,6 +54,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application),
override fun onSearchStart() { override fun onSearchStart() {
isSearchLiveData.postValue(true) isSearchLiveData.postValue(true)
isLoading = true
} }
override fun onSearchSuccess(searchBooks: ArrayList<SearchBook>) { override fun onSearchSuccess(searchBooks: ArrayList<SearchBook>) {

Loading…
Cancel
Save