This commit is contained in:
kunfei 2023-07-31 20:50:38 +08:00
parent 3d36422184
commit 93029ed1a1
8 changed files with 75 additions and 52 deletions

View File

@ -4,6 +4,7 @@ import android.content.Intent
import android.os.IBinder
import androidx.annotation.CallSuper
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import io.legado.app.R
import io.legado.app.help.LifecycleHelp
import io.legado.app.help.coroutine.Coroutine
@ -12,15 +13,13 @@ import io.legado.app.lib.permission.PermissionsCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive
import kotlin.coroutines.CoroutineContext
abstract class BaseService : LifecycleService(), CoroutineScope by MainScope() {
abstract class BaseService : LifecycleService() {
fun <T> execute(
scope: CoroutineScope = this,
scope: CoroutineScope = lifecycleScope,
context: CoroutineContext = Dispatchers.IO,
start: CoroutineStart = CoroutineStart.DEFAULT,
executeContext: CoroutineContext = Dispatchers.Main,
@ -49,7 +48,6 @@ abstract class BaseService : LifecycleService(), CoroutineScope by MainScope() {
@CallSuper
override fun onDestroy() {
super.onDestroy()
cancel()
LifecycleHelp.onServiceDestroy(this)
}
@ -68,7 +66,7 @@ abstract class BaseService : LifecycleService(), CoroutineScope by MainScope() {
.addPermissions(Permissions.POST_NOTIFICATIONS)
.rationale(R.string.notification_permission_rationale)
.onGranted {
if (isActive) {
if (lifecycleScope.isActive) {
upNotification()
}
}

View File

@ -15,6 +15,7 @@ import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import androidx.core.app.NotificationCompat
import androidx.lifecycle.lifecycleScope
import androidx.media.AudioFocusRequestCompat
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
@ -341,7 +342,7 @@ class AudioPlayService : BaseService(),
postEvent(EventBus.AUDIO_DS, timeMinute)
upNotification()
dsJob?.cancel()
dsJob = launch {
dsJob = lifecycleScope.launch {
while (isActive) {
delay(60000)
if (!pause) {
@ -363,7 +364,7 @@ class AudioPlayService : BaseService(),
*/
private fun upPlayProgress() {
upPlayProgressJob?.cancel()
upPlayProgressJob = launch {
upPlayProgressJob = lifecycleScope.launch {
while (isActive) {
AudioPlay.book?.let {
//更新buffer位置
@ -387,7 +388,7 @@ class AudioPlayService : BaseService(),
val book = AudioPlay.book
val bookSource = AudioPlay.bookSource
if (book != null && bookSource != null) {
WebBook.getContent(this@AudioPlayService, bookSource, book, chapter)
WebBook.getContent(lifecycleScope, bookSource, book, chapter)
.onSuccess { content ->
if (content.isEmpty()) {
toastOnUi("未获取到资源链接")

View File

@ -15,6 +15,7 @@ import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import androidx.annotation.CallSuper
import androidx.core.app.NotificationCompat
import androidx.lifecycle.lifecycleScope
import androidx.media.AudioFocusRequestCompat
import androidx.media.AudioManagerCompat
import io.legado.app.R
@ -160,18 +161,20 @@ abstract class BaseReadAloudService : BaseService(),
return super.onStartCommand(intent, flags, startId)
}
private fun newReadAloud(play: Boolean, pageIndex: Int, startPos: Int) = launch(IO) {
this@BaseReadAloudService.pageIndex = pageIndex
textChapter = ReadBook.curTextChapter
val textChapter = textChapter ?: return@launch
nowSpeak = 0
readAloudNumber = textChapter.getReadLength(pageIndex) + startPos
val readAloudByPage = getPrefBoolean(PreferKey.readAloudByPage)
contentList = textChapter.getNeedReadAloud(pageIndex, readAloudByPage, startPos)
.split("\n")
.filter { it.isNotEmpty() }
launch(Main) {
if (play) play() else pageChanged = true
private fun newReadAloud(play: Boolean, pageIndex: Int, startPos: Int) {
lifecycleScope.launch(IO) {
this@BaseReadAloudService.pageIndex = pageIndex
textChapter = ReadBook.curTextChapter
val textChapter = textChapter ?: return@launch
nowSpeak = 0
readAloudNumber = textChapter.getReadLength(pageIndex) + startPos
val readAloudByPage = getPrefBoolean(PreferKey.readAloudByPage)
contentList = textChapter.getNeedReadAloud(pageIndex, readAloudByPage, startPos)
.split("\n")
.filter { it.isNotEmpty() }
launch(Main) {
if (play) play() else pageChanged = true
}
}
}
@ -256,7 +259,7 @@ abstract class BaseReadAloudService : BaseService(),
postEvent(EventBus.READ_ALOUD_DS, timeMinute)
upNotification()
dsJob?.cancel()
dsJob = launch {
dsJob = lifecycleScope.launch {
while (isActive) {
delay(60000)
if (!pause) {

View File

@ -2,6 +2,7 @@ package io.legado.app.service
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.lifecycle.lifecycleScope
import io.legado.app.R
import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst
@ -61,7 +62,7 @@ class CacheBookService : BaseService() {
isRun = true
CacheBook.successDownloadSet.clear()
CacheBook.errorDownloadMap.clear()
launch {
lifecycleScope.launch {
while (isActive) {
delay(1000)
notificationContent = CacheBook.downloadSummary
@ -142,7 +143,7 @@ class CacheBookService : BaseService() {
private fun download() {
downloadJob?.cancel()
downloadJob = launch(cachePool) {
downloadJob = lifecycleScope.launch(cachePool) {
while (isActive) {
if (!CacheBook.isRun) {
CacheBook.stop(this@CacheBookService)

View File

@ -2,6 +2,7 @@ package io.legado.app.service
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.lifecycle.lifecycleScope
import com.script.ScriptException
import io.legado.app.R
import io.legado.app.base.BaseService
@ -109,7 +110,7 @@ class CheckSourceService : BaseService() {
synchronized(this) {
processIndex++
}
launch(IO) {
lifecycleScope.launch(IO) {
if (index < allIds.size) {
val sourceUrl = allIds[index]
appDb.bookSourceDao.getBookSource(sourceUrl)?.let { source ->

View File

@ -9,6 +9,7 @@ import android.content.IntentFilter
import android.net.Uri
import android.os.Environment
import androidx.core.app.NotificationCompat
import androidx.lifecycle.lifecycleScope
import io.legado.app.R
import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst
@ -147,7 +148,7 @@ class DownloadService : BaseService() {
private fun checkDownloadState() {
upStateJob?.cancel()
upStateJob = launch {
upStateJob = lifecycleScope.launch {
while (isActive) {
queryState()
delay(1000)

View File

@ -9,6 +9,7 @@ import android.os.Build
import android.util.ArraySet
import androidx.core.app.NotificationCompat
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
@ -53,6 +54,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.ag2s.epublib.domain.Author
import me.ag2s.epublib.domain.Date
import me.ag2s.epublib.domain.EpubBook
@ -171,7 +173,7 @@ class ExportBookService : BaseService() {
if (exportProgress.contains(bookUrl)) return
exportProgress[bookUrl] = 0
waitExportBooks.remove(bookUrl)
exportJob = launch(IO) {
exportJob = lifecycleScope.launch(IO) {
val book = appDb.bookDao.getBook(bookUrl)
try {
book ?: throw NoStackTraceException("获取${bookUrl}书籍出错")
@ -205,6 +207,12 @@ class ExportBookService : BaseService() {
}
}
private data class SrcData(
val chapterTitle: String,
val index: Int,
val src: String
)
private suspend fun export(path: String, book: Book) {
exportMsg.remove(book.bookUrl)
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
@ -227,12 +235,16 @@ class ExportBookService : BaseService() {
getAllContents(book) { text, srcList ->
bookOs.write(text.toByteArray(Charset.forName(AppConfig.exportCharset)))
srcList?.forEach {
val vFile = BookHelp.getImage(book, it.third)
val vFile = BookHelp.getImage(book, it.src)
if (vFile.exists()) {
DocumentUtils.createFileIfNotExist(
doc,
"${it.second}-${MD5Utils.md5Encode16(it.third)}.jpg",
subDirs = arrayOf("${book.name}_${book.author}", "images", it.first)
"${it.index}-${MD5Utils.md5Encode16(it.src)}.jpg",
subDirs = arrayOf(
"${book.name}_${book.author}",
"images",
it.chapterTitle
)
)?.writeBytes(this, vFile.readBytes())
}
}
@ -251,14 +263,14 @@ class ExportBookService : BaseService() {
getAllContents(book) { text, srcList ->
bookFile.appendText(text, Charset.forName(AppConfig.exportCharset))
srcList?.forEach {
val vFile = BookHelp.getImage(book, it.third)
val vFile = BookHelp.getImage(book, it.src)
if (vFile.exists()) {
FileUtils.createFileIfNotExist(
file,
"${book.name}_${book.author}",
"images",
it.first,
"${it.second}-${MD5Utils.md5Encode16(it.third)}.jpg"
it.chapterTitle,
"${it.index}-${MD5Utils.md5Encode16(it.src)}.jpg"
).writeBytes(vFile.readBytes())
}
}
@ -270,8 +282,8 @@ class ExportBookService : BaseService() {
private suspend fun getAllContents(
book: Book,
append: (text: String, srcList: ArrayList<Triple<String, Int, String>>?) -> Unit
) {
append: (text: String, srcList: ArrayList<SrcData>?) -> Unit
) = withContext(IO) {
val useReplace = AppConfig.exportUseReplace && book.getUseReplaceRule()
val contentProcessor = ContentProcessor.get(book.name, book.origin)
val qy = "${book.name}\n${
@ -285,7 +297,7 @@ class ExportBookService : BaseService() {
append(qy, null)
if (AppConfig.parallelExportBook) {
val oc =
OrderCoroutine<Pair<String, ArrayList<Triple<String, Int, String>>?>>(AppConfig.threadCount)
OrderCoroutine<Pair<String, ArrayList<SrcData>?>>(AppConfig.threadCount)
appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter ->
oc.submit { getExportData(book, chapter, contentProcessor, useReplace) }
}
@ -296,7 +308,7 @@ class ExportBookService : BaseService() {
}
} else {
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter ->
kotlin.coroutines.coroutineContext.ensureActive()
ensureActive()
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
exportProgress[book.bookUrl] = index
val result = getExportData(book, chapter, contentProcessor, useReplace)
@ -311,7 +323,7 @@ class ExportBookService : BaseService() {
chapter: BookChapter,
contentProcessor: ContentProcessor,
useReplace: Boolean
): Pair<String, ArrayList<Triple<String, Int, String>>?> {
): Pair<String, ArrayList<SrcData>?> {
BookHelp.getContent(book, chapter).let { content ->
val content1 = contentProcessor
.getContent(
@ -326,13 +338,13 @@ class ExportBookService : BaseService() {
).toString()
if (AppConfig.exportPictureFile) {
//txt导出图片文件
val srcList = arrayListOf<Triple<String, Int, String>>()
val srcList = arrayListOf<SrcData>()
content?.split("\n")?.forEachIndexed { index, text ->
val matcher = AppPattern.imgPattern.matcher(text)
while (matcher.find()) {
matcher.group(1)?.let {
val src = NetworkUtils.getAbsoluteURL(chapter.url, it)
srcList.add(Triple(chapter.title, index, src))
srcList.add(SrcData(chapter.title, index, src))
}
}
}
@ -594,12 +606,12 @@ class ExportBookService : BaseService() {
contentModel: String,
book: Book,
epubBook: EpubBook
) {
) = withContext(IO) {
//正文
val useReplace = AppConfig.exportUseReplace && book.getUseReplaceRule()
val contentProcessor = ContentProcessor.get(book.name, book.origin)
appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter ->
kotlin.coroutines.coroutineContext.ensureActive()
ensureActive()
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
exportProgress[book.bookUrl] = index
BookHelp.getContent(book, chapter).let { content ->
@ -701,7 +713,7 @@ class ExportBookService : BaseService() {
suspend fun export(
path: String,
book: Book
) {
) = withContext(IO) {
exportProgress[book.bookUrl] = 0
exportMsg.remove(book.bookUrl)
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
@ -729,7 +741,7 @@ class ExportBookService : BaseService() {
progressBar += book.totalChapterNum.toDouble() / scope.size / 2
exportProgress[book.bookUrl] = progressBar.toInt()
}
save2Drive(filename, epubBook, doc) { total, progress ->
save2Drive(filename, epubBook, doc) { total, _ ->
//写入硬盘时更新进度条
progressBar += book.totalChapterNum.toDouble() / epubList.size / total / 2
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
@ -761,7 +773,7 @@ class ExportBookService : BaseService() {
exportProgress[book.bookUrl]?.plus(book.totalChapterNum / scope.size)
?: 1
}
save2Drive(filename, epubBook, file) { total, progress ->
save2Drive(filename, epubBook, file) { total, _ ->
//设置进度
progressBar += book.totalChapterNum.toDouble() / epubList.size / total / 2
postEvent(EventBus.EXPORT_BOOK, book.bookUrl)
@ -785,13 +797,13 @@ class ExportBookService : BaseService() {
* @param epubBook 分割后的epub
* @param epubBookIndex 分割后的epub序号
*/
private fun setEpubContent(
private suspend fun setEpubContent(
contentModel: String,
book: Book,
epubBook: EpubBook,
epubBookIndex: Int,
updateProgress: (chapterList: MutableList<BookChapter>, index: Int) -> Unit
) {
) = withContext(IO) {
//正文
val useReplace = AppConfig.exportUseReplace && book.getUseReplaceRule()
val contentProcessor = ContentProcessor.get(book.name, book.origin)
@ -813,7 +825,7 @@ class ExportBookService : BaseService() {
if ((epubBookIndex + 1) * size > scope.size) scope.size else (epubBookIndex + 1) * size
)
chapterList.forEachIndexed { index, chapter ->
coroutineContext.ensureActive()
ensureActive()
updateProgress(chapterList, index)
BookHelp.getContent(book, chapter).let { content ->
var content1 = fixPic(

View File

@ -2,6 +2,7 @@ package io.legado.app.service
import android.app.PendingIntent
import android.net.Uri
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
@ -26,6 +27,7 @@ import io.legado.app.utils.printOnDebug
import io.legado.app.utils.servicePendingIntent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@ -33,6 +35,7 @@ import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import okhttp3.Response
import org.mozilla.javascript.WrappedException
import java.io.File
@ -151,7 +154,10 @@ class HttpReadAloudService : BaseReadAloudService(),
}
}
private suspend fun getSpeakStream(httpTts: HttpTTS, speakText: String): InputStream? {
private suspend fun getSpeakStream(
httpTts: HttpTTS,
speakText: String
): InputStream? = withContext(IO) {
while (true) {
try {
val analyzeUrl = AnalyzeUrl(
@ -180,7 +186,7 @@ class HttpReadAloudService : BaseReadAloudService(),
ensureActive()
response.body!!.byteStream().let { stream ->
downloadErrorNo = 0
return stream
return@withContext stream
}
} catch (e: Exception) {
when (e) {
@ -221,7 +227,7 @@ class HttpReadAloudService : BaseReadAloudService(),
}
}
}
return null
return@withContext null
}
private fun md5SpeakFileName(content: String): String {
@ -294,7 +300,7 @@ class HttpReadAloudService : BaseReadAloudService(),
private fun upPlayPos() {
playIndexJob?.cancel()
val textChapter = textChapter ?: return
playIndexJob = launch {
playIndexJob = lifecycleScope.launch {
postEvent(EventBus.TTS_PROGRESS, readAloudNumber + 1)
if (exoPlayer.duration <= 0) {
return@launch