Elye
Elye

Reputation: 60081

Is Coroutine job auto cancelled upon exiting Activity?

I have the below code of a slow loading image

class MainActivity : AppCompatActivity() {
    private lateinit var job: Job

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val imageLoader = ImageLoader.Builder(this)
            .componentRegistry { add(SvgDecoder(this@MainActivity)) }
            .build()

        job = MainScope().launch {
            try {
                val request = ImageRequest.Builder(this@MainActivity)
                    .data("https://restcountries.eu/data/afg.svg")
                    .build()
                val drawable = imageLoader.execute(request).drawable
                Log.d("TrackLog", "Loaded")
                findViewById<ImageView>(R.id.my_view).setImageDrawable(drawable)
            } catch (e: CancellationException) {
                Log.d("TrackLog", "Cancelled job")
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
//        job.cancel()
    }
}

If I exit the activity before the image loaded completed, I thought I should manually perform job.cancel() to get the coroutine canceled.

However, even when I commented out the job.cancel(), the job still get canceled when I exit MainActivity.

This is also true when I use either GlobalScope or even use a global variable scope and job.

val myScope = CoroutineScope(Dispatchers.IO)
private lateinit var job: Job

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val imageLoader = ImageLoader.Builder(this)
            .componentRegistry { add(SvgDecoder(this@MainActivity)) }
            .build()

        job = myScope.launch {
            try {
                val request = ImageRequest.Builder(this@MainActivity)
                    .data("https://restcountries.eu/data/afg.svg")
                    .build()
                val drawable = imageLoader.execute(request).drawable
                Log.d("TrackLog", "Loaded")
                findViewById<ImageView>(R.id.my_view).setImageDrawable(drawable)
            } catch (e: CancellationException) {
                Log.d("TrackLog", "Cancelled job")
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
//        job.cancel()
    }
}

I'm puzzled how did the job get canceled when we exit the Activity even when I don't call job.cancel().

Upvotes: 1

Views: 437

Answers (1)

Elye
Elye

Reputation: 60081

Apparently, because my request is made of this@MainActivity

                val request = ImageRequest.Builder(this@MainActivity)
                    .data("https://restcountries.eu/data/afg.svg")
                    .build()

hence, when exiting, the this@MainActivity is killed, hence the request also got terminated and perhaps canceled?

If we use baseContext

                val request = ImageRequest.Builder(baseContext)
                    .data("https://restcountries.eu/data/afg.svg")
                    .build()

then we have to manually cancel the job during onDestroy

Therefore it is always safer to use lifecycleScope

Upvotes: 2

Related Questions