22、人脸识别与MVVM架构:ViewModel管理认证状态、Repository模式封装、LiveData/StateFlow通知UI
说实话,很多新手做生物识别时,喜欢把代码全塞在Activity里。嗯,我早期也这么干过——结果项目一复杂,Activity直接变成“屎山”。后来我痛定思痛,全面拥抱MVVM架构。今天咱们就聊聊,怎么把人脸识别和MVVM结合起来,让代码既清爽又抗造。
为什么非得用MVVM?
你想想看,人脸识别涉及好几个环节:相机预览、检测回调、结果处理、UI更新。如果全写在一起,调试一个bug可能要翻几百行代码。我个人习惯把业务逻辑和UI彻底分离,这样好处很明显:
- ViewModel:掌管认证状态,Activity重建时数据不丢
- Repository:封装底层实现,切换人脸库时只改一个地方
- LiveData/StateFlow:自动通知UI更新,不用手动刷新
我在项目中遇到过最典型的场景:用户旋转手机,Activity重建,结果人脸认证状态丢了,还得重新检测。用ViewModel后,状态稳稳地保留着,用户体验直接提升一个档次。
ViewModel:认证状态的“保险箱”
ViewModel的生命周期比Activity长,它会在配置变更后继续存活。说白了,它就是认证状态的“保险箱”。
class FaceAuthViewModel : ViewModel() {
// 认证状态:空闲、检测中、成功、失败
private val _authState = MutableLiveData<AuthState>(AuthState.Idle)
val authState: LiveData<AuthState> = _authState
// 人脸特征数据
private val _faceFeatures = MutableLiveData<FaceFeatures?>(null)
val faceFeatures: LiveData<FaceFeatures?> = _faceFeatures
fun startAuth(bitmap: Bitmap) {
_authState.value = AuthState.Detecting
viewModelScope.launch(Dispatchers.Default) {
// 模拟人脸检测
val result = faceRepository.detectFace(bitmap)
_authState.postValue(
if (result.isSuccess) AuthState.Success
else AuthState.Failed(result.errorMsg)
)
}
}
}
sealed class AuthState {
object Idle : AuthState()
object Detecting : AuthState()
data class Success(val confidence: Float) : AuthState()
data class Failed(val reason: String) : AuthState()
}
Repository模式:封装底层实现
Repository就像个“中间人”,它把人脸检测的具体实现藏起来。你想想看,今天用Google的FaceDetector,明天换成华为的FaceKit,只要Repository接口不变,ViewModel完全不用改。
class FaceRepository(private val context: Context) {
private val detector = FaceDetector.Builder(context)
.setClassificationType(FaceDetector.ALL_CLASSIFICATIONS)
.build()
suspend fun detectFace(bitmap: Bitmap): Result<FaceFeatures> {
return withContext(Dispatchers.IO) {
try {
val faces = detector.detect(bitmap)
if (faces.isNotEmpty()) {
val feature = extractFeatures(faces[0])
Result.success(feature)
} else {
Result.failure(Exception("未检测到人脸"))
}
} catch (e: Exception) {
Result.failure(e)
}
}
}
private fun extractFeatures(face: Face): FaceFeatures {
// 提取特征点、置信度等
return FaceFeatures(
landmarks = face.landmarks,
confidence = face.confidence,
bounds = face.bounds
)
}
}
LiveData vs StateFlow:怎么选?
LiveData是Android原生方案,简单好用。StateFlow是Kotlin协程的产物,功能更强大。我个人建议:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 简单UI更新 | LiveData | 开箱即用,无需额外依赖 |
| 复杂数据流 | StateFlow | 支持操作符,防抖、去重更方便 |
| 需要初始值 | StateFlow | 天生带初始值,LiveData需要额外处理 |
举个例子,如果用StateFlow实现人脸检测状态:
class FaceAuthViewModel : ViewModel() {
private val _authState = MutableStateFlow<AuthState>(AuthState.Idle)
val authState: StateFlow<AuthState> = _authState.asStateFlow()
fun startAuth(bitmap: Bitmap) {
_authState.value = AuthState.Detecting
viewModelScope.launch {
faceRepository.detectFace(bitmap)
.onSuccess { _authState.value = AuthState.Success(it.confidence) }
.onFailure { _authState.value = AuthState.Failed(it.message ?: "未知错误") }
}
}
}
UI层如何订阅?
Activity或Fragment里,只需要观察ViewModel的状态,然后更新UI即可。代码非常干净:
class FaceAuthActivity : AppCompatActivity() {
private val viewModel: FaceAuthViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_face_auth)
// 观察认证状态
viewModel.authState.observe(this) { state ->
when (state) {
is AuthState.Idle -> showIdle()
is AuthState.Detecting -> showDetecting()
is AuthState.Success -> showSuccess(state.confidence)
is AuthState.Failed -> showFailed(state.reason)
}
}
}
}
你看,Activity里没有任何业务逻辑,全是UI操作。这就是MVVM的魅力——职责清晰,测试也方便。
整体架构图
下面这张图,展示了人脸识别在MVVM架构中的完整数据流:
好了,关于人脸识别和MVVM架构的整合,今天就聊到这儿。记住:ViewModel管状态,Repository管实现,LiveData/StateFlow管通知。这三者配合好了,你的代码会变得异常清晰。下次维护时,你会感谢当初的自己。
公众号:蓝海资料掘金营,微信deep3321