Issue
I have an app that allows a user to do multiple downloads.
- For example, the user clicks an icon, a fragment comes up starting the download and shows the progress by updating the progress bar every second.
- The user can press the back button, the fragment is destroyed and can click a different button, downloading a different item in parallel.
- If the user goes back to click the first icon, and the download is still ongoing, the requirement is that the progress bar will be shown automatically so that the user knows the download is not done yet.
My problem is #3 where if the user presses back and the Asynctask loses the UI, the progress updates stop on resume. How do I get the updates for the correct item being downloaded, since the feature is multiple parallel downloads?
Currently, I am wrapping the AsyncTask in a fragment to retain the instance. Could anyone advise on what is the proper way to do this (minimizing memory leaks as much as possible)?
class DownloadTaskFragment : Fragment() {
private var mCallbacks: TaskCallbacks? = null
private var mTask: DummyTask? = null
interface TaskCallbacks {
fun onPreExecute()
fun onProgressUpdate(progress: Int)
fun onCancelled()
fun onPostExecute()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
mTask = DummyTask(this)
mTask!!.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
fun setCallBackInterface(taskCallback: TaskCallbacks) {
mCallbacks = taskCallback
}
override fun onDetach() {
super.onDetach()
mCallbacks = null
}
class DummyTask// only retain a weak reference to the activity
internal constructor(context: DownloadTaskFragment) : AsyncTask<String, Int, Int>() {
private val reference: WeakReference<DownloadTaskFragment> = WeakReference(context)
private var mCallbacks: TaskCallbacks? = reference.get()!!.mCallbacks
override fun onPreExecute() {
if (mCallbacks != null) {
mCallbacks!!.onPreExecute()
}
}
override fun doInBackground(vararg params: String?): Int {
var i = 0
while (!isCancelled && i < 80) {
SystemClock.sleep(100)
publishProgress(i)
i++
}
return 1
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
if (mCallbacks != null) {
mCallbacks!!.onProgressUpdate(values[0]!!)
}
}
override fun onPostExecute(result: Int?) {
if (mCallbacks != null) {
mCallbacks!!.onPostExecute()
}
}
override fun onCancelled() {
if (mCallbacks != null) {
mCallbacks!!.onCancelled()
}
}
}
}
Solution
In order to make sure that the task continues in the background (especially with the increased restrictions in API 26+) you will need to create a foreground service. Note: you MUST show an accompanying notification (this can simply be a progress bar)
As a foreground service, your task will run separately from the main UI and therefore will not be effected by any changes to the UI. You can also update the UI from the service (see here for more information)
Answered By - Django
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.