IntentService / Service - 即使应用程序终止也继续运行 - Android

问题描述 投票:0回答:1

我正在实施IntentService下载PDF文件,它在downloadStart上向用户显示通知并在此过程中不断更新进度,服务正常工作并且进度正确更新,问题是我一删除我的来自“最近”的应用程序下载停止,甚至没有显示错误。

  • 这是我的DownloadService类:

class DownloadService : IntentService("DownloadService") {

lateinit var  downloadNotification : DownloadNotification
lateinit var book : BookData
private lateinit var fileName : String
private lateinit var fileFolder : String
private lateinit var filePath : String
lateinit var fileUrl : String
var isCancelled = false
private lateinit var handler : Handler

override fun onCreate() {
    super.onCreate()
    handler = Handler()
}


override fun onHandleIntent(p0: Intent?) {
    book = Gson().fromJson<BookData>(p0?.getStringExtra("book"), BookData::class.java)
    downloadNotification = DownloadNotification(this, book.id!!)
    init(book)
}

fun getFilePath() : String {
    val directory = File(fileFolder)
    if (!directory.exists()) {
        directory.mkdirs()
    }
    return filePath
}

private fun init(book : BookData) {
    fileName = "${book.id}.pdf"
    fileFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString() + File.separator + "Libranova/Books/"
    filePath = fileFolder + fileName
    fileUrl = book.downloadLink!!
    startDownload()
}

private fun startDownload() {
    downloadNotification.setTitle(book.name!!).setText("Preparing...").notificationCompat.apply {
        downloadNotification.notifyManager(true)
        DownloadUtils.downloadFile(this@DownloadService, object : DownloadListener {
            override fun onStarted() {
                handler.post {
                    Toast.makeText(this@DownloadService,"Download Started", Toast.LENGTH_LONG).show()
                }
            }

            override fun onSuccess() {
                downloadNotification.onFinishDownload().freeUp().setSuccess().notifyManager(true)
            }

            override fun onError(message: String) {
                downloadNotification.onFinishDownload().freeUp().setError(message).notifyManager(true)
            }

            override fun onCanceled() {
                downloadNotification.cancel()
            }

            override fun onProgress(progress: Int) {
                downloadNotification.setProgress(progress).setText("$progress%").notifyManager(false)
            }

        })
    }

}
}
  • 这是我的downloadFile方法,位于object

object DownloadUtils {
    
    fun downloadFile(downloadService: DownloadService, downloadListener: DownloadListener) {
        try {
            val url = URL(downloadService.fileUrl)
            val connection = url.openConnection()
            connection.connect()
            val lengthOfFile = connection.contentLength
            val input = BufferedInputStream(url.openStream(), 8192)
            val output = FileOutputStream(downloadService.getFilePath())
            val data = ByteArray(1024)
            var total: Long = 0
            var count = input.read(data)
            downloadListener.onStarted()
            while (count != -1)  {
                if (!downloadService.isCancelled) {
                    total += count.toLong()
                    downloadListener.onProgress(((total * 100) / lengthOfFile).toInt())
                    output.write(data, 0, count)
                    count = input.read(data)
                }
                else break
            }
            output.flush()
            output.close()
            input.close()
            if (downloadService.isCancelled) downloadListener.onCanceled() else downloadListener.onSuccess()
        }
        catch (e : Exception) {
            downloadListener.onError(e.message ?: "Unknown Error")
        }
    }

    fun fastFileDownload(downloadService: DownloadService) {
        URL(downloadService.fileUrl).openStream().use { input ->
            val folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString() + File.separator + "Libranova/Books/"
            val directory = File(folder)
            if (!directory.exists()) {
                directory.mkdirs()
            }
            FileOutputStream(File(downloadService.getFilePath())).use { output ->
                input.copyTo(output)
            }
        }
    }
}

经过长时间的互联网搜索,我发现使用Service而不是IntentService将解决问题,我改变了我的类结构继承Service()而不是一切正常,除了onError(message : String)返回null e.message(在这种情况下)它在"Unknown Error"中启动进程后立即从downloadFile方法返回catch (e : Exception))。是否有任何方法/替代方法可以保留文件下载和更新某些事件的通知?

备注:

  • 我之前使用过AsyncTask,但我的文件下载时间很长,这不是一个好方法(fileSize在5..150 MB)。
  • 我使用ThreadPoolExcuter / Thread使用runOnUiThread更新通知,但它也会在应用程序终止时被杀死。谢谢!

编辑:在onCreate方法中跟随m0skit0's Answer,我创建了一个通知,该通知在整个下载过程中可见,显示等待处理的下载次数,同时显示每个下载过程的进度的其他通知。通过在onCreate中调用startForeground(ID, notification),即使应用程序被杀,该服务也将可用。

  • 这是我的新onCreate()方法: override fun onCreate() { super.onCreate() handler = Handler() val notificationBuilder = NotificationCompat.Builder(this, "LibranovaDownloadService") val notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(NotificationCompat.PRIORITY_HIGH) .setSubText("Download Queue") .setContentText("Waiting For Download : 1 Book") .setCategory(NotificationCompat.CATEGORY_SERVICE) .build() startForeground(123, notification) }
java android kotlin service android-service
1个回答
1
投票

为了让Service保持活力,您可以使用startForeground API。

来自documentation

启动的服务可以使用startForeground(int, Notification) API将服务置于前台状态,系统认为该服务是用户主动了解的内容,因此在内存不足时不会成为查杀的候选者。 (从理论上讲,服务在当前前台应用程序的极端内存压力下被杀死仍然是可能的,但实际上这不应该是一个问题。)

© www.soinside.com 2019 - 2024. All rights reserved.