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()

基本上,我只在初始化完成后绑定视图...