androidbroadcast / ViewBindingPropertyDelegate

Make work with Android View Binding simpler
https://proandroiddev.com/make-android-view-binding-great-with-kotlin-b71dd9c87719
Apache License 2.0
1.42k stars 102 forks source link

Crash on language change #107

Open ro-mak opened 1 year ago

ro-mak commented 1 year ago

To reproduce

  1. Open a fragment that is clearing some stuff via viewbinding in onDestroyView
  2. Minimize app
  3. Change system locale
  4. Reopen app
  5. Crash!

Is it an intended behaviour?

If it is i think it should be mentioned in Readme I saw that there is a special callback onViewDestroyed, maybe that's the right place to clear some stuff?

kirich1409 commented 1 year ago

Hi, @ro-mak. Can you share stack trace of the crash?

ro-mak commented 1 year ago

Hi, @kirich1409, here it is, and below I added the project

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.fragmentviewbindingtest, PID: 18170
    java.lang.RuntimeException: Unable to destroy activity {com.example.fragmentviewbindingtest/com.example.fragmentviewbindingtest.MainActivity}: java.lang.IllegalStateException: Access to viewBinding after Lifecycle is destroyed or hasn't created yet. The instance of viewBinding will be not cached.
        at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5387)
        at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5420)
        at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5714)
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:5630)
        at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:71)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7839)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
     Caused by: java.lang.IllegalStateException: Access to viewBinding after Lifecycle is destroyed or hasn't created yet. The instance of viewBinding will be not cached.
        at by.kirich1409.viewbindingdelegate.LifecycleViewBindingProperty.runStrictModeChecks(ViewBindingProperty.kt:112)
        at by.kirich1409.viewbindingdelegate.LifecycleViewBindingProperty.getValue(ViewBindingProperty.kt:91)
        at by.kirich1409.viewbindingdelegate.FragmentViewBindingProperty.getValue(FragmentViewBindings.kt:63)
        at by.kirich1409.viewbindingdelegate.FragmentViewBindingProperty.getValue(FragmentViewBindings.kt:52)
        at com.example.fragmentviewbindingtest.ui.main.MainFragment.getViewBinding(MainFragment.kt:21)
        at com.example.fragmentviewbindingtest.ui.main.MainFragment.onDestroyView(MainFragment.kt:37)
        at androidx.fragment.app.Fragment.performDestroyView(Fragment.java:3232)
        at androidx.fragment.app.FragmentStateManager.destroyFragmentView(FragmentStateManager.java:744)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:327)
        at androidx.fragment.app.SpecialEffectsController$FragmentStateManagerOperation.complete(SpecialEffectsController.java:771)
        at androidx.fragment.app.SpecialEffectsController$Operation.cancel(SpecialEffectsController.java:615)
        at androidx.fragment.app.SpecialEffectsController.forceCompleteAllOperations(SpecialEffectsController.java:350)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2844)
        at androidx.fragment.app.FragmentManager.dispatchDestroy(FragmentManager.java:2820)
        at androidx.fragment.app.FragmentController.dispatchDestroy(FragmentController.java:345)
        at androidx.fragment.app.FragmentActivity.onDestroy(FragmentActivity.java:306)
        at androidx.appcompat.app.AppCompatActivity.onDestroy(AppCompatActivity.java:280)
        at android.app.Activity.performDestroy(Activity.java:8316)
        at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1364)
        at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5374)
        at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5420) 
        at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5714) 
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:5630) 
        at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:71) 
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7839) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 
ro-mak commented 1 year ago

here is the test project https://github.com/ro-mak/FragmentViewBindingTest

kirich1409 commented 1 year ago

Cleaning reference to view binding and detection of current lifecycle state is based on Jetpack Lifecycle library. Need to check it's behaviour during config changes

kirich1409 commented 1 year ago

As temporary solution yon can use


class MainFragment : Fragment() {

    companion object {
        fun newInstance() = MainFragment()
    }

    private lateinit var viewModel: MainViewModel

    private val viewBinding: FragmentMainBinding by viewBinding(FragmentMainBinding::bind, onViewDestroyed = {viewBinding - > viewBinding.main.minHeight = 100}
)

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        // TODO: Use the ViewModel
    }

}
kirich1409 commented 1 year ago

I've tried to reproduce bug in 1.5.8. I checked on Android 13 emulator. No crash

Reginer commented 1 year ago

ViewBindingPropertyDelegate.strictMode = false