Android使用ViewModel初始化Adapter抛出NullPointerException问题求助
问题根源排查
1. 响应校验逻辑被注释失效
你在BaseDataSource中注释了响应成功校验、数据非空校验的逻辑,无论接口返回是否成功、数据是否为空,都会直接返回SUCCESS状态的Resource,这是你拿到SUCCESS状态但data为null的核心原因。
2. 反序列化字段不匹配
你当前的代码从ApiResponse.data取数据,需要确认两点:
- 你定义的
ApiResponse类的列表字段名是否和接口实际返回的字段完全一致,如果你用的是newsapi公开接口,返回的新闻列表字段是articles而非data,会导致Gson反序列化后该字段为null - 你定义的
JapaneseResponse类的字段名是否和接口返回的单条新闻字段完全对应,比如接口返回的标题字段是小写的title,如果你写的是大写开头的Title且没有加@SerializedName注解,反序列化后也会为空
3. 空安全操作不规范
japaneseAdapter = it.data?.let { it1 -> JapaneseAdapter(it1) }!!这段代码中,如果it.data为null,let逻辑不会执行,整个表达式返回null,末尾的!!强制拆箱就会直接抛出空指针异常。
4. 缺少必要配置
你的代码中没有给RecyclerView设置LayoutManager,这也会导致列表无法正常渲染。
修复方案
步骤1:修复BaseDataSource逻辑
放开注释的校验逻辑,正确处理不同响应状态:
abstract class BaseDataSource { protected suspend fun <T> getResult(call: suspend () -> Response<ApiResponse<T>>): Resource<T> { try { val response = call() if(response.isSuccessful) { // 注意:如果ApiResponse的列表字段名是articles,这里要改成response.body()?.articles val body = response.body()?.data if(body != null) return Resource.success(body) return Resource.error("响应数据为空") } return Resource.error("${response.code()}: ${response.message()}") } catch (e: Exception) { return Resource.error(e.message ?: "网络请求出错") } } }
步骤2:校正数据类字段
确保ApiResponse和JapaneseResponse的字段名和接口返回完全匹配,示例:
// 适配newsapi返回结构的ApiResponse示例 data class ApiResponse<T>( val status: String, val totalResults: Int, val articles: T? // 和接口返回的列表字段名一致 ) // 适配newsapi返回结构的JapaneseResponse示例 data class JapaneseResponse( val title: String, // 小写和接口对应,若要大写可以加@SerializedName("title") val description: String?, val urlToImage: String? )
步骤3:优化Activity初始化与空处理
提前初始化Adapter,避免重复创建,规范空判断:
@AndroidEntryPoint class JapaneseActivity : AppCompatActivity() { private lateinit var binding: ActivityJapaneseBinding private val japaneseViewModel by viewModels<JapaneseViewModel>() // 提前初始化Adapter,传入空列表 private val japaneseAdapter = JapaneseAdapter(emptyList()) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityJapaneseBinding.inflate(layoutInflater) setContentView(binding.root) // 给RecyclerView设置必要的LayoutManager和Adapter binding.rvNews.layoutManager = LinearLayoutManager(this) binding.rvNews.adapter = japaneseAdapter japaneseViewModel.japaneseResponse.observe(this, { when(it.status){ Resource.Status.LOADING -> { // 可自定义加载逻辑 } Resource.Status.SUCCESS -> { it.data?.let { data -> // 更新Adapter数据 japaneseAdapter.japaneseResponse = data japaneseAdapter.notifyDataSetChanged() } ?: run { Toast.makeText(this, "暂无新闻数据", Toast.LENGTH_SHORT).show() } } Resource.Status.ERROR -> { Log.d("ERROR", it.message ?: "未知错误") Toast.makeText(this, it.message ?: "加载失败", Toast.LENGTH_SHORT).show() } } }) } }
步骤4:调整Adapter支持数据更新
把Adapter的列表属性改为可修改:
class JapaneseAdapter(var japaneseResponse: List<JapaneseResponse>) : RecyclerView.Adapter<JapaneseAdapter.ViewHolder>() { // 其余原有逻辑保持不变 }
内容的提问来源于stack exchange,提问作者dazai
相关产品推荐
相关产品推荐

