抽象BaseViewModel类能否通过Hilt注入?如何实现通用日志功能
问题解答
你给出的方案是否可行?
这个方案是可行的,但需要调整几个细节:
- 子类继承
BaseViewModel时,必须在自身的@Inject构造函数中传递Logger实例给父类,示例代码如下:@HiltViewModel class HomeViewModel @Inject constructor(logger: Logger) : BaseViewModel(logger) - 将
sendLog设为open方法是合理的,子类如果需要自定义日志逻辑(比如添加额外标识、调整日志格式),可以直接重写该方法。 - 利用
viewModelScope启动协程确保日志请求在ViewModel生命周期内执行,避免内存泄漏,这个设计是正确的。
另外,抽象类本身不需要添加@HiltViewModel注解——这个注解只需加在具体的子类ViewModel上即可。修正后的BaseViewModel代码:
abstract class BaseViewModel(protected val logger: Logger) : ViewModel() { open fun sendLog(msg: String){ viewModelScope.launch{ logger.callApi(msg) } } }
更优方案推荐
方案1:将日志逻辑抽离为独立的UseCase类
把日志相关逻辑从ViewModel中解耦,放到单独的LogUseCase类中,让ViewModel只负责调用,职责更单一,也方便单独测试日志逻辑:
// 独立的日志用例类 class LogUseCase @Inject constructor(private val logger: Logger) { suspend fun sendLog(msg: String) { logger.callApi(msg) } } // 基类ViewModel abstract class BaseViewModel : ViewModel() { @Inject lateinit var logUseCase: LogUseCase fun sendLog(msg: String) { viewModelScope.launch { logUseCase.sendLog(msg) } } } // 子类ViewModel @HiltViewModel class HomeViewModel @Inject constructor() : BaseViewModel()
这种方式的优势:
- 日志逻辑可在其他组件(如Repository、其他UseCase)中复用,不局限于ViewModel。
- 日志逻辑的单元测试可独立进行,无需依赖ViewModel。
- ViewModel的职责更聚焦于业务逻辑,符合单一职责原则。
方案2:使用委托模式(灵活场景)
如果不想强制所有ViewModel继承BaseViewModel,或需要给不同ViewModel提供不同日志实现,可以用委托模式:
// 定义日志接口 interface Loggable { fun sendLog(msg: String) } // 日志实现类 class LoggerDelegate @Inject constructor( private val logger: Logger, private val coroutineScope: CoroutineScope ) : Loggable { override fun sendLog(msg: String) { coroutineScope.launch { logger.callApi(msg) } } } // 子类ViewModel通过委托实现日志功能 @HiltViewModel class HomeViewModel @Inject constructor( loggerDelegate: LoggerDelegate ) : ViewModel(), Loggable by loggerDelegate
这种方式灵活性更高,ViewModel可按需选择是否实现日志功能,也能随时替换不同的Loggable实现。
内容的提问来源于stack exchange,提问作者c-an
相关产品推荐
相关产品推荐

