pull/32/head
Administrator 5 years ago
parent c86a671447
commit 4cf51ac940
  1. 18
      app/src/main/java/io/legado/app/data/api/CommonHttpApi.kt
  2. 13
      app/src/main/java/io/legado/app/data/api/IHttpGetApi.kt
  3. 14
      app/src/main/java/io/legado/app/data/api/IHttpPostApi.kt
  4. 131
      app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
  5. 17
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  6. 2
      app/src/main/java/io/legado/app/model/webbook/BookChapterList.kt
  7. 2
      app/src/main/java/io/legado/app/model/webbook/BookContent.kt
  8. 16
      app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt

@ -1,18 +0,0 @@
package io.legado.app.data.api
import kotlinx.coroutines.Deferred
import retrofit2.http.*
interface CommonHttpApi {
@GET
fun get(@Url url: String, @QueryMap map: Map<String, String>): Deferred<String>
@FormUrlEncoded
@POST
fun post(@Url url: String, @FieldMap map: Map<String, String>): Deferred<String>
@GET
fun get(@Url url: String) : Deferred<String>
}

@ -1,7 +1,6 @@
package io.legado.app.data.api package io.legado.app.data.api
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import retrofit2.Call
import retrofit2.Response import retrofit2.Response
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.HeaderMap import retrofit2.http.HeaderMap
@ -27,16 +26,4 @@ interface IHttpGetApi {
@HeaderMap headers: Map<String, String> @HeaderMap headers: Map<String, String>
): Deferred<Response<String>> ): Deferred<Response<String>>
@GET
fun get(
@Url url: String,
@HeaderMap headers: Map<String, String>
): Call<String>
@GET
fun getMap(
@Url url: String,
@QueryMap(encoded = true) queryMap: Map<String, String>,
@HeaderMap headers: Map<String, String>
): Call<String>
} }

@ -28,18 +28,4 @@ interface IHttpPostApi {
@HeaderMap headers: Map<String, String> @HeaderMap headers: Map<String, String>
): Deferred<Response<String>> ): Deferred<Response<String>>
@FormUrlEncoded
@POST
fun postMap(
@Url url: String,
@FieldMap(encoded = true) fieldMap: Map<String, String>,
@HeaderMap headers: Map<String, String>
): Call<String>
@POST
fun postBody(
@Url url: String,
@Body body: RequestBody,
@HeaderMap headers: Map<String, String>
): Call<String>
} }

