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
import kotlinx.coroutines.Deferred
import retrofit2.Call
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.HeaderMap
@ -27,16 +26,4 @@ interface IHttpGetApi {
@HeaderMap headers: Map<String, 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>
): 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
import android.util.Log
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
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 var start: (suspend CoroutineScope.() -> Unit)? = null
private var execute: (suspend CoroutineScope.(T?) -> Unit)? = null
private var success: (suspend CoroutineScope.(T?) -> Unit)? = null
private var error: (suspend CoroutineScope.(Throwable) -> Unit)? = null
private var finally: (suspend CoroutineScope.() -> Unit)? = null
private var start: VoidCallback? = null
private var success: Callback<T?>? = null
private var error: Callback<Throwable>? = null
private var finally: VoidCallback? = null
private var timeMillis: Long? = null
private var errorReturn: Result<T>? = null
val isCancelled: Boolean
@ -37,9 +37,7 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
get() = job.isCompleted
init {
this.job = scope.plus(Dispatchers.Main).launch {
executeInternal(this@launch, block)
}
this.job = executeInternal(scope, block)
}
fun timeout(timeMillis: () -> Long): Coroutine<T> {
@ -62,28 +60,35 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
return this@Coroutine
}
fun onStart(start: (suspend CoroutineScope.() -> Unit)): Coroutine<T> {
this.start = start
return this@Coroutine
}
fun onExecute(execute: suspend CoroutineScope.(T?) -> Unit): Coroutine<T> {
this.execute = execute
fun onStart(
context: CoroutineContext? = null,
block: (suspend CoroutineScope.() -> Unit)
): Coroutine<T> {
this.start = VoidCallback(context, block)
return this@Coroutine
}
fun onSuccess(success: suspend CoroutineScope.(T?) -> Unit): Coroutine<T> {
this.success = success
fun onSuccess(
context: CoroutineContext? = null,
block: suspend CoroutineScope.(T?) -> Unit
): Coroutine<T> {
this.success = Callback(context, block)
return this@Coroutine
}
fun onError(error: suspend CoroutineScope.(Throwable) -> Unit): Coroutine<T> {
this.error = error
fun onError(
context: CoroutineContext? = null,
block: suspend CoroutineScope.(Throwable) -> Unit
): Coroutine<T> {
this.error = Callback(context, block)
return this@Coroutine
}
fun onFinally(finally: suspend CoroutineScope.() -> Unit): Coroutine<T> {
this.finally = finally
fun onFinally(
context: CoroutineContext? = null,
block: suspend CoroutineScope.() -> Unit
): Coroutine<T> {
this.finally = VoidCallback(context, block)
return this@Coroutine
}
@ -96,50 +101,72 @@ class Coroutine<T>(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
return job.invokeOnCompletion(handler)
}
private suspend fun executeInternal(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) {
tryCatch(
{
start?.invoke(scope)
val result = executeBlockIO(block, timeMillis ?: 0L)
success?.invoke(scope, result)
},
{ e ->
private fun executeInternal(scope: CoroutineScope, block: suspend CoroutineScope.() -> T): Job {
return scope.plus(Dispatchers.Main).launch {
try {
start?.let { dispatchVoidCallback(this, it) }
val value = executeBlock(scope, timeMillis ?: 0L, block)
success?.let { dispatchCallback(this, value, it) }
} catch (e: Throwable) {
val consume: Boolean = errorReturn?.value?.let { value ->
success?.invoke(scope, value)
success?.let { dispatchCallback(this, value, it) }
true
} ?: false
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? {
val execution = withContext(Dispatchers.IO) {
val result = block()
execute?.invoke(this, result)
result
private suspend inline fun dispatchVoidCallback(scope: CoroutineScope, callback: VoidCallback) {
if (null == callback.context) {
callback.block.invoke(scope)
} else {
withContext(scope.coroutineContext.plus(callback.context)) {
callback.block.invoke(this)
}
}
return if (timeMillis > 0L) withTimeout(timeMillis) { execution } else execution
}
private suspend fun tryCatch(
tryBlock: suspend () -> Unit,
errorBlock: (suspend (Throwable) -> Unit)? = null,
finallyBlock: (suspend () -> Unit)? = null
private suspend inline fun <R> dispatchCallback(
scope: CoroutineScope,
value: R,
callback: Callback<R>
) {
try {
tryBlock()
} catch (e: Throwable) {
errorBlock?.invoke(e)
} finally {
finallyBlock?.invoke()
if (null == callback.context) {
callback.block.invoke(scope, value)
} else {
withContext(scope.coroutineContext.plus(callback.context)) {
callback.block.invoke(this, value)
}
}
}
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 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)
}
}
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]
while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) {
nextUrlList.add(nextUrl)
AnalyzeUrl(ruleUrl = nextUrl, book = book).getResponse().execute()
AnalyzeUrl(ruleUrl = nextUrl, book = book).getResponseAsync().await()
.body()?.let { nextBody ->
chapterData = analyzeChapterList(nextBody, nextUrl, tocRule, listRule, book, bookSource)
nextUrl = if (chapterData.nextUrl.isNotEmpty()) chapterData.nextUrl[0] else ""

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

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

Loading…
Cancel
Save