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

在MVVM架构中结合Dagger2与Retrofit的实现疑问

你的思路完全正确!接下来一步步完成实现

首先,你的核心方向没问题:用Dagger将Retrofit生成的ApiCredentials实例注入到MainViewModel中,是MVVM+Dagger+Retrofit架构的标准做法。咱们来把细节补全,解决你遇到的问题:

1. 先修正Retrofit接口的小问题

你当前的ApiCredentials接口写法有个小问题:@get:POST这种写法不太适合POST请求(POST通常需要传递请求体参数,比如用户名密码)。咱们改成标准的方法形式,同时确保返回的Observable能正确携带凭证:

interface ApiCredentials {
    // 假设你需要传递账号密码作为请求体,这里用LoginRequest作为示例数据类
    @POST("/api/auth/sign_in")
    fun getAuthentication(@Body loginRequest: LoginRequest): Observable<Credentials>
}

// 配套的请求体数据类
data class LoginRequest(
    val email: String,
    val password: String
)

// 你的Credentials数据类(假设已定义)
data class Credentials(
    val token: String,
    val userId: Int
)

如果你的POST请求不需要请求体(这种情况很少见),也可以保持方法形式,而不是用val属性,这样更符合Retrofit的规范。

2. 完善Dagger组件配置

你已经有了NetworkModule,现在需要创建一个Dagger Component,用来连接Module和需要注入的类(比如你的ViewModel):

@Singleton
@Component(modules = [NetworkModule::class])
interface AppComponent {
    // 提供一个方法来创建ViewModel工厂,或者直接注入ViewModel
    fun viewModelFactory(): ViewModelProvider.Factory
}

另外,你的NetworkModule里的provideRetrofit最好改成@Provides方法,保持Module的一致性:

@Module
object NetworkModule{
    private const val BASE_URL = "你的基础URL"

    @Provides
    @Reusable
    @JvmStatic
    fun provideUserAuth(retrofit: Retrofit): ApiCredentials{
        return retrofit.create(ApiCredentials::class.java)
    }

    @Provides
    @Singleton
    @JvmStatic
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
    }
}

3. 让MainViewModel接受注入并处理Observable

接下来修改MainViewModel,让它通过构造函数注入ApiCredentials,然后将Retrofit返回的Observable转换成LiveData(MVVM中ViewModel通常用LiveData和UI层交互,确保生命周期安全):

class MainViewModel @Inject constructor(
    private val apiCredentials: ApiCredentials
) : ViewModel() {
    // 用LiveData来暴露凭证数据给UI层
    private val _credentialsLiveData = MutableLiveData<Result<Credentials>>()
    val credentialsLiveData: LiveData<Result<Credentials>> = _credentialsLiveData

    // 发起登录请求的方法,供UI层调用
    fun authenticate(email: String, password: String) {
        apiCredentials.getAuthentication(LoginRequest(email, password))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                { credentials ->
                    _credentialsLiveData.value = Result.success(credentials)
                },
                { error ->
                    _credentialsLiveData.value = Result.failure(error)
                }
            )
    }

    // 记得在ViewModel销毁时取消RxJava订阅,避免内存泄漏
    private val compositeDisposable = CompositeDisposable()

    override fun onCleared() {
        super.onCleared()
        compositeDisposable.clear()
    }
}

4. 实现Dagger ViewModel工厂

为了让Dagger能正确注入ViewModel,咱们需要一个自定义的ViewModel工厂:

class DaggerViewModelFactory @Inject constructor(
    private val viewModels: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val provider = viewModels[modelClass] ?: viewModels.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("Unknown ViewModel class: $modelClass")
        return provider.get() as T
    }
}

5. 在Activity/Fragment中获取ViewModel

最后,在你的MainActivity里,通过Dagger获取ViewModelFactory,然后得到MainViewModel:

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 初始化Dagger组件(这里假设你已经在Application里创建了AppComponent实例)
        val appComponent = (application as MyApp).appComponent
        val viewModelFactory = appComponent.viewModelFactory()
        viewModel = ViewModelProvider(this, viewModelFactory)[MainViewModel::class.java]

        // 观察LiveData,更新UI
        viewModel.credentialsLiveData.observe(this) { result ->
            result.onSuccess { credentials ->
                // 处理成功的凭证,比如保存到SharedPreferences
                Toast.makeText(this, "登录成功,Token: ${credentials.token}", Toast.LENGTH_SHORT).show()
            }.onFailure { error ->
                // 处理错误
                Toast.makeText(this, "登录失败: ${error.message}", Toast.LENGTH_SHORT).show()
            }
        }

        // 示例:点击按钮发起登录请求
        btn_login.setOnClickListener {
            viewModel.authenticate("test@example.com", "password123")
        }
    }
}

最后补充几个关键点

  • 确保你的Application类初始化了Dagger的AppComponent:
class MyApp : Application() {
    val appComponent: AppComponent by lazy {
        DaggerAppComponent.create()
    }
}
  • 别忘了在AndroidManifest.xml里指定这个Application类。
  • RxJava的订阅一定要在ViewModel销毁时取消,避免内存泄漏(上面的代码已经用CompositeDisposable处理了)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:38:08