@ -1,6 +1,8 @@
package io.legado.app.help.coroutine package io.legado.app.help.coroutine
import android.util.Log
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) { class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) {
@ -17,14 +19,12 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
private val job: Job private val job: Job
private var start: (suspend CoroutineScope.() -> Unit)? = null private var start: VoidCallback? = null
private var execute: (suspend CoroutineScope.(T?) -> Unit)? = null private var success: Callback<T?>? = null
private var success: (suspend CoroutineScope.(T?) -> Unit)? = null private var error: Callback<Throwable>? = null
private var error: (suspend CoroutineScope.(Throwable) -> Unit)? = null private var finally: VoidCallback? = null
private var finally: (suspend CoroutineScope.() -> Unit)? = null
private var timeMillis: Long? = null private var timeMillis: Long? = null
private var errorReturn: Result<T>? = null private var errorReturn: Result<T>? = null
val isCancelled: Boolean val isCancelled: Boolean
@ -37,9 +37,7 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
get() = job.isCompleted get() = job.isCompleted
init { init {
this.job = scope.plus(Dispatchers.Main).launch { this.job = executeInternal(scope, block)
executeInternal(this@launch, block)
}
} }
fun timeout(timeMillis: () -> Long): Coroutine<T> { fun timeout(timeMillis: () -> Long): Coroutine<T> {
@ -62,28 +60,35 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
return this@Coroutine return this@Coroutine
} }
fun onStart(start: (suspend CoroutineScope.() -> Unit)): Coroutine<T> { fun onStart(
this.start = start context: CoroutineContext? = null,
return this@Coroutine block: (suspend CoroutineScope.() -> Unit)
} ): Coroutine<T> {
this.start = VoidCallback(context, block)
fun onExecute(execute: suspend CoroutineScope.(T?) -> Unit): Coroutine<T> {
this.execute = execute
return this@Coroutine return this@Coroutine
} }
fun onSuccess(success: suspend CoroutineScope.(T?) -> Unit): Coroutine<T> { fun onSuccess(
this.success = success context: CoroutineContext? = null,
block: suspend CoroutineScope.(T?) -> Unit
): Coroutine<T> {
this.success = Callback(context, block)
return this@Coroutine return this@Coroutine
} }
fun onError(error: suspend CoroutineScope.(Throwable) -> Unit): Coroutine<T> { fun onError(
this.error = error context: CoroutineContext? = null,
block: suspend CoroutineScope.(Throwable) -> Unit
): Coroutine<T> {
this.error = Callback(context, block)
return this@Coroutine return this@Coroutine
} }
fun onFinally(finally: suspend CoroutineScope.() -> Unit): Coroutine<T> { fun onFinally(
this.finally = finally context: CoroutineContext? = null,
block: suspend CoroutineScope.() -> Unit
): Coroutine<T> {
this.finally = VoidCallback(context, block)
return this@Coroutine return this@Coroutine
} }
@ -96,50 +101,72 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
return job.invokeOnCompletion(handler) return job.invokeOnCompletion(handler)
} }
private suspend fun executeInternal(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) { private fun executeInternal(scope: CoroutineScope, block: suspend CoroutineScope.() -> T): Job {
tryCatch( return scope.plus(Dispatchers.Main).launch {
{ try {
start?.invoke(scope) start?.let { dispatchVoidCallback(this, it) }
val result = executeBlockIO(block, timeMillis ?: 0L) val value = executeBlock(scope, timeMillis ?: 0L, block)
success?.invoke(scope, result) success?.let { dispatchCallback(this, value, it) }
}, } catch (e: Throwable) {
{ e ->
val consume: Boolean = errorReturn?.value?.let { value -> val consume: Boolean = errorReturn?.value?.let { value ->
success?.invoke(scope, value) success?.let { dispatchCallback(this, value, it) }
true true
} ?: false } ?: false
if (!consume) { if (!consume) {
error?.invoke(scope, e) error?.let { dispatchCallback(this, e, it) }
} }
}, } finally {
{ finally?.let { dispatchVoidCallback(this, it) }
finally?.invoke(scope) }
}) }
} }
private suspend fun executeBlockIO(block: suspend CoroutineScope.() -> T, timeMillis: Long): T? { private suspend inline fun dispatchVoidCallback(scope: CoroutineScope, callback: VoidCallback) {
val execution = withContext(Dispatchers.IO) { if (null == callback.context) {
val result = block() callback.block.invoke(scope)
execute?.invoke(this, result) } else {
result withContext(scope.coroutineContext.plus(callback.context)) {
callback.block.invoke(this)
}
} }
return if (timeMillis > 0L) withTimeout(timeMillis) { execution } else execution
} }
private suspend fun tryCatch( private suspend inline fun <R> dispatchCallback(
tryBlock: suspend () -> Unit, scope: CoroutineScope,
errorBlock: (suspend (Throwable) -> Unit)? = null, value: R,
finallyBlock: (suspend () -> Unit)? = null callback: Callback<R>
) { ) {
try { if (null == callback.context) {
tryBlock() callback.block.invoke(scope, value)
} catch (e: Throwable) { } else {
errorBlock?.invoke(e) withContext(scope.coroutineContext.plus(callback.context)) {
} finally { callback.block.invoke(this, value)
finallyBlock?.invoke() }
}
}
private suspend inline fun executeBlock(
scope: CoroutineScope,
timeMillis: Long,
noinline block: suspend CoroutineScope.() -> T
): T? {
return withContext(scope.coroutineContext.plus(Dispatchers.IO)) {
if (timeMillis > 0L) withTimeout(timeMillis) {
block()
} else block()
} }
} }
private data class Result<out T>(val value: T?) private data class Result<out T>(val value: T?)
private inner class VoidCallback(
val context: CoroutineContext?,
val block: suspend CoroutineScope.() -> Unit
)
private inner class Callback<VALUE>(
val context: CoroutineContext?,
val block: suspend CoroutineScope.(VALUE) -> Unit
)
} }

