使用Retrofit2+RxJava2解析Gson时lateinit未初始化致RecyclerView数据异常
嘿,我来帮你搞定这个问题!首先,导致你遇到UninitializedPropertyAccessException(提示lateinit property data has not been initialized)的直接原因非常明确,咱们先从这个核心错误入手:
核心错误:传错了参数到displayData方法
在fetchData方法的订阅回调里,你写的是:
.subscribe ({ companyList-> displayData(data) },{ ... })
这里的data是你在Activity里声明的lateinit var data : List<Company>,但这个变量自始至终都没有被赋值过!你应该把回调返回的companyList传给displayData才对,也就是改成:
.subscribe ({ companyList-> displayData(companyList) },{ ... })
而且这个data变量其实完全是多余的,直接删掉它就好,避免后续混淆。
其他需要修复的问题
除了核心错误,你的代码里还有几个坑会导致RecyclerView无法正常显示数据甚至崩溃:
1. CompanyAdapter里遗留的TODO注释
Adapter的onCreateViewHolder、getItemCount、onBindViewHolder方法里都有TODO("not implemented"),这行代码会直接抛出NotImplementedError,必须删掉,保留实际的业务代码。比如onCreateViewHolder应该改成:
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): CompanyViewHolder { val itemView = LayoutInflater.from(p0.context).inflate(R.layout.list_view_item,p0,false) return CompanyViewHolder(itemView) }
2. getItemCount里的冗余非空断言
你的Adapter构造函数里companyList是不可空的List<Company>,所以getItemCount里直接返回companyList.size就行,不用companyList?.size!!这种多余的写法:
override fun getItemCount(): Int { return companyList.size }
3. RecyclerView LayoutManager的不必要强转
在showCompanyPopupView里,设置LayoutManager的时候不需要强转,直接写:
rvCompany.layoutManager = LinearLayoutManager(this)
LinearLayoutManager本身就是RecyclerView.LayoutManager的子类,强转完全没必要。
4. 避免内存泄漏:清理CompositeDisposable
记得在Activity的onDestroy方法里清理CompositeDisposable,防止RxJava订阅导致的内存泄漏:
override fun onDestroy() { super.onDestroy() compositeDisposable.dispose() }
5. 修复Activity类名冲突
你的Activity类名居然叫Company,这和你的数据类重名了,会导致混淆,建议改成CompanyActivity:
class CompanyActivity : AppCompatActivity() { ... }
6. 处理Retrofit为空的情况
在fetchData里,如果APIClient.apIClient返回null,api这个lateinit变量会未初始化,建议加个判断:
private fun fetchData(){ val retrofit = APIClient.apIClient ?: run { Toast.makeText(applicationContext, "初始化Retrofit失败", Toast.LENGTH_SHORT).show() return } api = retrofit.create(APIInterface::class.java) // 后续订阅代码... }
修正后的关键代码片段
CompanyActivity.kt(原MainActivity.kt改名后)
class CompanyActivity : AppCompatActivity() { internal lateinit var api : APIInterface val compositeDisposable = CompositeDisposable() internal lateinit var companyDialog : Dialog internal lateinit var adapter: CompanyAdapter internal lateinit var rvCompany : RecyclerView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_company) companyDialog = Dialog(this) btnSelectCompany.setOnClickListener{ showCompanyPopupView() } } fun showCompanyPopupView(){ companyDialog.setContentView(R.layout.compny_popup_screen) rvCompany = companyDialog.findViewById(R.id.rvCompany) rvCompany.setHasFixedSize(true) rvCompany.layoutManager = LinearLayoutManager(this) fetchData() companyDialog.window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) companyDialog.show() } private fun fetchData(){ val retrofit = APIClient.apIClient ?: run { Toast.makeText(applicationContext, "Retrofit初始化失败", Toast.LENGTH_SHORT).show() return } api = retrofit.create(APIInterface::class.java) compositeDisposable.add(api.getCompanyData() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe ({ companyList-> displayData(companyList) },{ Toast.makeText(applicationContext, it.message, Toast.LENGTH_SHORT).show() }) ) } private fun displayData(companyList: List<Company>) { adapter = CompanyAdapter(this,companyList) rvCompany.adapter = adapter } override fun onDestroy() { super.onDestroy() compositeDisposable.dispose() } }
CompanyAdapter.kt
class CompanyAdapter(internal var context: Context, internal var companyList: List<Company>) :RecyclerView.Adapter<CompanyAdapter.CompanyViewHolder>() { override fun onCreateViewHolder(p0: ViewGroup, p1: Int): CompanyViewHolder { val itemView = LayoutInflater.from(p0.context).inflate(R.layout.list_view_item,p0,false) return CompanyViewHolder(itemView) } override fun getItemCount(): Int { return companyList.size } override fun onBindViewHolder(p0: CompanyViewHolder, p1: Int) { p0.bindModel(companyList[p1]) } inner class CompanyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){ val radioButton : RadioButton = itemView.findViewById(R.id.rbCompanyName) fun bindModel(company: Company){ radioButton.text = company.Cmp_Name } } }
把这些修改都做完,你的代码应该就能正常请求数据并显示在RecyclerView里了。
内容的提问来源于stack exchange,提问作者Kanan

