koral--/android-gif-drawable

App crashes on nexus 6 api 24

gitAzim opened this issue · 3 comments

App is working fine mi A2 phone but facing crash on nexus 6 having api 24
GifDrawable.java – line 288

Fatal Exception: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8358)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1364)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5433)
at android.view.View.invalidateInternal(View.java:13997)
at android.view.View.invalidate(View.java:13961)
at android.view.View.invalidate(View.java:13945)
at android.widget.ImageView.invalidateDrawable(ImageView.java:262)
at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:844)
at pl.droidsonroids.gif.GifDrawable.invalidateSelf(GifDrawable.java:288)
at pl.droidsonroids.gif.InvalidationHandler.handleMessage(InvalidationHandler.java:29)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:7406)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

Dependency: implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.19'

Please attach a code reproducing this issue.

private var dialog: CustomProgressBar? = null

class CustomProgressBar(val activity: Activity?) {
var dialog: Dialog? =
    null //..we need the context else we can not create the dialog so get context in constructor

fun showDialog() {
    //start a new thread to process job
    Thread(Runnable {
        //heavy job here
        //send message to main thread
        Looper.prepare()
        val v = activity?.layoutInflater?.inflate(R.layout.custom_progress_dialog, null)

        dialog = activity?.let { Dialog(it, R.style.DialogStyle) }
        //...that's the layout i told you will inflate later
        dialog?.requestWindowFeature(Window.FEATURE_NO_TITLE)
        if (v != null) {
            dialog?.setContentView(v)
        }
        dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
        //...set cancelable false so that it's never get hidden
        dialog?.setCancelable(false)
        dialog?.setCanceledOnTouchOutside(false)

        //...initialize the imageView form infalted layout
        dialog?.show()
        Looper.loop()
    }).start()

}

//..also create a method which will hide the dialog when some work is done
fun hideDialog() {
    activity?.runOnUiThread {
        dialog?.dismiss()
    }

}
}

private fun getIntroImages() {
    if (NetworkUtil.getConnectivityStatus(this) != 0) {
        dialog?.showDialog() 
        disposable = Global.apiService.getLandingImages(
            WebServices.LandingPageWs + Global.getLanguage(this) + "&category_id=" + Global.getPreferenceCategory(
                this@SplashActivity
            )
        )
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                { result ->
                    dialog?.hideDialog()
                    //hideProgressDialog()
                    if (result != null) {
                        if (result.status == 200 && result.data != null) {
                            val intent =
                                Intent(this@SplashActivity, IntroSliderActivity::class.java)
                            intent.putExtra("introModel", result.data)
                            startActivity(intent)
                            finish()
                        }
                    }
                },
                { error ->
                    //println("ERROR - landing Ws :   " + error.localizedMessage)
                    dialog?.hideDialog()
                    //hideProgressDialog()
                    init()

                })

    }
}

XML

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <pl.droidsonroids.gif.GifImageView
        android:id="@+id/gif"
        android:layout_width="@dimen/_40sdp"
        android:layout_height="@dimen/_40sdp"
        android:src="@drawable/loading_new"
        android:visibility="visible" />

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="@dimen/_40sdp"
        android:layout_height="@dimen/_40sdp"
        android:visibility="gone" />

</LinearLayout>

The easiest solution coming into my mind is replacing code between Looper.prepare() and Looper.loop() with wrapping by Handler(Looper.getMainLooper()).post { //...

In your case:

fun showDialog() {
    //start a new thread to process job
    Thread(Runnable {
        //heavy job here
        //send message to main thread
       Handler(Looper.getMainLooper()).post {
        val v = activity?.layoutInflater?.inflate(R.layout.custom_progress_dialog, null)

        dialog = activity?.let { Dialog(it, R.style.DialogStyle) }
        //...that's the layout i told you will inflate later
        dialog?.requestWindowFeature(Window.FEATURE_NO_TITLE)
        if (v != null) {
            dialog?.setContentView(v)
        }
        dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
        //...set cancelable false so that it's never get hidden
        dialog?.setCancelable(false)
        dialog?.setCanceledOnTouchOutside(false)

        //...initialize the imageView form infalted layout
        dialog?.show()
        }
    }).start()

}