You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 12:36:00