Search code examples
androidkotlinandroid-fragmentsnullpointerexceptionprogressdialog

NullPointerException: createProgressDialog


Get next error from Crashlytics for Android version: 10:

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference
       at android.app.Dialog.<init>(Dialog.java:196)
       at android.app.ColorBaseAlertDialog.<init>(ColorBaseAlertDialog.java:33)
       at android.app.AlertDialog.<init>(AlertDialog.java:208)
       at android.app.AlertDialog.<init>(AlertDialog.java:204)
       at android.app.ProgressDialog.<init>(ProgressDialog.java:112)
       at com.sau.authenticator.widget.fragment.BaseFragment.createProgressDialog(BaseFragment.java:46)
       at com.sau.authenticator.widget.fragment.BaseFragment.showLoadProgress(BaseFragment.java:41)
       at com.sau.authenticator.widget.fragment.WebViewFragment$webViewClient$1.onPageStarted(WebViewFragment.java:69)
       at i6.c(i6.java:2)
       at Hn.handleMessage(Hn.java:145)
       at android.os.Handler.dispatchMessage(Handler.java:107)
       at android.os.Looper.loop(Looper.java:228)
       at android.app.ActivityThread.main(ActivityThread.java:7782)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:981)

In my function createProgressDialog, which is called in BaseFragment:

abstract class BaseFragment : Fragment() {
    private var progressDialog: ProgressDialog? = null
    val activityComponents: ActivityComponentsContract?
        get() = activity?.activityComponentsContract

    override fun onDestroy() {
        progressDialog?.dismiss()
        progressDialog = null
        super.onDestroy()
    }

    protected fun showLoadProgress() {
        if (progressDialog == null) progressDialog = createProgressDialog()
        progressDialog?.show()
    }

    private fun createProgressDialog(): ProgressDialog? {
        val dialog = ProgressDialog(activity, R.style.ProgressDialogTheme)
        dialog.setCancelable(false)
        dialog.setProgressStyle(android.R.style.Widget_ProgressBar_Large)
        return dialog
    }

    protected fun dismissLoadProgress() {
        progressDialog?.dismiss()
    }
}

Here is code of my WebViewFragment:

class WebViewFragment : BaseFragment() {

    private var url = ""
    private var title = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            url = it.getString(KEY_URL, "")
            title = it.getString(KEY_TITLE, "")
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        activityComponents?.updateAppbar(
            title = title,
            backActionImageResId = R.drawable.ic_appbar_action_back
        )
        return inflater.inflate(R.layout.fragment_web_view, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        customWebView?.webViewClient = webViewClient
        customWebView?.loadUrl(url)
    }

    private val webViewClient = object : WebViewClient() {

        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            super.onPageStarted(view, url, favicon)
            showLoadProgress()
        }

        override fun onPageFinished(view: WebView?, url: String?) {
            super.onPageFinished(view, url)
            dismissLoadProgress()
        }
    }

    companion object {
        const val KEY_URL = "KEY_URL"

        fun newBundle(url: String = "", title: String): Bundle {
            return Bundle().apply {
                putString(KEY_URL, url)
                putString(KEY_TITLE, title)
            }
        }
    }
}

Solution

  • I'm not too familiar with WebView, but it seems like by the time onPageStarted() is called your user already navigated away (or rotated his device). Hence your Fragment's onDestroyView() is likely called already, but you are not notifying your WebView that starts loading your url. That's why activity is null and you get the exception.

    So on your WebViewFragment's lifecycle callbacks make sure you notify your WebView too:

    override fun onResume() {
        super.onResume()
        customWebView?.onResume()
    }
    
    override fun onPause() {
        super.onPause()
        customWebView?.onPause()
    }
    
    override fun onDestroyView() {
        super.onDestroyView()
        customWebView?.stopLoading() // this should prevent onPageStarted() from being called
    }