I get an occasional app crash report which shouldn't occur based on my understanding of the Android lifecycle. Obviously I'm wrong somehow.
I have an Activity which initializes an API interface in the onCreate and provides a method called getApi()
class MainActivity : AppCompatActivity(), IApiProvider...{
...
private lateinit var mApiInterface: ApiInterface
override fun onCreate(savedInstanceState: Bundle?) {
...
mApiInterface = ApiInterface.invoke(
ConnectivityInterceptor(applicationContext, this),
AuthorizationInterceptor(this),
prefs,
authenticator
)
...
}
override fun getApi(): ApiInterface {
return mApiInterface // THIS IS WHERE THE CRASH OCCURS
}
}
I also have a Fragment which attempts to grab this Api interface from the activity in the following fashion
class MyFragment()...{
private lateinit var mApiProvider: IApiProvider
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is IApiProvider) {
mApiProvider = context
} else {
throw RuntimeException("$context must implement IApiProvider")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
if (activity != null) {
mViewModel = ViewModelProvider(
this,
MyViewModelFactory(requireActivity().application, mApiProvider.getApi()) // This call causes the crash in the Activity
)
.get(LapTimingViewModel::class.java)
}
}
So even if the Activity gets recycled due to some Android memory management, shouldn't the Activity onCreate method be executed before the Fragment onCreate method and therefore the mApiInterface should be initialized?
Edit #1: Adding stacktrace
2022-03-24 12:45:32.600 23305-23305/hidden.package.name.debug E/AndroidRuntime: FATAL EXCEPTION: main
Process: hidden.package.name.debug, PID: 23305
java.lang.RuntimeException: Unable to start activity ComponentInfo{hidden.package.name.debug/hidden.package.name.MainActivity}: android.view.InflateException: Binary XML file line #11 in hidden.package.name.debug:layout/activity_main: Binary XML file line #47 in hidden.package.name.debug:layout/app_bar_main: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3527)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3702)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
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:2151)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:250)
at android.app.ActivityThread.main(ActivityThread.java:7844)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
Caused by: android.view.InflateException: Binary XML file line #11 in hidden.package.name.debug:layout/activity_main: Binary XML file line #47 in hidden.package.name.debug:layout/app_bar_main: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #47 in hidden.package.name.debug:layout/app_bar_main: Error inflating class fragment
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mApiInterface has not been initialized
at hidden.package.name.MainActivity.getApi(MainActivity.kt:536)
at hidden.package.name.ui.laptiming.LapTimingFragment.onCreate(LapTimingFragment.kt:112)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2684)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:280)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1175)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2625)
at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:2571)
at androidx.fragment.app.Fragment.restoreChildFragmentState(Fragment.java:1707)
at androidx.fragment.app.Fragment.onCreate(Fragment.java:1683)
at hidden.package.name.ui.home.BottomNavigationFragment.onCreate(BottomNavigationFragment.kt:62)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2684)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:280)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1175)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2625)
at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:2571)
at androidx.fragment.app.Fragment.restoreChildFragmentState(Fragment.java:1707)
at androidx.fragment.app.Fragment.onCreate(Fragment.java:1683)
at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:264)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2684)
at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:280)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1175)
2022-03-24 12:45:32.601 23305-23305/hidden.package.name.debug E/AndroidRuntime: at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:135)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:356)
at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:335)
at android.view.LayoutInflater.tryCreateView(LayoutInflater.java:1067)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:995)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:959)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1121)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1124)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.parseInclude(LayoutInflater.java:1261)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1117)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.inflate(LayoutInflater.java:680)
at android.view.LayoutInflater.inflate(LayoutInflater.java:532)
at hidden.package.name.databinding.ActivityMainBinding.inflate(ActivityMainBinding.java:52)
at hidden.package.name.databinding.ActivityMainBinding.inflate(ActivityMainBinding.java:46)
at hidden.package.name.MainActivity.onCreate(MainActivity.kt:83)
at android.app.Activity.performCreate(Activity.java:8114)
at android.app.Activity.performCreate(Activity.java:8098)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3500)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3702)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
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:2151)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:250)
at android.app.ActivityThread.main(ActivityThread.java:7844)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
CodePudding user response:
After a config change, fragments get restored when you call activity super.onCreate(savedInstanceState)
. You should have the activity ready for your fragments init at that point. One way to do that is to postpone the super.onCreate()
call to a later point in your onCreate()
.
CodePudding user response:
Before returning the lateinit property where a crash occurs, you can simply check if that property is initialized or not like this:
this::propertyName.isInitialized
If the property has not been initialized, you can simply return something else or null which will avoid the crash.
CodePudding user response:
Found an answer thanks to @laalto
I changed my Activity onCreate from this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityMainBinding.inflate(layoutInflater)
val view = mBinding.root
setContentView(view)
val toolbar: Toolbar = findViewById(R.id.toolbar)
toolbar.visibility = View.GONE
setSupportActionBar(toolbar)
setupDrawerNav()
...
mApiInterface = ApiInterface.invoke(
ConnectivityInterceptor(applicationContext, this),
AuthorizationInterceptor(this),
prefs,
authenticator
)
...
}
to this
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
mApiInterface = ApiInterface.invoke(
ConnectivityInterceptor(applicationContext, this),
AuthorizationInterceptor(this),
prefs,
authenticator
)
mBinding = ActivityMainBinding.inflate(layoutInflater)
val view = mBinding.root
setContentView(view)
val toolbar: Toolbar = findViewById(R.id.toolbar)
toolbar.visibility = View.GONE
setSupportActionBar(toolbar)
setupDrawerNav()
Basically, I only bind the view after the init is done...