Из-за чего может появляться исключение kotlin.TypeCastException для Activity?

В консоли Google Play смотрю отчет по ошибкам и вижу следующее:

kotlin.TypeCastException: 
  at com.app.android.ui.SplashFragment.loadAds (SplashFragment.java:111)
  at com.app.android.ui.SplashFragment$requestConsentInfoUpdate$1.onConsentInfoUpdated (SplashFragment.java:68)
  at com.google.ads.consent.ConsentInformation$ConsentInfoUpdateTask.onPostExecute (ConsentInformation.java:277)
  at com.google.ads.consent.ConsentInformation$ConsentInfoUpdateTask.onPostExecute (ConsentInformation.java:188)
  at android.os.AsyncTask.finish (AsyncTask.java:632)
  at android.os.AsyncTask.access$600 (AsyncTask.java:177)
  at android.os.AsyncTask$InternalHandler.handleMessage (AsyncTask.java:645)
  at android.os.Handler.dispatchMessage (Handler.java:102)
  at android.os.Looper.loop (Looper.java:145)
  at android.app.ActivityThread.main (ActivityThread.java:5942)
  at java.lang.reflect.Method.invoke (Method.java)
  at java.lang.reflect.Method.invoke (Method.java:372)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1400)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1195)

Также гугл предоставляет дополнительную. информацию по ошибке: введите сюда описание изображения

Я пытался запустить приложение на разных версиях Android, но у меня ошибки не возникало

Приложение состоит из одного активити (MainActivity) и нескольких фрагментов.

Метод в SplashFragment, на котором появляется ошибка:

fun loadAds() {
        (activity as MainActivity).forceShowAd(callback = { // <- вот тут возникает исключение
            Log.i(TAG, "load ads");
            (activity as MainActivity).toMain()
        })
    }

Вызываемый метод в MainActivity:

fun forceShowAd(callback: () -> Unit) {
        Log.i(TAG, "forceShowAd")
        run {
            val mInterstitialAd = InterstitialAd(this)
            mInterstitialAd.apply {
                adUnitId = getString(R.string.admob_inter)
                adListener = object : AdListener() {
                    override fun onAdLoaded() {
                        callback()
                        show()
                    }

                    override fun onAdFailedToLoad(p0: Int) {
                        super.onAdFailedToLoad(p0)
                        var mStartAppAd = StartAppAd(applicationContext)
                        mStartAppAd.loadAd(object : AdEventListener {
                            override fun onReceiveAd(ad: Ad) {
                                mStartAppAd.showAd()
                                callback()
                            }
                            override fun onFailedToReceiveAd(ad: Ad) {
                                callback()
                            }
                        })
                    }
                }
                loadAd(getAdRequest())
            }
        }

    }

Вот так запускается SplashFragment в MainActivity:

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

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                    .replace(R.id.container, SplashFragment.newInstance())
                    .commit()
        }
    }

Исходя из жизненного цикла приложения, пока работает фрагмент, должно работать и активити. Но при этом, как я понял, на момент срабатывания исключения activity == null

Из-за чего может возникать проблема с исключением? Такая проблема встречается аж на 6% устройств


Ответы (1 шт):

Автор решения: Eugene Krivenja

Фрагмент может в любой момент перейти состояние "отключен от активити".
При выполнении асинхронных операций в фрагменте нужно всегда об этом помнить. Пока она выполняется, может произойти что угодно. Никто не гарантирует что если активити при старте операции была, то останется и при ее окончании.

В вашем случае нужно писать null-безопасный код типа:

(activity as? MainActivity)?.forceShowAd(callback = { 
    Log.i(TAG, "load ads");
    (activity as? MainActivity)?.toMain()
})
→ Ссылка