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

基于Kotlin协程+MVVM构建食谱App遇协程异常求助

协程异常排查与修复(Kotlin+MVVM食谱App)

问题背景

基于recipesdb开发食谱App,采用Kotlin+协程+MVVM架构,运行时出现协程异常,报错定位到CategoryViewModel.kt:26行。

核心错误原因

  • Repository依赖未初始化:ViewModel中创建Repository时未传入getDataService、application等必要参数,导致getDataService为null,调用API时实际执行null?.getCategoryList(),后续对result!!的非空断言直接触发空指针异常。
  • 协程嵌套冗余:getMainCategory()中已在viewModelScope.launch(Dispatchers.IO)协程内,又嵌套withContext(Dispatchers.IO),属于无效操作。
  • 非空断言滥用:mainCategoryList!!和result!!的非空断言在对象为null时直接崩溃,未做安全处理。
  • Activity协程使用不当:用GlobalScope启动协程(不推荐在Android中使用),且suspend fun getCategories()无需挂起,ViewModel内部已处理协程逻辑。
  • Repository逻辑错误:return res_rep后的Log语句永远无法执行,属于无效代码。

修复后的代码

1. Repository代码修复

class Repository(
    private val getDataService: GetDataServices,
    private val dao: RecipeDao?,
    private val application: Application
) {
    // 获取主分类列表,添加异常捕获
    suspend fun getMainCategory(): Category? {
        return runCatching {
            getDataService.getCategoryList()
        }.onSuccess {
            Log.v("res_rep_get", it.toString())
        }.getOrNull()
    }
}

2. ViewModel代码修复

class CategoryViewModel(
    private val repository: Repository
) : ViewModel() {
    // 直接初始化LiveData,避免可空性问题
    val mainCategoryList = MutableLiveData<ArrayList<CategoryItems>?>()

    // 去掉suspend修饰符,内部用viewModelScope处理协程
    fun getMainCategory() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = repository.getMainCategory()
            // 切换到主线程更新LiveData
            withContext(Dispatchers.Main) {
                mainCategoryList.value = result?.categoriesitems as? ArrayList<CategoryItems>
            }
        }
    }
}

3. ViewModel工厂(用于依赖注入)

class CategoryViewModelFactory(
    private val repository: Repository
) : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(CategoryViewModel::class.java)) {
            return CategoryViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

4. Activity代码修复

@OptIn(DelicateCoroutinesApi::class)
class SplashActivity : BaseActivity(), EasyPermissions.RationaleCallbacks,
    EasyPermissions.PermissionCallbacks {

    private var READ_STORAGE_PERM = 123
    private lateinit var categoryViewModel: CategoryViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)

        // 初始化Retrofit服务实例(替换为你的API基础地址)
        val getDataService = Retrofit.Builder()
            .baseUrl("https://www.themealdb.com/api/json/v1/1/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(GetDataServices::class.java)
        
        // 初始化Repository和ViewModel
        val repository = Repository(getDataService, null, application)
        val viewModelFactory = CategoryViewModelFactory(repository)
        categoryViewModel = ViewModelProvider(this, viewModelFactory)[CategoryViewModel::class.java]

        // 触发数据请求并观察LiveData
        categoryViewModel.getMainCategory()
        categoryViewModel.mainCategoryList.observe(this) { value ->
            value?.let {
                Log.d("cor_get", it.toString())
            }
        }
    }

    // 权限相关方法保持不变...
}

5. 依赖优化(使用稳定版)

def lifecycle_version = "2.6.2"
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")
kapt("androidx.lifecycle:lifecycle-compiler:$lifecycle_version")
// 若未使用Jetpack Compose,移除该依赖
// implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")

内容的提问来源于stack exchange,提问作者Abdulrahman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 18:27:21