kunfei 3 سال پیش
والد
کامیت
26ab592b74

+ 4 - 6
app/src/main/java/io/legado/app/base/BaseService.kt

@@ -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()
                 }
             }

+ 4 - 3
app/src/main/java/io/legado/app/service/AudioPlayService.kt

@@ -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("未获取到资源链接")

+ 16 - 13
app/src/main/java/io/legado/app/service/BaseReadAloudService.kt

@@ -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) {

+ 3 - 2
app/src/main/java/io/legado/app/service/CacheBookService.kt

@@ -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)

+ 2 - 1
app/src/main/java/io/legado/app/service/CheckSourceService.kt

@@ -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 ->

+ 2 - 1
app/src/main/java/io/legado/app/service/DownloadService.kt

@@ -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)

+ 34 - 22
app/src/main/java/io/legado/app/service/ExportBookService.kt

@@ -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(

+ 10 - 4
app/src/main/java/io/legado/app/service/HttpReadAloudService.kt

@@ -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