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

Android开发中向领域层用例传递Context是否为最佳实践?

问题解答

直接给UseCase传Context靠谱吗?

不太靠谱,原因如下:

  • 职责混乱:UseCase的核心应该是处理业务逻辑(比如计算闹钟时间、存储闹钟数据),而创建PendingIntent属于Android框架层操作,塞到UseCase里会让它的职责变得不纯粹。
  • 测试难度高:依赖Context后,UseCase会和Android系统绑定,单元测试时需要mock整个Context,操作繁琐,甚至无法脱离Android环境做纯JVM测试。
  • 内存泄漏风险:如果不小心传入Activity的Context,一旦UseCase持有这个Context但Activity已销毁,就可能引发内存泄漏。

可行的解决方案

1. 封装闹钟调度逻辑到独立类(首推)

创建专门的AlarmScheduler接口与实现类,把Context、AlarmManager和PendingIntent的创建逻辑全部封装进去,UseCase只依赖这个接口,完全不接触Android框架类。

示例代码:

// 定义抽象接口,规范闹钟调度行为
interface AlarmScheduler {
    fun scheduleRepeatingAlarm(alarmTime: Long, alarmId: Int)
}

// Android平台的实现类,处理具体的闹钟操作
class AndroidAlarmScheduler(
    private val appContext: Context,
    private val alarmManager: AlarmManager
) : AlarmScheduler {
    override fun scheduleRepeatingAlarm(alarmTime: Long, alarmId: Int) {
        val pendingIntent = PendingIntent.getBroadcast(
            appContext,
            alarmId,
            Intent(appContext, AlarmReceiver::class.java).apply {
                putExtra("ALARM_ID", alarmId)
            },
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        alarmManager.setRepeating(
            AlarmManager.RTC_WAKEUP,
            alarmTime,
            AlarmManager.INTERVAL_DAY,
            pendingIntent
        )
    }
}

// 修改后的UseCase,仅依赖业务类与抽象调度接口
class SetReminderAlarmsUseCase(
    private val hydrateRepository: HydrateRepository,
    private val alarmScheduler: AlarmScheduler
) {
    suspend operator fun invoke(
        wakeUpTime: String,
        sleepTime: String,
        drinkAmount: Int,
        target: Int,
    ) {
        val alarms = getAlarmsList(wakeUpTime, sleepTime, drinkAmount, target)
        insertAlarms(alarms)

        alarms.forEach { alarm ->
            alarmScheduler.scheduleRepeatingAlarm(getAlarmTime(alarm), alarm.id)
        }
    }

    // 业务辅助方法保留
    private fun getAlarmsList(...) = ...
    private suspend fun insertAlarms(alarms: List<Alarm>) = hydrateRepository.insertAlarms(alarms)
    private fun getAlarmTime(alarm: Alarm): Long = ...
}

这种方案的优势:

  • UseCase彻底与Android框架解绑,单元测试时只需mockAlarmScheduler接口即可验证业务逻辑正确性。
  • 闹钟调度逻辑集中管理,后续修改(比如换用setExactAndAllowWhileIdle实现精准闹钟)仅需改动实现类,不影响业务代码。

2. 提供PendingIntent工厂类

如果不想封装完整的调度逻辑,可以创建PendingIntentFactory类专门负责创建所需的PendingIntent,将Context依赖转移到工厂中,UseCase仅依赖工厂接口:

// 抽象工厂接口
interface PendingIntentFactory {
    fun createAlarmPendingIntent(alarmId: Int): PendingIntent
}

// Android平台的工厂实现
class AndroidPendingIntentFactory(private val appContext: Context) : PendingIntentFactory {
    override fun createAlarmPendingIntent(alarmId: Int): PendingIntent {
        return PendingIntent.getBroadcast(
            appContext,
            alarmId,
            Intent(appContext, AlarmReceiver::class.java).apply {
                putExtra("ALARM_ID", alarmId)
            },
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
    }
}

// 修改后的UseCase
class SetReminderAlarmsUseCase(
    private val hydrateRepository: HydrateRepository,
    private val alarmManager: AlarmManager,
    private val pendingIntentFactory: PendingIntentFactory
) {
    suspend operator fun invoke(
        wakeUpTime: String,
        sleepTime: String,
        drinkAmount: Int,
        target: Int,
    ) {
        val alarms = getAlarmsList(wakeUpTime, sleepTime, drinkAmount, target)
        insertAlarms(alarms)

        alarms.forEach { alarm ->
            val pendingIntent = pendingIntentFactory.createAlarmPendingIntent(alarm.id)
            alarmManager.setRepeating(
                AlarmManager.RTC_WAKEUP,
                getAlarmTime(alarm),
                AlarmManager.INTERVAL_DAY,
                pendingIntent
            )
        }
    }
}

这种方式比直接传Context更合理,虽然UseCase仍依赖AlarmManager,但AlarmManager可通过依赖注入提供,测试时也能轻松mock。

注意事项

  • 无论采用哪种方案,都要使用Application Context而非Activity Context,避免内存泄漏;可通过依赖注入(如Dagger/Hilt)提供Application Context实例。
  • Android 12及以上版本创建PendingIntent时,必须添加FLAG_IMMUTABLE标记,避免安全风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 16:54:28