pull/1352/head
gedoor 3 years ago
parent c4e51aaf9a
commit a5c6e6f7dc
  1. 433
      app/src/main/java/io/legado/app/model/CacheBook.kt

@ -19,269 +19,270 @@ import kotlinx.coroutines.delay
import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.CoroutineContext
class CacheBook(var bookSource: BookSource, var book: Book) {
object CacheBook {
companion object {
val cacheBookMap = ConcurrentHashMap<String, CacheBookModel>()
val cacheBookMap = ConcurrentHashMap<String, CacheBook>()
@Synchronized
fun getOrCreate(bookUrl: String): CacheBook? {
val book = appDb.bookDao.getBook(bookUrl) ?: return null
val bookSource = appDb.bookSourceDao.getBookSource(book.origin) ?: return null
var cacheBook = cacheBookMap[bookUrl]
if (cacheBook != null) {
//存在时更新,书源可能会变化,必须更新
cacheBook.bookSource = bookSource
cacheBook.book = book
return cacheBook
}
cacheBook = CacheBook(bookSource, book)
cacheBookMap[bookUrl] = cacheBook
@Synchronized
fun getOrCreate(bookUrl: String): CacheBookModel? {
val book = appDb.bookDao.getBook(bookUrl) ?: return null
val bookSource = appDb.bookSourceDao.getBookSource(book.origin) ?: return null
var cacheBook = cacheBookMap[bookUrl]
if (cacheBook != null) {
//存在时更新,书源可能会变化,必须更新
cacheBook.bookSource = bookSource
cacheBook.book = book
return cacheBook
}
cacheBook = CacheBookModel(bookSource, book)
cacheBookMap[bookUrl] = cacheBook
return cacheBook
}
@Synchronized
fun getOrCreate(bookSource: BookSource, book: Book): CacheBook {
var cacheBook = cacheBookMap[book.bookUrl]
if (cacheBook != null) {
//存在时更新,书源可能会变化,必须更新
cacheBook.bookSource = bookSource
cacheBook.book = book
return cacheBook
}
cacheBook = CacheBook(bookSource, book)
cacheBookMap[book.bookUrl] = cacheBook
@Synchronized
fun getOrCreate(bookSource: BookSource, book: Book): CacheBookModel {
var cacheBook = cacheBookMap[book.bookUrl]
if (cacheBook != null) {
//存在时更新,书源可能会变化,必须更新
cacheBook.bookSource = bookSource
cacheBook.book = book
return cacheBook
}
cacheBook = CacheBookModel(bookSource, book)
cacheBookMap[book.bookUrl] = cacheBook
return cacheBook
}
fun start(context: Context, bookUrl: String, start: Int, end: Int) {
context.startService<CacheBookService> {
action = IntentAction.start
putExtra("bookUrl", bookUrl)
putExtra("start", start)
putExtra("end", end)
}
fun start(context: Context, bookUrl: String, start: Int, end: Int) {
context.startService<CacheBookService> {
action = IntentAction.start
putExtra("bookUrl", bookUrl)
putExtra("start", start)
putExtra("end", end)
}
}
fun remove(context: Context, bookUrl: String) {
context.startService<CacheBookService> {
action = IntentAction.remove
putExtra("bookUrl", bookUrl)
}
fun remove(context: Context, bookUrl: String) {
context.startService<CacheBookService> {
action = IntentAction.remove
putExtra("bookUrl", bookUrl)
}
}
fun stop(context: Context) {
context.startService<CacheBookService> {
action = IntentAction.stop
}
fun stop(context: Context) {
context.startService<CacheBookService> {
action = IntentAction.stop
}
}
val downloadSummary: String
get() {
return "正在下载:${onDownloadCount}|等待中:${waitCount}|失败:${errorCount}|成功:${successCount}"
}
val downloadSummary: String
get() {
return "正在下载:${onDownloadCount}|等待中:${waitCount}|失败:${errorCount}|成功:${successCount}"
}
val isRun: Boolean
get() {
var isRun = false
cacheBookMap.forEach {
isRun = isRun || it.value.isRun()
}
return isRun
val isRun: Boolean
get() {
var isRun = false
cacheBookMap.forEach {
isRun = isRun || it.value.isRun()
}
return isRun
}
private val waitCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.waitCount
}
return count
private val waitCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.waitCount
}
return count
}
private val successCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.successCount
}
return count
private val successCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.successCount
}
return count
}
val onDownloadCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.onDownloadCount
}
return count
val onDownloadCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.onDownloadCount
}
return count
}
private val errorCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.errorCount
}
return count
private val errorCount: Int
get() {
var count = 0
cacheBookMap.forEach {
count += it.value.errorCount
}
}
private val waitDownloadSet = hashSetOf<Int>()
private val onDownloadSet = hashSetOf<Int>()
private val successDownloadSet = hashSetOf<Int>()
private val errorDownloadMap = hashMapOf<Int, Int>()
return count
}
val waitCount get() = waitDownloadSet.size
val onDownloadCount get() = onDownloadSet.size
val successCount get() = successDownloadSet.size
val errorCount get() = errorDownloadMap.size
class CacheBookModel(var bookSource: BookSource, var book: Book) {
@Synchronized
fun isRun(): Boolean {
return waitDownloadSet.size > 0 || onDownloadSet.size > 0
}
private val waitDownloadSet = hashSetOf<Int>()
private val onDownloadSet = hashSetOf<Int>()
private val successDownloadSet = hashSetOf<Int>()
private val errorDownloadMap = hashMapOf<Int, Int>()
@Synchronized
fun stop() {
waitDownloadSet.clear()
}
val waitCount get() = waitDownloadSet.size
val onDownloadCount get() = onDownloadSet.size
val successCount get() = successDownloadSet.size
val errorCount get() = errorDownloadMap.size
@Synchronized
fun addDownload(start: Int, end: Int) {
for (i in start..end) {
if (!onDownloadSet.contains(i)) {
waitDownloadSet.add(i)
}
@Synchronized
fun isRun(): Boolean {
return waitDownloadSet.size > 0 || onDownloadSet.size > 0
}
}
@Synchronized
private fun onSuccess(index: Int) {
onDownloadSet.remove(index)
successDownloadSet.add(index)
errorDownloadMap.remove(index)
}
@Synchronized
fun stop() {
waitDownloadSet.clear()
}
@Synchronized
private fun onError(index: Int, error: Throwable, chapterTitle: String) {
if (error !is ConcurrentException) {
errorDownloadMap[index] = (errorDownloadMap[index] ?: 0) + 1
@Synchronized
fun addDownload(start: Int, end: Int) {
for (i in start..end) {
if (!onDownloadSet.contains(i)) {
waitDownloadSet.add(i)
}
}
}
onDownloadSet.remove(index)
//重试3次
if (errorDownloadMap[index] ?: 0 < 3) {
waitDownloadSet.add(index)
} else {
AppLog.addLog(
"下载${book.name}-${chapterTitle}失败\n${error.localizedMessage}",
error
)
error.printOnDebug()
@Synchronized
private fun onSuccess(index: Int) {
onDownloadSet.remove(index)
successDownloadSet.add(index)
errorDownloadMap.remove(index)
}
}
@Synchronized
private fun onCancel(index: Int) {
onDownloadSet.remove(index)
waitDownloadSet.add(index)
}
@Synchronized
private fun onError(index: Int, error: Throwable, chapterTitle: String) {
if (error !is ConcurrentException) {
errorDownloadMap[index] = (errorDownloadMap[index] ?: 0) + 1
}
onDownloadSet.remove(index)
//重试3次
if (errorDownloadMap[index] ?: 0 < 3) {
waitDownloadSet.add(index)
} else {
AppLog.addLog(
"下载${book.name}-${chapterTitle}失败\n${error.localizedMessage}",
error
)
error.printOnDebug()
}
}
@Synchronized
private fun onFinally() {
if (waitDownloadSet.isEmpty() && onDownloadSet.isEmpty()) {
postEvent(EventBus.UP_DOWNLOAD, "")
cacheBookMap.remove(book.bookUrl)
@Synchronized
private fun onCancel(index: Int) {
onDownloadSet.remove(index)
waitDownloadSet.add(index)
}
}
/**
* 从待下载列表内取第一条下载
*/
@Synchronized
fun download(scope: CoroutineScope, context: CoroutineContext): Boolean {
val chapterIndex = waitDownloadSet.firstOrNull()
if (chapterIndex == null) {
if (onDownloadSet.isEmpty()) {
@Synchronized
private fun onFinally() {
if (waitDownloadSet.isEmpty() && onDownloadSet.isEmpty()) {
postEvent(EventBus.UP_DOWNLOAD, "")
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)
}
waitDownloadSet.remove(chapterIndex)
onDownloadSet.add(chapterIndex)
WebBook.getContent(
scope,
bookSource,
book,
chapter,
context = context
).onSuccess { content ->
onSuccess(chapterIndex)
downloadFinish(chapter, content)
}.onError {
//出现错误等待一秒后重新加入待下载列表
delay(1000)
onError(chapterIndex, it, chapter.title)
downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}")
}.onCancel {
onCancel(chapterIndex)
}.onFinally {
onFinally()
}
return true
}
@Synchronized
fun download(
scope: CoroutineScope,
chapter: BookChapter,
resetPageOffset: Boolean = false
) {
if (onDownloadSet.contains(chapter.index)) {
return
}
onDownloadSet.add(chapter.index)
waitDownloadSet.remove(chapter.index)
WebBook.getContent(scope, bookSource, book, chapter)
.onSuccess { content ->
onSuccess(chapter.index)
downloadFinish(chapter, content, resetPageOffset)
/**
* 从待下载列表内取第一条下载
*/
@Synchronized
fun download(scope: CoroutineScope, context: CoroutineContext): Boolean {
val chapterIndex = waitDownloadSet.firstOrNull()
if (chapterIndex == null) {
if (onDownloadSet.isEmpty()) {
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)
}
waitDownloadSet.remove(chapterIndex)
onDownloadSet.add(chapterIndex)
WebBook.getContent(
scope,
bookSource,
book,
chapter,
context = context
).onSuccess { content ->
onSuccess(chapterIndex)
downloadFinish(chapter, content)
}.onError {
onError(chapter.index, it, chapter.title)
downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}", resetPageOffset)
//出现错误等待一秒后重新加入待下载列表
delay(1000)
onError(chapterIndex, it, chapter.title)
downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}")
}.onCancel {
onCancel(chapter.index)
onCancel(chapterIndex)
}.onFinally {
if (waitDownloadSet.isEmpty() && onDownloadSet.isEmpty()) {
postEvent(EventBus.UP_DOWNLOAD, "")
}
onFinally()
}
}
return true
}
private fun downloadFinish(
chapter: BookChapter,
content: String,
resetPageOffset: Boolean = false
) {
if (ReadBook.book?.bookUrl == book.bookUrl) {
ReadBook.contentLoadFinish(
book, chapter, content,
resetPageOffset = resetPageOffset
)
@Synchronized
fun download(
scope: CoroutineScope,
chapter: BookChapter,
resetPageOffset: Boolean = false
) {
if (onDownloadSet.contains(chapter.index)) {
return
}
onDownloadSet.add(chapter.index)
waitDownloadSet.remove(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, "获取正文失败\n${it.localizedMessage}", resetPageOffset)
}.onCancel {
onCancel(chapter.index)
}.onFinally {
if (waitDownloadSet.isEmpty() && onDownloadSet.isEmpty()) {
postEvent(EventBus.UP_DOWNLOAD, "")
}
}
}
private fun downloadFinish(
chapter: BookChapter,
content: String,
resetPageOffset: Boolean = false
) {
if (ReadBook.book?.bookUrl == book.bookUrl) {
ReadBook.contentLoadFinish(
book, chapter, content,
resetPageOffset = resetPageOffset
)
}
}
}
}
Loading…
Cancel
Save