diff --git a/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt b/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt index 30f8f794f..d39022f7e 100644 --- a/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt +++ b/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt @@ -198,8 +198,21 @@ abstract class CommonRecyclerAdapter(protected val context: Context) : Rec synchronized(lock) { val size = getActualItemCount() if (oldPosition in 0 until size && newPosition in 0 until size) { - Collections.swap(this.items, oldPosition + getHeaderCount(), newPosition + getHeaderCount()) - notifyDataSetChanged() + val srcPosition = oldPosition + getHeaderCount() + val targetPosition = newPosition + getHeaderCount() + Collections.swap(this.items, srcPosition, targetPosition) + notifyItemChanged(srcPosition) + notifyItemChanged(targetPosition) + } + } + } + + fun updateItem(item: ITEM) { + synchronized(lock) { + val index = this.items.indexOf(item) + if (index >= 0) { + this.items[index] = item + notifyItemChanged(index) } } } diff --git a/app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt b/app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt index b4cd06a2f..2ac1090f2 100644 --- a/app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt +++ b/app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt @@ -7,7 +7,7 @@ import android.content.Context * * item代理, */ -abstract class ItemViewDelegate(protected val context: Context, val layoutId: Int) { +abstract class ItemViewDelegate(protected val context: Context, val layoutId: Int) { abstract fun convert(holder: ItemViewHolder, item: ITEM, payloads: MutableList) 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 26d1c7c5e..030295275 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 @@ -30,6 +30,15 @@ class Coroutine() { private var errorReturn: Result? = null + val isCancelled: Boolean + get() = job?.isCancelled ?: false + + val isActive: Boolean + get() = job?.isActive ?: false + + val isCompleted: Boolean + get() = job?.isCompleted ?: false + private constructor( scope: CoroutineScope, block: suspend CoroutineScope.() -> T @@ -106,24 +115,24 @@ class Coroutine() { private suspend fun executeInternal(block: suspend CoroutineScope.() -> T) { tryCatch( { - start?.let { it() } + start?.invoke(this) val result = executeBlock(block, timeMillis ?: 0L) - success?.let { it(result) } + success?.invoke(this, result) }, { e -> val consume: Boolean = errorReturn?.value?.let { value -> - success?.let { it(value) } + success?.invoke(this, value) true } ?: false if (!consume) { - error?.let { it(e) } + error?.invoke(this, e) } }, { - finally?.let { it() } + finally?.invoke(this) }) } @@ -142,9 +151,9 @@ class Coroutine() { try { coroutineScope { tryBlock() } } catch (e: Throwable) { - coroutineScope { errorBlock?.let { it(e) } } + coroutineScope { errorBlock?.invoke(this, e) } } finally { - coroutineScope { finallyBlock?.let { it() } } + coroutineScope { finallyBlock?.invoke(this) } } } diff --git a/app/src/main/java/io/legado/app/model/WebBook.kt b/app/src/main/java/io/legado/app/model/WebBook.kt index 242510ee9..43882e881 100644 --- a/app/src/main/java/io/legado/app/model/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/WebBook.kt @@ -13,6 +13,9 @@ import io.legado.app.model.webbook.BookList class WebBook(private val bookSource: BookSource) { + val sourceUrl: String + get() = bookSource.bookSourceUrl + fun searchBook(key: String, page: Int?, isSearch: Boolean = true): Coroutine> { return Coroutine.async { bookSource.getSearchRule().searchUrl?.let { searchUrl -> diff --git a/app/src/main/java/io/legado/app/model/webbook/SourceDebug.kt b/app/src/main/java/io/legado/app/model/webbook/SourceDebug.kt index 2155cf3c5..03e9d0446 100644 --- a/app/src/main/java/io/legado/app/model/webbook/SourceDebug.kt +++ b/app/src/main/java/io/legado/app/model/webbook/SourceDebug.kt @@ -4,23 +4,26 @@ import android.annotation.SuppressLint import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp +import io.legado.app.help.coroutine.Coroutine import io.legado.app.model.WebBook import io.legado.app.utils.htmlFormat +import io.legado.app.utils.isAbsUrl import java.text.SimpleDateFormat import java.util.* class SourceDebug(private val webBook: WebBook, callback: Callback) { companion object { - var debugSource: String? = null - var callback: Callback? = null + private var debugSource: String? = null + private var callback: Callback? = null + private val tasks: MutableList> = mutableListOf() + @SuppressLint("ConstantLocale") private val DEBUG_TIME_FORMAT = SimpleDateFormat("[mm:ss.SSS]", Locale.getDefault()) private val startTime: Long = System.currentTimeMillis() fun printLog(sourceUrl: String?, state: Int, msg: String, print: Boolean = true, isHtml: Boolean = false) { - if (debugSource != sourceUrl) return - if (!print) return + if (debugSource != sourceUrl || callback == null || !print) return var printMsg = msg if (isHtml) { printMsg = printMsg.htmlFormat() @@ -29,18 +32,46 @@ class SourceDebug(private val webBook: WebBook, callback: Callback) { String.format("%s %s", DEBUG_TIME_FORMAT.format(Date(System.currentTimeMillis() - startTime)), printMsg) callback?.printLog(state, printMsg) } - } - interface Callback { - fun printLog(state: Int, msg: String) + fun cancelDebug(destroy: Boolean = false) { + tasks.forEach { + if (!it.isCancelled) { + it.cancel() + } + } + tasks.clear() + + if (destroy) { + debugSource = null + callback = null + } + } + } init { + debugSource = webBook.sourceUrl SourceDebug.callback = callback } + fun startDebug(key: String) { + cancelDebug() + with(webBook) { + if (key.isAbsUrl()) { + val book = Book() + book.origin = sourceUrl + book.bookUrl = key + printLog(sourceUrl, 1, "开始访问$key") + infoDebug(book) + } else { + printLog(sourceUrl, 1, "开始搜索关键字$key") + searchDebug(key) + } + } + } + fun searchDebug(key: String) { - webBook.searchBook(key, 1) + val search = webBook.searchBook(key, 1) .onSuccess { searchBooks -> searchBooks?.let { if (searchBooks.isNotEmpty()) { @@ -51,20 +82,22 @@ class SourceDebug(private val webBook: WebBook, callback: Callback) { .onError { printLog(debugSource, -1, it.localizedMessage) } + tasks.add(search) } fun infoDebug(book: Book) { - webBook.getBookInfo(book) + val info = webBook.getBookInfo(book) .onSuccess { tocDebug(book) } .onError { printLog(debugSource, -1, it.localizedMessage) } + tasks.add(info) } private fun tocDebug(book: Book) { - webBook.getChapterList(book) + val chapterList = webBook.getChapterList(book) .onSuccess { chapterList -> chapterList?.let { if (it.isNotEmpty()) { @@ -75,10 +108,11 @@ class SourceDebug(private val webBook: WebBook, callback: Callback) { .onError { printLog(debugSource, -1, it.localizedMessage) } + tasks.add(chapterList) } private fun contentDebug(book: Book, bookChapter: BookChapter) { - webBook.getContent(book, bookChapter) + val content = webBook.getContent(book, bookChapter) .onSuccess { content -> content?.let { printLog(debugSource, 1000, it) @@ -87,6 +121,11 @@ class SourceDebug(private val webBook: WebBook, callback: Callback) { .onError { printLog(debugSource, -1, it.localizedMessage) } + tasks.add(content) + } + + interface Callback { + fun printLog(state: Int, msg: String) } fun printLog( diff --git a/app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt index 3252e5112..d3dddb787 100644 --- a/app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt @@ -10,6 +10,8 @@ import io.legado.app.data.entities.SearchBook import io.legado.app.help.http.HttpHelper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class SearchViewModel(application: Application) : BaseViewModel(application) { @@ -19,86 +21,68 @@ class SearchViewModel(application: Application) : BaseViewModel(application) { private val channel = Channel()//协程之间通信 fun search(start: (() -> Unit)? = null, finally: (() -> Unit)? = null) { -// launch { -// repeat(1000) { -// channel.send(it) -// } -// } -// -// -// val c = execute { -// -// Log.e("TAG1", "start") -// -// val response: String = HttpHelper.getApiService( -// "http://www.baidu.com" -// ).get("http://www.baidu.com").await() -// -// Log.e("TAG1", "result: $response") -// -// delay(2000L) -// -// response -// } -// .onStart { -// Log.e("TAG!", "start") -// start?.let { it() } -// } + launch { + repeat(1000) { + channel.send(it) + } + } + + + val c = execute { + + val response: String = HttpHelper.getApiService( + "http://www.baidu.com" + ).get("http://www.baidu.com").await() + + delay(2000L) + + response + } + .onStart { + Log.e("TAG!", "start") + start?.let { it() } + } + .onSuccess { + Log.e("TAG!", "success: $it") + } + .onError { + Log.e("TAG!", "error: $it") + } + .onFinally { + Log.e("TAG!", "finally") + if (finally != null) { + finally() + } + } + + val c2 = plus(c) +// .timeout { 100L } +// .onErrorReturn { "error return2" } + .onStart { + //会拦截掉c的onStart + Log.e("TAG!", "start2") + start?.let { it() } + } // .onSuccess { -// Log.e("TAG!", "success: $it") -// } -// .onError { -// Log.e("TAG!", "error: $it") -// } -// .onFinally { -// Log.e("TAG!", "finally") -// if (finally != null) { -// finally() -// } -// } -// -// val c2 = plus(c) -//// .timeout { 100L } -//// .onErrorReturn { "error return2" } -// .onStart { -// //会拦截掉c的onStart -// Log.e("TAG!", "start2") -// start?.let { it() } -// } -//// .onSuccess { -//// Log.e("TAG!", "success2: $it") -//// } -// .onError { -// Log.e("TAG!", "error2: $it") +// Log.e("TAG!", "success2: $it") // } -// .onFinally { -// Log.e("TAG!", "finally2") -// if (finally != null) { -// finally() -// } -// -// Log.e("TAG!", "rec2: " + channel.receive()) -// } -// -// launch { -// delay(1500L) -//// c2.cancel() -// -//// c.cancel() -// } -// -// -// launch { -// val list = test() -// println("size: ${list.size} $list") -// } + .onError { + Log.e("TAG!", "error2: $it") + } + .onFinally { + Log.e("TAG!", "finally2") + if (finally != null) { + finally() + } + Log.e("TAG!", "rec2: " + channel.receive()) + } - execute { - test(this) - }.onSuccess { - println("size: ${it?.size} $it") - } +// execute { +// test(this) +// }.onSuccess { +// println("size: ${it?.size} $it") +// } } suspend fun test(scope: CoroutineScope): MutableList { diff --git a/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugActivity.kt b/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugActivity.kt index 3de2e2fd0..400328f1f 100644 --- a/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugActivity.kt +++ b/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugActivity.kt @@ -7,10 +7,8 @@ import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.BaseActivity -import io.legado.app.data.entities.BookSource import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ThemeStore -import io.legado.app.model.webbook.SourceDebug import io.legado.app.utils.getViewModel import kotlinx.android.synthetic.main.activity_source_debug.* import kotlinx.android.synthetic.main.view_title_bar.* @@ -71,9 +69,4 @@ class SourceDebugActivity : BaseActivity() { toast("未获取到书源") }) } - - override fun onDestroy() { - SourceDebug.debugSource = null - super.onDestroy() - } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugModel.kt b/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugModel.kt index e7a9c920f..0a9732ad5 100644 --- a/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugModel.kt +++ b/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugModel.kt @@ -4,48 +4,39 @@ import android.app.Application import androidx.lifecycle.MutableLiveData import io.legado.app.App import io.legado.app.base.BaseViewModel -import io.legado.app.data.entities.Book -import io.legado.app.data.entities.BookSource import io.legado.app.help.EventMessage import io.legado.app.model.WebBook import io.legado.app.model.webbook.SourceDebug -import io.legado.app.utils.isAbsUrl class SourceDebugModel(application: Application) : BaseViewModel(application), SourceDebug.Callback { val logs: MutableLiveData = MutableLiveData() - private var bookSource: BookSource? = null + private var webBook: WebBook? = null fun init(sourceUrl: String?) { sourceUrl?.let { //优先使用这个,不会抛出异常 execute { - bookSource = App.db.bookSourceDao().findByKey(sourceUrl) + val bookSource = App.db.bookSourceDao().findByKey(sourceUrl) + bookSource?.let { webBook = WebBook(it) } } } } fun startDebug(key: String, start: (() -> Unit)? = null, error: (() -> Unit)? = null) { - bookSource?.let { - start?.let { it() } - SourceDebug.debugSource = it.bookSourceUrl - if (key.isAbsUrl()) { - val book = Book() - book.origin = it.bookSourceUrl - book.bookUrl = key - SourceDebug(WebBook(it), this) - .printLog(it.bookSourceUrl, 1, "开始访问$key") - .infoDebug(book) - } else { - SourceDebug(WebBook(it), this) - .printLog(it.bookSourceUrl, 1, "开始搜索关键字$key") - .searchDebug(key) - } - } ?: error?.let { it() } + webBook?.let { + start?.invoke() + SourceDebug(it, this).startDebug(key) + } ?: error?.invoke() } override fun printLog(state: Int, msg: String) { logs.postValue(EventMessage.obtain(state, msg)) } + + override fun onCleared() { + super.onCleared() + SourceDebug.cancelDebug(true) + } }