Android Kotlin:onResponse初始化enrollBookAdapter仍报错的解决方法
问题
运行时出现如下报错:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.clone.practice/com.clone.practice.EnrollBookActivity}: kotlin.UninitializedPropertyAccessException: lateinit property enrollBookAdapter has not been initialized
原本预期在网络请求onResponse回调中执行response.body()?.let {}时,enrollBookAdapter会完成初始化,但实际未成功。请问哪里操作有误?该如何使用Books数据正确初始化bookList?
相关代码:
class EnrollBookActivity : AppCompatActivity() { private lateinit var binding: ActivityEnrollBookBinding private lateinit var bookService: BookService private lateinit var bookList:MutableList<Book> private lateinit var enrollBookAdapter: EnrollBookAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityEnrollBookBinding.inflate(layoutInflater) setContentView(binding.root) initBookService() bookServiceLoadBestSellers() binding.selfBtn.setOnClickListener{ startActivity(Intent(this,SelfWriteActivity::class.java)) } enrollBookAdapter.setOnBookClickListener(object:EnrollBookAdapter.OnBookClickListener{ override fun onBookClick(position: Int, book: Book) { val intent = Intent(this@EnrollBookActivity,TalerMain::class.java) intent.putExtra("title",enrollBookAdapter.bookList[position].title) intent.putExtra("url",enrollBookAdapter.bookList[position].title) startActivity(intent) } }) } private fun initBookRecyclerView(bookList: MutableList<Book>) { enrollBookAdapter = EnrollBookAdapter(bookList) enrollBookAdapter.setOnBookClickListener(object:EnrollBookAdapter.OnBookClickListener{ override fun onBookClick(position: Int, book: Book) { val intent = Intent(this@EnrollBookActivity,TalerMain::class.java) intent.putExtra("title",enrollBookAdapter.bookList[position].title) intent.putExtra("url",enrollBookAdapter.bookList[position].title) startActivity(intent) } }) binding.bookRv.layoutManager = GridLayoutManager(this,2) binding.bookRv.adapter = EnrollBookAdapter(bookList) binding.bookRv.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) } private fun bookServiceLoadBestSellers() { bookService.getBestSellerBooks(getString(R.string.interparkAPIKey)) .enqueue(object : Callback<BestSellerDto> { override fun onResponse( call: Call<BestSellerDto>, response: Response<BestSellerDto> ) { if (response.isSuccessful.not()) { Log.e(M_TAG, "NOT!! SUCCESS") return } response.body()?.let { Log.d(M_TAG, it.toString()) it.books.forEach { book -> Log.d(M_TAG, book.toString()) } //enrollBookAdapter = EnrollBookAdapter(it.books as MutableList<Book>) initBookRecyclerView(it.books as MutableList<Book>) //setRecyclerView(it.books) } } override fun onFailure(call: Call<BestSellerDto>, t: Throwable) { Log.e(M_TAG, t.toString()) } }) } }
问题分析
报错核心原因:onCreate中直接调用enrollBookAdapter.setOnBookClickListener时,enrollBookAdapter还未初始化。
网络请求是异步操作,bookServiceLoadBestSellers()调用后不会立刻执行onResponse回调,而onCreate会继续向下执行,此时enrollBookAdapter作为lateinit变量仍未被赋值,直接调用其方法必然抛出初始化异常。
此外还有两处额外问题:
initBookRecyclerView中,先初始化了全局的enrollBookAdapter,但给RecyclerView设置的却是新创建的EnrollBookAdapter(bookList),导致全局变量和RecyclerView绑定的适配器不是同一个对象。- 全局
bookList变量从未被赋值,一直处于未初始化状态。
解决方案
1. 调整适配器初始化与监听设置时机
把设置点击监听的逻辑移到initBookRecyclerView方法中,确保在适配器初始化完成后再绑定监听。
2. 统一RecyclerView的适配器实例
在initBookRecyclerView中,给RecyclerView设置适配器时直接使用已初始化的enrollBookAdapter,避免创建重复实例。
3. 正确初始化全局bookList
在onResponse回调中通过initBookRecyclerView给全局bookList赋值。
修改后的完整代码:
class EnrollBookActivity : AppCompatActivity() { private lateinit var binding: ActivityEnrollBookBinding private lateinit var bookService: BookService private lateinit var bookList: MutableList<Book> private lateinit var enrollBookAdapter: EnrollBookAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityEnrollBookBinding.inflate(layoutInflater) setContentView(binding.root) initBookService() bookServiceLoadBestSellers() binding.selfBtn.setOnClickListener{ startActivity(Intent(this,SelfWriteActivity::class.java)) } // 移除此处未初始化的适配器监听设置 } private fun initBookRecyclerView(books: MutableList<Book>) { // 初始化全局bookList bookList = books // 初始化适配器 enrollBookAdapter = EnrollBookAdapter(bookList) // 设置点击监听 enrollBookAdapter.setOnBookClickListener(object: EnrollBookAdapter.OnBookClickListener{ override fun onBookClick(position: Int, book: Book) { val intent = Intent(this@EnrollBookActivity, TalerMain::class.java) intent.putExtra("title", book.title) intent.putExtra("url", book.url) // 修正原代码中url传title的错误 startActivity(intent) } }) // 设置RecyclerView参数 binding.bookRv.layoutManager = GridLayoutManager(this, 2) binding.bookRv.adapter = enrollBookAdapter // 使用已初始化的适配器实例 binding.bookRv.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) } private fun bookServiceLoadBestSellers() { bookService.getBestSellerBooks(getString(R.string.interparkAPIKey)) .enqueue(object : Callback<BestSellerDto> { override fun onResponse( call: Call<BestSellerDto>, response: Response<BestSellerDto> ) { if (!response.isSuccessful) { Log.e(M_TAG, "请求失败") return } response.body()?.let { dto -> Log.d(M_TAG, dto.toString()) dto.books.forEach { book -> Log.d(M_TAG, book.toString()) } // 安全转换为MutableList并初始化RecyclerView initBookRecyclerView(dto.books.toMutableList()) } } override fun onFailure(call: Call<BestSellerDto>, t: Throwable) { Log.e(M_TAG, t.toString()) } }) } }
额外优化说明
- 修正原代码中点击监听里把
book.title作为url传递的错误。 - 用
toMutableList()替代强制类型转换as MutableList,避免不可变List转换时的异常。 - 点击监听中直接使用回调参数的
book对象,无需从适配器列表中重复获取,代码更简洁安全。
内容的提问来源于stack exchange,提问作者Joon
相关产品推荐
相关产品推荐

