pull/1301/head
gedoor 4 years ago
parent 2310270f2e
commit 855963b92a
  1. 204
      app/src/main/java/io/legado/app/model/CacheBook.kt
  2. 6
      app/src/main/java/io/legado/app/model/Exceptions.kt
  3. 2
      app/src/main/java/io/legado/app/service/CacheBookService.kt

@ -98,7 +98,7 @@ class CacheBook(var bookSource: BookSource, var book: Book) {
get() { get() {
var count = 0 var count = 0
cacheBookMap.forEach { cacheBookMap.forEach {
count += it.value.waitDownloadSet.size count += it.value.waitCount
} }
return count return count
} }
@ -107,7 +107,7 @@ class CacheBook(var bookSource: BookSource, var book: Book) {
get() { get() {
var count = 0 var count = 0
cacheBookMap.forEach { cacheBookMap.forEach {
count += it.value.successDownloadSet.size count += it.value.successCount
} }
return count return count
} }
@ -116,107 +116,127 @@ class CacheBook(var bookSource: BookSource, var book: Book) {
get() { get() {
var count = 0 var count = 0
cacheBookMap.forEach { cacheBookMap.forEach {
count += it.value.onDownloadSet.size count += it.value.onDownloadCount
} }
return count return count
} }
val errorCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.errorCount
}
return count
}
} }
val waitDownloadSet = hashSetOf<Int>() private val waitDownloadSet = hashSetOf<Int>()
val onDownloadSet = hashSetOf<Int>() private val onDownloadSet = hashSetOf<Int>()
val successDownloadSet = hashSetOf<Int>() private val successDownloadSet = hashSetOf<Int>()
private val errorDownloadMap = hashMapOf<Int, Int>()
fun addDownload(start: Int, end: Int) { val waitCount get() = waitDownloadSet.size
synchronized(this) { val onDownloadCount get() = onDownloadSet.size
for (i in start..end) { val successCount get() = successDownloadSet.size
waitDownloadSet.add(i) val errorCount get() = errorDownloadMap.size
}
}
}
@Synchronized
fun isRun(): Boolean { fun isRun(): Boolean {
return synchronized(this) { return waitDownloadSet.size > 0 || onDownloadSet.size > 0
waitDownloadSet.size > 0 || onDownloadSet.size > 0 }
@Synchronized
fun addDownload(start: Int, end: Int) {
for (i in start..end) {
waitDownloadSet.add(i)
} }
} }
@Synchronized
private fun onSuccess(index: Int) { private fun onSuccess(index: Int) {
synchronized(this) { onDownloadSet.remove(index)
onDownloadSet.remove(index) successDownloadSet.add(index)
successDownloadSet.add(index) errorDownloadMap.remove(index)
}
} }
private fun onErrorOrCancel(index: Int) { @Synchronized
synchronized(this) { private fun onError(index: Int, error: Throwable, chapterTitle: String) {
onDownloadSet.remove(index) if (error !is ConcurrentException) {
errorDownloadMap[index] = (errorDownloadMap[index] ?: 0) + 1
}
onDownloadSet.remove(index)
//重试3次
if (errorDownloadMap[index] ?: 0 < 3) {
waitDownloadSet.add(index) waitDownloadSet.add(index)
} else {
addLog("${book.name}-${chapterTitle} ${error.localizedMessage}")
} }
} }
@Synchronized
private fun onCancel(index: Int) {
onDownloadSet.remove(index)
waitDownloadSet.add(index)
}
@Synchronized
private fun onFinally() { private fun onFinally() {
synchronized(this) { if (waitDownloadSet.isEmpty() && onDownloadSet.isEmpty()) {
if (waitDownloadSet.isEmpty() && onDownloadSet.isEmpty()) { postEvent(EventBus.UP_DOWNLOAD, "")
postEvent(EventBus.UP_DOWNLOAD, "") cacheBookMap.remove(book.bookUrl)
cacheBookMap.remove(book.bookUrl)
}
} }
} }
/** /**
* 从待下载列表内取第一条下载 * 从待下载列表内取第一条下载
*/ */
@Synchronized
fun download(scope: CoroutineScope, context: CoroutineContext): Boolean { fun download(scope: CoroutineScope, context: CoroutineContext): Boolean {
synchronized(this) { val chapterIndex = waitDownloadSet.firstOrNull()
val chapterIndex = waitDownloadSet.firstOrNull() if (chapterIndex == null) {
if (chapterIndex == null) { if (onDownloadSet.isEmpty()) {
if (onDownloadSet.isEmpty()) { cacheBookMap.remove(book.bookUrl)
cacheBookMap.remove(book.bookUrl)
}
return false
}
if (onDownloadSet.contains(chapterIndex)) {
waitDownloadSet.remove(chapterIndex)
return download(scope, context)
}
val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, chapterIndex) ?: let {
waitDownloadSet.remove(chapterIndex)
return download(scope, context)
}
if (BookHelp.hasContent(book, chapter)) {
waitDownloadSet.remove(chapterIndex)
return download(scope, context)
} }
return false
}
if (onDownloadSet.contains(chapterIndex)) {
waitDownloadSet.remove(chapterIndex) waitDownloadSet.remove(chapterIndex)
onDownloadSet.add(chapterIndex) return download(scope, context)
WebBook.getContent( }
scope, val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, chapterIndex) ?: let {
bookSource, waitDownloadSet.remove(chapterIndex)
book, return download(scope, context)
chapter, }
context = context if (BookHelp.hasContent(book, chapter)) {
).onSuccess { content -> waitDownloadSet.remove(chapterIndex)
onSuccess(chapterIndex) return download(scope, context)
addLog("${book.name}-${chapter.title} getContentSuccess") }
downloadFinish(chapter, content) waitDownloadSet.remove(chapterIndex)
}.onError { onDownloadSet.add(chapterIndex)
//出现错误等待后重新加入待下载列表 WebBook.getContent(
when (it) { scope,
is ConcurrentException -> delay(it.waitTime) bookSource,
else -> delay(1000) book,
} chapter,
onErrorOrCancel(chapterIndex) context = context
print(it.localizedMessage) ).onSuccess { content ->
addLog("${book.name}-${chapter.title} getContentError${it.localizedMessage}") onSuccess(chapterIndex)
downloadFinish(chapter, it.localizedMessage ?: "download error") downloadFinish(chapter, content)
}.onCancel { }.onError {
onErrorOrCancel(chapterIndex) //出现错误等待后重新加入待下载列表
}.onFinally { when (it) {
onFinally() is ConcurrentException -> delay(it.waitTime)
else -> delay(1000)
} }
return true onError(chapterIndex, it, chapter.title)
downloadFinish(chapter, "error:${it.localizedMessage}")
}.onCancel {
onCancel(chapterIndex)
}.onFinally {
onFinally()
} }
return true
} }
@Synchronized @Synchronized
@ -225,30 +245,24 @@ class CacheBook(var bookSource: BookSource, var book: Book) {
chapter: BookChapter, chapter: BookChapter,
resetPageOffset: Boolean = false resetPageOffset: Boolean = false
) { ) {
synchronized(this) { if (onDownloadSet.contains(chapter.index)) {
if (onDownloadSet.contains(chapter.index)) { return
return
}
onDownloadSet.add(chapter.index)
WebBook.getContent(scope, bookSource, book, chapter)
.onSuccess { content ->
onSuccess(chapter.index)
downloadFinish(chapter, content, resetPageOffset)
}.onError {
onErrorOrCancel(chapter.index)
downloadFinish(
chapter,
it.localizedMessage ?: "download error",
resetPageOffset
)
}.onCancel {
onErrorOrCancel(chapter.index)
}.onFinally {
if (waitDownloadSet.isEmpty() && onDownloadSet.isEmpty()) {
postEvent(EventBus.UP_DOWNLOAD, "")
}
}
} }
onDownloadSet.add(chapter.index)
WebBook.getContent(scope, bookSource, book, chapter)
.onSuccess { content ->
onSuccess(chapter.index)
downloadFinish(chapter, content, resetPageOffset)
}.onError {
onError(chapter.index, it, chapter.title)
downloadFinish(chapter, "error:${it.localizedMessage}", resetPageOffset)
}.onCancel {
onCancel(chapter.index)
}.onFinally {
if (waitDownloadSet.isEmpty() && onDownloadSet.isEmpty()) {
postEvent(EventBus.UP_DOWNLOAD, "")
}
}
} }
private fun downloadFinish( private fun downloadFinish(

@ -1,5 +1,11 @@
package io.legado.app.model package io.legado.app.model
/**
* 内容为空
*/
class ContentEmptyException(msg: String) : Exception(msg) class ContentEmptyException(msg: String) : Exception(msg)
/**
* 并发限制
*/
class ConcurrentException(msg: String, val waitTime: Long) : Exception(msg) class ConcurrentException(msg: String, val waitTime: Long) : Exception(msg)

@ -108,7 +108,7 @@ class CacheBookService : BaseService() {
private fun upNotificationContent() { private fun upNotificationContent() {
notificationContent = notificationContent =
"正在下载:${CacheBook.onDownloadCount}/等待中:${CacheBook.waitDownloadCount}/成功:${CacheBook.successDownloadCount}" "正在下载:${CacheBook.onDownloadCount}|等待中:${CacheBook.waitDownloadCount}|失败:${CacheBook.errorCount}|成功:${CacheBook.successDownloadCount}"
upNotification() upNotification()
} }

Loading…
Cancel
Save