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

时间:2019-03-11 14:48:25

标签: java android kotlin service android-service

我正在实现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) downloadFile 方法返回后立即从 downloadFile 方法返回null e.message(在这种情况下,它返回"Unknown Error")之外。 catch (e : Exception)中的处理。 有什么方法/替代方法可以在某些事件中保持文件下载和更新通知?

注意:

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

修改: 跟随m0skit0's Answer 在onCreate方法中,我创建了一个通知,该通知将在整个下载过程中可见,显示等待处理的下载数量,同时显示另一个通知以及每个下载过程的进度。通过在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)
    

    }

1 个答案:

答案 0 :(得分:1)

要使Service保持有效,您可以使用startForeground API。

来自documentation

  

已启动的服务可以使用startForeground(int, Notification)   用于将服务置于前台状态的API   认为它是用户积极了解的东西,因此   内存不足时不适合杀死。 (它还是   从理论上讲,在极端情况下可以杀死该服务   当前前台应用程序的内存压力,但是   实践,这不应该是一个问题。)