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

多模块架构下,未引入任何Android库的data模块如何通过Dependency Inversion访问SharedPreferences?

多模块架构下,未引入任何Android库的data模块如何通过Dependency Inversion访问SharedPreferences?

我完全懂你这种困惑!当年第一次接触依赖倒置在多模块架构里的实际应用时,也绕了好一会儿才摸透门道。咱们一步步拆解这个问题,把依赖倒置的思路落地:

核心思路:用抽象隔离平台依赖

依赖倒置原则(Dependency Inversion)的核心就是高层模块不依赖低层模块,两者都依赖抽象;抽象不依赖细节,细节依赖抽象。放到你的场景里:

  • data模块是「业务数据处理层」,它不能碰Android的具体类(比如SharedPreferences),所以我们要给它一个纯抽象的接口;
  • app模块是「细节实现层」,它能访问Android框架,负责把SharedPreferences包装成这个抽象接口的实现;
  • 最后通过依赖注入把实现传递给data模块,让它能间接使用SharedPreferences的功能。

第一步:在data模块定义抽象存储接口

在data模块里写一个完全不涉及Android API的接口,只定义你需要的存储操作(比如存字符串、取字符串,按需扩展)。这样data模块完全不需要引入任何Android库:

// data模块的纯Kotlin/Java代码,无Android依赖
interface LocalStorage {
    fun saveString(key: String, value: String)
    fun getString(key: String, defaultValue: String?): String?
    // 可以根据业务需求添加其他方法,比如saveInt、getBoolean等
}

这个接口就是data模块依赖的「抽象」,它只关心要做什么,不关心具体怎么实现。

第二步:在app模块实现抽象接口

app模块可以自由访问Android框架,所以我们在这里写一个基于SharedPreferences的LocalStorage实现,把Android的具体操作封装起来:

// app模块的代码,可访问Android API
class SharedPreferencesLocalStorage(private val sharedPreferences: SharedPreferences) : LocalStorage {
    override fun saveString(key: String, value: String) {
        sharedPreferences.edit().putString(key, value).apply()
    }

    override fun getString(key: String, defaultValue: String?): String? {
        return sharedPreferences.getString(key, defaultValue)
    }
}

这里我们把SharedPreferences的操作转换成了LocalStorage接口定义的方法,相当于给Android的具体实现套了一层「抽象壳」。

第三步:通过依赖注入把实现注入到data模块

不管你用Hilt、Dagger这类DI框架,还是手动做依赖注入,核心都是把SharedPreferencesLocalStorage的实例传递给data模块需要用它的地方。比如data模块的Repository:

// data模块的Repository,只依赖抽象的LocalStorage
class UserRepository(private val localStorage: LocalStorage) {
    fun saveUserName(userName: String) {
        localStorage.saveString("USER_NAME", userName)
    }

    fun getUserName(): String? {
        return localStorage.getString("USER_NAME", null)
    }
}

然后在app模块的DI配置里,把LocalStorage和它的具体实现绑定。比如用Hilt的话:

// app模块的Hilt配置模块
@Module
@InstallIn(SingletonComponent::class)
object StorageModule {
    @Provides
    fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
        return context.getSharedPreferences("APP_PREFS", Context.MODE_PRIVATE)
    }

    @Provides
    fun provideLocalStorage(sharedPreferences: SharedPreferences): LocalStorage {
        return SharedPreferencesLocalStorage(sharedPreferences)
    }
}

这样当data模块的UserRepository需要LocalStorage时,DI框架会自动把SharedPreferencesLocalStorage的实例传进去,data模块完全不知道背后是SharedPreferences在工作。


为什么这就是依赖倒置?

如果按照传统的思路,你可能会让data模块直接依赖SharedPreferences(低层的Android具体类),但这样data模块就被绑定到了Android平台,还违反了「不能加Android库」的限制。

而现在的做法:

  1. data模块只依赖自己定义的抽象(LocalStorage),不依赖任何Android细节;
  2. app模块的具体实现(SharedPreferencesLocalStorage)依赖这个抽象;
  3. 高层模块(data的Repository)和低层模块(SharedPreferences实现)都依赖抽象,完全符合依赖倒置的原则。

而且这么做还有额外好处:如果以后要把data模块移植到其他平台(比如桌面端),只需要写一个对应平台的LocalStorage实现(比如用Java的Preferences类),根本不需要修改data模块的代码!

备注:内容来源于stack exchange,提问作者Varian Wrynn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 15:13:07