如何在ViewModel中使用Sms Retriever Api且不注入Context?
解决ViewModel中使用Sms Retriever Api避免传入Context的方案
方案1:封装操作到Repository层
将Sms Retriever的相关逻辑封装到Repository类中,Repository持有Application Context(全局上下文无内存泄漏风险),ViewModel仅依赖Repository,不直接接触Context,符合MVVM的职责分离原则。
代码实现:
首先定义Repository:
class SmsRepository(private val appContext: Context) { // 获取SmsRetriever客户端 private fun getSmsRetrieverClient(): SmsRetrieverClient { return SmsRetriever.getClient(appContext) } // 封装短信监听启动逻辑 fun startSmsRetrieval(callback: SmsRetrieverCallback) { val task = getSmsRetrieverClient().startSmsRetriever() task.addOnSuccessListener { callback.onRetrievalStarted() } .addOnFailureListener { callback.onRetrievalFailed(it) } } // 定义回调接口,让ViewModel接收结果 interface SmsRetrieverCallback { fun onRetrievalStarted() fun onRetrievalFailed(error: Exception) } }
然后ViewModel依赖Repository:
class MyViewModel(private val smsRepository: SmsRepository) : ViewModel() { fun initSmsRetrieval() { smsRepository.startSmsRetrieval(object : SmsRepository.SmsRetrieverCallback { override fun onRetrievalStarted() { // 处理监听启动成功的业务逻辑 } override fun onRetrievalFailed(error: Exception) { // 处理启动失败的业务逻辑 } }) } }
最后在UI层(Activity/Fragment)初始化Repository并传入ViewModel:
class MyActivity : AppCompatActivity() { private lateinit var viewModel: MyViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 用Application Context初始化Repository val smsRepo = SmsRepository(applicationContext) // 通过自定义Factory注入Repository到ViewModel viewModel = ViewModelProvider(this, MyViewModelFactory(smsRepo))[MyViewModel::class.java] // 触发ViewModel中的业务逻辑 viewModel.initSmsRetrieval() } } // 自定义ViewModel工厂类,用于注入依赖 class MyViewModelFactory(private val smsRepo: SmsRepository) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(MyViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return MyViewModel(smsRepo) as T } throw IllegalArgumentException("Unknown ViewModel class") } }
方案2:使用依赖注入框架(如Hilt)管理依赖
如果项目已使用依赖注入框架(如Hilt),可以通过框架自动注入SmsRetriever客户端或Repository,完全避免手动传递Context。
代码实现:
首先创建Hilt模块提供依赖:
@Module @InstallIn(SingletonComponent::class) object SmsModule { // 提供Application Context @Provides @ApplicationContext fun provideAppContext(@ApplicationContext context: Context): Context { return context } // 提供SmsRetriever客户端 @Provides fun provideSmsRetrieverClient(@ApplicationContext appContext: Context): SmsRetrieverClient { return SmsRetriever.getClient(appContext) } // 提供SmsRepository @Provides fun provideSmsRepository(client: SmsRetrieverClient, @ApplicationContext appContext: Context): SmsRepository { return SmsRepository(appContext) } }
然后ViewModel通过构造函数注入Repository:
@HiltViewModel class MyViewModel @Inject constructor(private val smsRepository: SmsRepository) : ViewModel() { fun initSmsRetrieval() { smsRepository.startSmsRetrieval(object : SmsRepository.SmsRetrieverCallback { override fun onRetrievalStarted() { // 业务逻辑处理 } override fun onRetrievalFailed(error: Exception) { // 业务逻辑处理 } }) } }
最后在UI层直接获取ViewModel:
@AndroidEntryPoint class MyActivity : AppCompatActivity() { // Hilt自动注入ViewModel private val viewModel: MyViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.initSmsRetrieval() } }
方案3:封装为全局单例(谨慎使用)
将SmsRetriever客户端封装为单例,在Application初始化时传入Application Context,ViewModel直接调用单例方法。此方案简单但扩展性差,适合小型项目。
代码实现:
object SmsRetrieverManager { private lateinit var smsRetrieverClient: SmsRetrieverClient // 在Application中初始化 fun initialize(appContext: Context) { if (!::smsRetrieverClient.isInitialized) { smsRetrieverClient = SmsRetriever.getClient(appContext) } } // 对外提供客户端实例 fun getClient(): SmsRetrieverClient { check(::smsRetrieverClient.isInitialized) { "SmsRetrieverManager未初始化,请先调用initialize方法" } return smsRetrieverClient } // 封装短信监听启动逻辑 fun startSmsRetrieval(callback: SmsRetrievalCallback) { val task = getClient().startSmsRetriever() task.addOnSuccessListener { callback.onSuccess() } .addOnFailureListener { callback.onError(it) } } interface SmsRetrievalCallback { fun onSuccess() fun onError(error: Exception) } }
在Application类中初始化:
class MyApp : Application() { override fun onCreate() { super.onCreate() SmsRetrieverManager.initialize(this) } }
ViewModel中直接使用:
class MyViewModel : ViewModel() { fun startSmsRetrieval() { SmsRetrieverManager.startSmsRetrieval(object : SmsRetrieverManager.SmsRetrievalCallback { override fun onSuccess() { // 业务逻辑处理 } override fun onError(error: Exception) { // 业务逻辑处理 } }) } }
方案推荐
优先选择方案1(Repository模式),它完全符合MVVM架构的解耦要求,且无需引入额外框架;如果项目已使用依赖注入,方案2会更简洁;方案3仅适合小型或快速迭代的项目,长期维护性较差。
内容的提问来源于stack exchange,提问作者Reza
相关产品推荐
相关产品推荐

