くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

非同期でのUIの取り扱い

非同期でUIの更新を行うときに気にしないといけないことが多いですよね 今ではRxjavaを使えば、あまり気にすることはないんですが、AsyncTaskLoaderを使う場合は、気にしないといけません。

私も一部分で、AsyncTaskLoaderを使っていて、罠にはまりました。

MainActivity

    // LoaderCallbacksの関数をオーバーライドして、処理を変更したコールバックオブジェクトを作成する
    private val getRssUrlCallback : LoaderManager.LoaderCallbacks<Rss> = object : LoaderManager.LoaderCallbacks<Rss> {

        override fun onLoadFinished(loader: Loader<Rss>?, data: Rss?) {
            if (data?.feedUrl.isNullOrBlank()) {
                var dialog = AlertDialog()
                dialog.title = "対応していません"
                dialog.onOkClickListener = DialogInterface.OnClickListener { dialog, which ->
                }
                dialog.show(supportFragmentManager, null)

            } 
        }
}

Activityでコールバック用のオブジェクトを作成しています。 自作のアラートを表示させる予定でしたが、下記エラーでアプリがクラッシュしました。

java.lang.IllegalStateException: Can not perform this action inside of onLoadFinished

どうやら、onLoadFinished()内でダイアログを表示してはいけないようです。

対処法は2つあります。

①Handlerを使う

MainActivity

    private val getRssUrlCallback : LoaderManager.LoaderCallbacks<Rss> = object : LoaderManager.LoaderCallbacks<Rss> {

        override fun onLoadFinished(loader: Loader<Rss>?, data: Rss?) {
            if (data?.feedUrl.isNullOrBlank()) {
                progressBar.visibility = android.widget.ProgressBar.INVISIBLE
                val handler = Handler()
                handler.post {
                    var dialog = AlertDialog()
                    dialog.title = "対応していません"
                    dialog.onOkClickListener = DialogInterface.OnClickListener { dialog, which ->
                    }
                    dialog.show(supportFragmentManager, null)
                }
            } 
        }
    }

Handler.post()でUIスレッドにRunnableに送るので、UIスレッドの更新が行えます。 Handlerの説明は下記サイトがわかりやすいかと思います。

LoaderManager.LoaderCallbacksインタフェースのAPIまとめ

この辺りはまだあまり詳しくないため、調べてみようと思います。

②AndroidAlertBuilderを使う

MainActivity

    private val getRssUrlCallback : LoaderManager.LoaderCallbacks<Rss> = object : LoaderManager.LoaderCallbacks<Rss> {

        override fun onLoadFinished(loader: Loader<Rss>?, data: Rss?) {
            if (data?.feedUrl.isNullOrBlank()) {
                alert("対応していません") {
                    yesButton {}
                }.show()
            } 
        }
    }

これだとFragmentを継承していないので、エラーは発生しません。