feat: 优化下载

pull/262/head
gedoor 5 years ago
parent cdbb2cabe4
commit 09a405647a
  1. 185
      app/src/main/java/io/legado/app/service/DownloadService.kt

@ -9,6 +9,7 @@ import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
@ -24,14 +25,21 @@ import org.jetbrains.anko.toast
import java.util.concurrent.Executors import java.util.concurrent.Executors
class DownloadService : BaseService() { class DownloadService : BaseService() {
private val threadCount = AppConfig.threadCount
private var searchPool = private var searchPool =
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher()
private var tasks = CompositeCoroutine() private var tasks = CompositeCoroutine()
private val handler = Handler() private val handler = Handler()
private var runnable: Runnable = Runnable { upDownload() } private var runnable: Runnable = Runnable { upDownload() }
private val bookMap = hashMapOf<String, Book>()
private val webBookMap = hashMapOf<String, WebBook>()
private val downloadMap = hashMapOf<String, LinkedHashSet<BookChapter>>() private val downloadMap = hashMapOf<String, LinkedHashSet<BookChapter>>()
private val downloadCount = hashMapOf<String, DownloadCount>() private val downloadCount = hashMapOf<String, DownloadCount>()
private val finalMap = hashMapOf<String, LinkedHashSet<BookChapter>>() private val finalMap = hashMapOf<String, LinkedHashSet<BookChapter>>()
private val downloadingList = arrayListOf<String>()
@Volatile
private var downloadingCount = 0
private var notificationContent = "正在启动下载" private var notificationContent = "正在启动下载"
private val notificationBuilder by lazy { private val notificationBuilder by lazy {
@ -78,20 +86,50 @@ class DownloadService : BaseService() {
postEvent(EventBus.UP_DOWNLOAD, downloadMap) postEvent(EventBus.UP_DOWNLOAD, downloadMap)
} }
private fun getBook(bookUrl: String): Book? {
var book = bookMap[bookUrl]
if (book == null) {
book = App.db.bookDao().getBook(bookUrl)
}
if (book == null) {
removeDownload(bookUrl)
}
return book
}
private fun getWebBook(bookUrl: String, origin: String): WebBook? {
var webBook = webBookMap[origin]
if (webBook == null) {
App.db.bookSourceDao().getBookSource(origin)?.let {
webBook = WebBook(it)
}
}
if (webBook == null) {
removeDownload(bookUrl)
}
return webBook
}
private fun addDownloadData(bookUrl: String?, start: Int, end: Int) { private fun addDownloadData(bookUrl: String?, start: Int, end: Int) {
bookUrl ?: return bookUrl ?: return
if (downloadMap.containsKey(bookUrl)) { if (downloadMap.containsKey(bookUrl)) {
toast("该书已在下载列表") toast("该书已在下载列表")
return return
} }
downloadCount[bookUrl] = DownloadCount()
execute { execute {
val chapterMap = downloadMap[bookUrl] ?: linkedSetOf<BookChapter>().apply {
downloadMap[bookUrl] = this
}
App.db.bookChapterDao().getChapterList(bookUrl, start, end).let { App.db.bookChapterDao().getChapterList(bookUrl, start, end).let {
chapterMap.addAll(it) if (it.isNotEmpty()) {
val chapters = linkedSetOf<BookChapter>()
chapters.addAll(it)
downloadMap[bookUrl] = chapters
}
}
for (i in 0 until threadCount) {
if (downloadingCount < threadCount) {
download()
}
} }
download()
} }
} }
@ -100,70 +138,84 @@ class DownloadService : BaseService() {
finalMap.remove(bookUrl) finalMap.remove(bookUrl)
} }
private fun updateNotification(downloadCount:DownloadCount, totalCount: Int, content: String){
notificationContent =
"进度:${downloadCount.downloadFinishedCount}/$totalCount,成功:${downloadCount.successCount},$content"
}
private fun download() { private fun download() {
val task = Coroutine.async(this, context = searchPool) { ++downloadingCount
downloadMap.forEach { entry -> tasks.add(Coroutine.async(this, context = searchPool) {
if (!isActive) return@async if (!isActive) return@async
if (!finalMap.containsKey(entry.key)) { val bookChapter: BookChapter? = synchronized(this@DownloadService) {
val book = App.db.bookDao().getBook(entry.key) ?: return@async downloadMap.forEach {
val bookSource = it.value.forEach { chapter ->
App.db.bookSourceDao().getBookSource(book.origin) ?: return@async if (!downloadingList.contains(chapter.url)) {
val webBook = WebBook(bookSource) downloadingList.add(chapter.url)
return@synchronized chapter
downloadCount[entry.key] = DownloadCount() }
}
entry.value.forEach { chapter -> }
if (!isActive) return@async return@synchronized null
if (downloadMap.containsKey(book.bookUrl)) { }
if (!BookHelp.hasContent(book, chapter)) { if (bookChapter == null) {
webBook.getContent( postDownloading(false)
book, } else {
chapter, val book = getBook(bookChapter.bookUrl)
scope = this, if (book == null) {
context = searchPool postDownloading(true)
).onSuccess(IO) { content -> return@async
downloadCount[entry.key]?.increaseSuccess() }
BookHelp.saveContent(book, chapter, content) val webBook = getWebBook(bookChapter.bookUrl, book.origin)
} if (webBook == null) {
.onFinally(IO) { postDownloading(true)
synchronized(this@DownloadService) { return@async
downloadCount[entry.key]?.increaseFinished() }
downloadCount[entry.key]?.let { updateNotification(it, entry.value.size, chapter.title) } if (!BookHelp.hasContent(book, bookChapter)) {
val chapterMap = webBook.getContent(
finalMap[book.bookUrl] book,
?: linkedSetOf<BookChapter>().apply { bookChapter,
finalMap[book.bookUrl] = this scope = this,
} context = searchPool
chapterMap.add(chapter) ).onSuccess(IO) { content ->
if (chapterMap.size == entry.value.size) { downloadCount[book.bookUrl]?.increaseSuccess()
downloadMap.remove(book.bookUrl) BookHelp.saveContent(book, bookChapter, content)
finalMap.remove(book.bookUrl) }.onFinally(IO) {
downloadCount.remove(entry.key) synchronized(this@DownloadService) {
} downloadCount[book.bookUrl]?.increaseFinished()
} downloadCount[book.bookUrl]?.let {
updateNotification(
it,
downloadMap[book.bookUrl]?.size,
bookChapter.title
)
}
val chapterMap =
finalMap[book.bookUrl]
?: linkedSetOf<BookChapter>().apply {
finalMap[book.bookUrl] = this
} }
} else{ chapterMap.add(bookChapter)
//无需下载的,设置为增加成功 if (chapterMap.size == downloadMap[book.bookUrl]?.size) {
downloadCount[entry.key]?.increaseSuccess() downloadMap.remove(book.bookUrl)
downloadCount[entry.key]?.increaseFinished() finalMap.remove(book.bookUrl)
downloadCount.remove(book.bookUrl)
} }
} }
} }
postDownloading(true)
} else {
//无需下载的,设置为增加成功
downloadCount[book.bookUrl]?.increaseSuccess()
downloadCount[book.bookUrl]?.increaseFinished()
postDownloading(true)
} }
} }
} })
}
tasks.add(task) private fun postDownloading(hasChapter: Boolean) {
task.invokeOnCompletion { --downloadingCount
tasks.remove(task) if (hasChapter) {
if (tasks.isEmpty) { download()
stopSelf() } else {
if (downloadingCount < 1) {
stopDownload()
} }
} }
} }
@ -180,6 +232,15 @@ class DownloadService : BaseService() {
handler.postDelayed(runnable, 1000) handler.postDelayed(runnable, 1000)
} }
private fun updateNotification(
downloadCount: DownloadCount,
totalCount: Int?,
content: String
) {
notificationContent =
"进度:${downloadCount.downloadFinishedCount}/$totalCount,成功:${downloadCount.successCount},$content"
}
/** /**
* 更新通知 * 更新通知
*/ */

Loading…
Cancel
Save