leandroBorgesFerreira / LoadingButtonAndroid

A button to substitute the ProgressDialog
MIT License
1.94k stars 214 forks source link

UninitializedPropertyAccessException happens if View is GONE #134

Closed cdongieux closed 5 years ago

cdongieux commented 5 years ago

Hi,

A UninitializedPropertyAccessException is raised during LifecycleOwner destruction (e.g. Activity finishes) when the button visibility is View.GONE. An easy way to reproduce this issue is to set a CircularProgress*Button XML property android:visibility to gone, launch the Activity holding this button, and press the Back navigation button.

Stack trace:

2019-05-16 10:00:33.846 22258-22258/br.com.simplepass.loadingbutton E/AndroidRuntime: FATAL EXCEPTION: main
    Process: br.com.simplepass.loadingbutton, PID: 22258
    java.lang.RuntimeException: Unable to destroy activity {br.com.simplepass.loadingbutton/br.com.simplepass.loadingbuttonsample.MainActivity}: java.lang.RuntimeException: Failed to call observer method
        at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4458)
        at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4476)
        at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:39)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.RuntimeException: Failed to call observer method
        at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:225)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:193)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:184)
        at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:36)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:355)
        at androidx.lifecycle.LifecycleRegistry.backwardPass(LifecycleRegistry.java:309)
        at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:328)
        at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:138)
        at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:124)
        at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:122)
        at androidx.lifecycle.ReportFragment.onDestroy(ReportFragment.java:107)
        at android.app.Fragment.performDestroy(Fragment.java:2782)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1451)
        at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1576)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1637)
        at android.app.FragmentManagerImpl.dispatchMoveToState(FragmentManager.java:3046)
        at android.app.FragmentManagerImpl.dispatchDestroy(FragmentManager.java:3026)
        at android.app.FragmentController.dispatchDestroy(FragmentController.java:248)
        at android.app.Activity.performDestroy(Activity.java:7394)
        at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1306)
        at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4443)
        at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4476) 
        at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:39) 
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
     Caused by: kotlin.UninitializedPropertyAccessException: lateinit property initialState has not been initialized
        at br.com.simplepass.loadingbutton.customViews.CircularProgressButton.access$getInitialState$p(CircularProgressButton.kt:22)
        at br.com.simplepass.loadingbutton.customViews.CircularProgressButton$morphAnimator$2.invoke(CircularProgressButton.kt:72)
        at br.com.simplepass.loadingbutton.customViews.CircularProgressButton$morphAnimator$2.invoke(CircularProgressButton.kt:22)
        at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
        at br.com.simplepass.loadingbutton.customViews.CircularProgressButton.getMorphAnimator(Unknown Source:7)
        at br.com.simplepass.loadingbutton.customViews.CircularProgressButton.dispose(CircularProgressButton.kt:173)

In your implementation the initialState var is lateinit, and it is only initialized in saveInitialState() which might not be called. Actually it is only called in the presenter's onDraw() method. In the use case I described above the onDraw() method is never called because the View visibility state is View.GONE.

leandroBorgesFerreira commented 5 years ago

Oh my... I totally didn't consider onDraw not being called if the visibility is GONE. I'll take a look at this one.