Reputation: 2511
I'm implementing an IntentService
to download a PDF file, it shows a notification to the user on downloadStart
and keep updating progress during this process, the service is working fine and the progress is updated correctly, the problem is as soon as i remove my app from "Recents" the download stops without even showing an error.
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)
}
})
}
}
}
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)
}
}
}
}
After a long searching on the internet, i have found that using a Service
instead of IntentService
will solve the problem, i have changed my class structure to inherit from Service()
instead, everything worked fine except for the onError(message : String)
returning a null
e.message
(in this case it returns "Unknown Error"
) from the downloadFile method immediately after starting the process in catch (e : Exception)
.
Is there any way/alternative to keep the file downloading and updating the notification on certain events?
Notes :
AsyncTask
before but my file take pretty long to download which is not a good approach (fileSize in 5..150 MB).ThreadPoolExcuter
/Thread
that updates notification using runOnUiThread
but it also get killed on application termination.
Thank you!Edit :
following m0skit0's Answer
in the onCreate method, i have created a notification that will be visible during the whole downloading process, showing the number of downloads waiting to handled while it shows an other notification with progress for each downloading process. by calling startForeground(ID, notification)
in onCreate, the service will be available even the app is killed.
Here's my new onCreate() method :
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)
}
Upvotes: 0
Views: 42
Reputation: 25873
To keep a Service
alive you can use the startForeground
API.
From the documentation:
A started service can use the
startForeground(int, Notification)
API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory. (It is still theoretically possible for the service to be killed under extreme memory pressure from the current foreground application, but in practice this should not be a concern.)
Upvotes: 1