添加NavHostFragment后笔记应用启动失败,报UninitializedPropertyAccessException
解决lateinit属性binding未初始化的异常问题
问题概述
跟随Plural Sight课程开发笔记管理应用,包含两个Activity。编译成功但应用无法启动,添加NavHostFragment后仍抛出kotlin.UninitializedPropertyAccessException,提示lateinit属性binding未初始化。
Logcat错误信息
Launching 'app' on Pixel 6 API 33. Install successfully finished in 6 s 713 ms. $ adb shell am start -n "com.example.nokeeper4/com.example.nokeeper4.NoteListActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER Connected to process 3595 on device 'Pixel_6_API_33 [emulator-5554]'. ... E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.nokeeper4, PID: 3595 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.nokeeper4/com.example.nokeeper4.NoteListActivity}: kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized ...
相关代码
MainActivity XML代码
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/Theme.Nokeeper4.AppBarOverlay"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/Theme.Nokeeper4.PopupOverlay" /> </com.google.android.material.appbar.AppBarLayout> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_frag" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" app:navGraph="@navigation/nav_graph2" tools:layout="@layout/activity_main"/> <include android:id="@+id/contentMain" layout="@layout/content_main" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
NoteListActivity XML代码
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".NoteListActivity"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appbar" android:layout_height="100dp" android:layout_width="match_parent"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_height="?attr/actionBarSize" android:layout_width="match_parent" /> </com.google.android.material.appbar.AppBarLayout> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="16dp" android:contentDescription="@string/new_note" android:src="@drawable/baseline_add_white_24" app:backgroundTint="#9C27B0" /> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_frag" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" app:navGraph="@navigation/nav_graph2" tools:layout="@layout/activity_note_list"/> <include android:id="@+id/contentNoteList" layout="@layout/content_note_list" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
MainActivity.kt代码
class MainActivity : AppCompatActivity() { private var notePosition = POSITION_NOT_SET private lateinit var spinnerCourses: Spinner private lateinit var textNoteTitle: EditText private lateinit var textNoteText: EditText private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) binding = ActivityMainBinding.inflate(layoutInflater) setSupportActionBar(binding.toolbar) spinnerCourses = findViewById(R.id.spinnerCourses) textNoteTitle = findViewById(R.id.textNoteTitle) textNoteText = findViewById(R.id.textNoteText) val adapterCourses = ArrayAdapter<CourseInfo>(this, android.R.layout.simple_spinner_item, DataManager.courses.values.toList()) adapterCourses.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) spinnerCourses.adapter = adapterCourses notePosition = savedInstanceState?.getInt(NOTE_POSITION, POSITION_NOT_SET) ?: intent.getIntExtra(NOTE_POSITION,POSITION_NOT_SET) if (notePosition != POSITION_NOT_SET) displayNote() else{ DataManager.notes.add(NoteInfo()) notePosition = DataManager.notes.lastIndex } } ... }
NoteListActivity.kt代码
class NoteListActivity : AppCompatActivity() { private lateinit var binding: ActivityNoteListBinding private lateinit var listNotes: ListView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) binding = ActivityNoteListBinding.inflate(layoutInflater) listNotes = findViewById(R.id.listNotes) binding.fab.setOnClickListener { val activityIntent = Intent(this, MainActivity::class.java) startActivity(activityIntent) } listNotes.adapter = ArrayAdapter( this, android.R.layout.simple_list_item_1, DataManager.notes) listNotes.setOnItemClickListener { _, _, position, _ -> val activityIntent = Intent(this, MainActivity::class.java) activityIntent.putExtra(NOTE_POSITION, position) startActivity(activityIntent) } } ... }
解决方案
核心问题
异常的原因是初始化binding的顺序错误:在调用setContentView(binding.root)时,binding变量还未被赋值(lateinit属性必须先初始化才能使用),直接访问binding.root会触发未初始化异常。
修正代码
修改MainActivity的onCreate方法
将binding = ActivityMainBinding.inflate(layoutInflater)移到setContentView(binding.root)之前,同时优化视图获取方式(直接通过binding获取,无需再调用findViewById):
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) setSupportActionBar(binding.toolbar) // 直接通过binding获取视图,替代findViewById spinnerCourses = binding.spinnerCourses textNoteTitle = binding.textNoteTitle textNoteText = binding.textNoteText val adapterCourses = ArrayAdapter<CourseInfo>(this, android.R.layout.simple_spinner_item, DataManager.courses.values.toList()) adapterCourses.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) spinnerCourses.adapter = adapterCourses notePosition = savedInstanceState?.getInt(NOTE_POSITION, POSITION_NOT_SET) ?: intent.getIntExtra(NOTE_POSITION,POSITION_NOT_SET) if (notePosition != POSITION_NOT_SET) displayNote() else{ DataManager.notes.add(NoteInfo()) notePosition = DataManager.notes.lastIndex } }
修改NoteListActivity的onCreate方法
同样调整binding初始化顺序,并通过binding获取ListView:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityNoteListBinding.inflate(layoutInflater) setContentView(binding.root) // 直接通过binding获取ListView listNotes = binding.listNotes binding.fab.setOnClickListener { val activityIntent = Intent(this, MainActivity::class.java) startActivity(activityIntent) } listNotes.adapter = ArrayAdapter( this, android.R.layout.simple_list_item_1, DataManager.notes) listNotes.setOnItemClickListener { _, _, position, _ -> val activityIntent = Intent(this, MainActivity::class.java) activityIntent.putExtra(NOTE_POSITION, position) startActivity(activityIntent) } }
额外说明
使用View Binding时,正确的流程是:
- 通过
BindingClass.inflate(layoutInflater)初始化binding对象 - 调用
setContentView(binding.root)设置布局 - 通过binding对象直接访问布局中的视图,无需使用findViewById
内容的提问来源于stack exchange,提问作者atiku
相关产品推荐
相关产品推荐

