lateinit 属性 尚未初始化 - Android 生命周期问题
lateinit property has not been initialized - Android Lifecycle issue
我偶尔会收到应用程序崩溃报告,根据我对 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 方法之前执行,因此 mApi接口需要初始化吗?
编辑 #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)
配置更改后,片段会在您调用 activity super.onCreate(savedInstanceState)
时恢复。那时你应该为你的片段初始化准备好 activity 。一种方法是将 super.onCreate()
调用推迟到 onCreate()
.
中的稍后时间点
在发生崩溃的 lateinit 属性 return 之前,您可以简单地检查 属性 是否已初始化,如下所示:
this::propertyName.isInitialized
如果 属性 尚未初始化,您可以简单地 return 其他东西或 null 来避免崩溃。
感谢@laalto
找到了答案
我将 Activity onCreate 更改为:
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
)
...
}
至此
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()
基本上,我只在初始化完成后绑定视图...
我偶尔会收到应用程序崩溃报告,根据我对 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 方法之前执行,因此 mApi接口需要初始化吗?
编辑 #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)
配置更改后,片段会在您调用 activity super.onCreate(savedInstanceState)
时恢复。那时你应该为你的片段初始化准备好 activity 。一种方法是将 super.onCreate()
调用推迟到 onCreate()
.
在发生崩溃的 lateinit 属性 return 之前,您可以简单地检查 属性 是否已初始化,如下所示:
this::propertyName.isInitialized
如果 属性 尚未初始化,您可以简单地 return 其他东西或 null 来避免崩溃。
感谢@laalto
找到了答案我将 Activity onCreate 更改为:
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
)
...
}
至此
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()
基本上,我只在初始化完成后绑定视图...