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

集成ViewModel与Dialog后Android应用持续崩溃,求排查解决

山羊养殖场追踪应用Dialog+ViewModel集成崩溃问题修复

我开发了一个山羊养殖场信息追踪的新项目,这是我首次为弹窗Dialog使用ViewModel,推测问题出在此处——在集成ViewModel和Dialog之前,应用运行流畅。当前Gradle构建无报错,但应用频繁崩溃。

核心问题分析

代码中存在一个致命的ViewModel使用错误:在GoatList的列表循环里,每次渲染GoatCard时都手动实例化了MainViewModel()。这会导致:

  • 每个列表项持有独立的ViewModel实例,状态无法统一管理
  • 手动创建的ViewModel不受Jetpack生命周期框架管控,引发生命周期相关崩溃
  • LazyGrid复用列表项时,ViewModel频繁创建销毁,进一步加剧崩溃概率

同时,Activity中初始化的ViewModel没有传递到Composable层级,状态管理链路完全断裂。

修复后的完整代码

1. MainActivity 调整

将Activity中获取的ViewModel传递给根Composable:

class MainActivity : AppCompatActivity() {
    private val viewModel by viewModels<MainViewModel>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            GoatsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    GoatsApp(viewModel = viewModel)
                }
            }
        }
    }
}

2. GoatsApp 调整

接收ViewModel并向下传递给列表组件:

@Composable
fun GoatsApp(viewModel: MainViewModel) {
    val provider = GoogleFont.Provider(
        providerAuthority = "com.google.android.gms.fonts",
        providerPackage = "com.google.android.gms",
        certificates = R.array.com_google_android_gms_fonts_certs
    )
    val fontName = GoogleFont("Lato")

    val fontFamily = FontFamily(
        Font(googleFont = fontName, fontProvider = provider)
    )
    Column(modifier = Modifier.padding(16.dp)) {
        Text(text = "Does", modifier = Modifier
            .background(color = Color.LightGray)
            .fillMaxWidth()
            .padding(12.dp),
            fontStyle = FontStyle.Italic,
            textAlign = TextAlign.Center,
            fontFamily = fontFamily,
            fontWeight = FontWeight.ExtraLight
        )

        GoatList(goatList = DataSource.goats, viewModel = viewModel, modifier = Modifier.padding(8.dp))
    }
}

3. GoatList 调整

接收ViewModel并传递给单个卡片组件,禁止手动实例化ViewModel:

@Composable
fun GoatList(goatList: List<Goats>, viewModel: MainViewModel, modifier: Modifier = Modifier.padding(8.dp)) {
    LazyVerticalGrid(columns = GridCells.Fixed(1), modifier.padding(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
        items(goats) { allgoats ->
            GoatCard(allgoats = allgoats, viewModel = viewModel)
        }
    }
}

4. GoatCard 保持不变(仅接收ViewModel参数)

@Composable
fun GoatCard(allgoats: Goats, viewModel: MainViewModel) {
    val years = allgoats.age.toString()

    Card(modifier = Modifier) {
        Row{
            Image(painter = painterResource(allgoats.image), contentDescription = allgoats.name.toString(),
                modifier = Modifier.size(68.dp),
                contentScale = ContentScale.Crop)

            Column(Modifier.width(120.dp)) {
                Text(text = stringResource(allgoats.name), modifier = Modifier.padding(8.dp),
                    style = MaterialTheme.typography.titleSmall)
                Row{
                    Text(text = stringResource(allgoats.breed), modifier = Modifier.padding(8.dp), style = MaterialTheme.typography.bodySmall)
                    Text(years, modifier = Modifier.padding(8.dp), style = MaterialTheme.typography.bodySmall)

                    Box {
                        Button(onClick = { viewModel.isGoatClick() },
                            modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors(Color.LightGray)) { }
                        Text(text = "More",
                            style = MaterialTheme.typography.bodySmall,
                            textAlign = TextAlign.End,
                            fontWeight = FontWeight.Bold,
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(end = 6.dp, top = 12.dp))
                    }
                }

                if(viewModel.isDialogShown) {
                    CustomDialog(onDismiss = { viewModel.onDismissDialog() }, 
                                myGoats = Goats(name = allgoats.name, age = allgoats.age, breed = allgoats.breed, image = allgoats.image))
                }
            }
        }
    }
}

5. MainViewModel 保持不变

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {
    var isDialogShown by mutableStateOf(false)
        private set

    fun isGoatClick() {
        isDialogShown = true
    }

    fun onDismissDialog() {
        isDialogShown = false
    }
}

更简洁的实现方式

可以直接在Composable中使用viewModel()函数获取ViewModel,无需从Activity传递,代码更简洁:

@Composable
fun GoatsApp() {
    val viewModel: MainViewModel = viewModel()
    // 剩余代码与之前一致
}

对应的MainActivity简化为:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            GoatsTheme {
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    GoatsApp()
                }
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 18:27:04