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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 23:45:17