Lateinit 属性尚未初始化 - Android 生命周期问题
我偶尔会收到一份应用程序崩溃报告,根据我对 Android 生命周期的理解,该报告不应发生。显然我错了。
我有一个 Activity 在 onCreate 中初始化一个 API 接口并提供一个名为 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
}
}
我还有一个 Fragment 尝试以以下方式从 Activity 中获取此 Api 接口
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)
}
}
所以即使 Activity 由于某些 Android 被回收内存管理,Activity的onCreate方法不应该在Fragment的onCreate方法之前执行,因此mApiInterface应该被初始化吗?
编辑#1:添加堆栈跟踪
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)
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
配置更改后,当您调用 Activity
super.onCreate(savedInstanceState)
时,片段会恢复。此时您应该已为片段初始化准备好活动。一种方法是将super.onCreate()
调用推迟到onCreate()
中的稍后位置。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 thesuper.onCreate()
call to a later point in youronCreate()
.在返回发生崩溃的 Lateinit 属性之前,您可以简单地检查该属性是否已初始化,如下所示:
如果该属性尚未初始化,您可以简单地返回其他内容或 null,这将避免崩溃。
Before returning the lateinit property where a crash occurs, you can simply check if that property is initialized or not like this:
If the property has not been initialized, you can simply return something else or null which will avoid the crash.
感谢@laalto找到了答案
我将我的Activity onCreate从这个:
改为这个
基本上,我只在初始化完成后绑定视图......
Found an answer thanks to @laalto
I changed my Activity onCreate from this:
to this
Basically, I only bind the view after the init is done...
你必须将这 6 行添加到代码中,并且你的程序运行良好
you have to add this 6 lines to codes and your program runes well