@ -223,21 +223,4 @@ class AnalyzeUrl(
.getMapAsync(url, fieldMap, headerMap) .getMapAsync(url, fieldMap, headerMap)
} }
} }
fun getResponse(): Call<String> {
return when {
method == Method.POST -> HttpHelper.getApiService<IHttpPostApi>(
baseUrl
).postBody(
url,
body,
headerMap
)
fieldMap.isEmpty() -> HttpHelper.getApiService<IHttpGetApi>(
baseUrl
).get(url, headerMap)
else -> HttpHelper.getApiService<IHttpGetApi>(baseUrl)
.getMap(url, fieldMap, headerMap)
}
}
} }

@ -52,7 +52,7 @@ object BookChapterList {
var nextUrl = chapterData.nextUrl[0] var nextUrl = chapterData.nextUrl[0]
while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) { while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) {
nextUrlList.add(nextUrl) nextUrlList.add(nextUrl)
AnalyzeUrl(ruleUrl = nextUrl, book = book).getResponse().execute() AnalyzeUrl(ruleUrl = nextUrl, book = book).getResponseAsync().await()
.body()?.let { nextBody -> .body()?.let { nextBody ->
chapterData = analyzeChapterList(nextBody, nextUrl, tocRule, listRule, book, bookSource) chapterData = analyzeChapterList(nextBody, nextUrl, tocRule, listRule, book, bookSource)
nextUrl = if (chapterData.nextUrl.isNotEmpty()) chapterData.nextUrl[0] else "" nextUrl = if (chapterData.nextUrl.isNotEmpty()) chapterData.nextUrl[0] else ""

@ -51,7 +51,7 @@ object BookContent {
== NetworkUtils.getAbsoluteURL(baseUrl, nextChapterUrl) == NetworkUtils.getAbsoluteURL(baseUrl, nextChapterUrl)
) break ) break
nextUrlList.add(nextUrl) nextUrlList.add(nextUrl)
AnalyzeUrl(ruleUrl = nextUrl, book = book).getResponse().execute() AnalyzeUrl(ruleUrl = nextUrl, book = book).getResponseAsync().await()
.body()?.let { nextBody -> .body()?.let { nextBody ->
contentData = analyzeContent(nextBody, contentRule, book, baseUrl) contentData = analyzeContent(nextBody, contentRule, book, baseUrl)
nextUrl = if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else "" nextUrl = if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else ""

@ -4,10 +4,14 @@ import android.app.Application
import android.util.Log import android.util.Log
import io.legado.app.App import io.legado.app.App
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.SearchBook
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook import io.legado.app.model.WebBook
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.lang.Exception
import java.util.concurrent.CancellationException
class SearchViewModel(application: Application) : BaseViewModel(application) { class SearchViewModel(application: Application) : BaseViewModel(application) {
private var task: Coroutine<*>? = null private var task: Coroutine<*>? = null
@ -29,7 +33,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
//task取消时自动取消 by (scope = this@execute) //task取消时自动取消 by (scope = this@execute)
WebBook(item).searchBook(key, searchPage, scope = this@execute) WebBook(item).searchBook(key, searchPage, scope = this@execute)
.timeout(30000L) .timeout(30000L)
.onExecute{ .onSuccess(Dispatchers.IO) {
it?.let { list -> it?.let { list ->
App.db.searchBookDao().insert(*list.toTypedArray()) App.db.searchBookDao().insert(*list.toTypedArray())
} }
@ -43,14 +47,12 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
task?.invokeOnCompletion { task?.invokeOnCompletion {
finally?.invoke() finally?.invoke()
} }
} }
fun stop() { fun stop() {
task?.cancel() task?.cancel()
} }
override fun onCleared() {
super.onCleared()
task?.cancel()
}
} }

Loading…
Cancel
Save