首页 > 解决方案 > Android Dagger2 问题。找到多个 Retrofit 注释,只允许一个

问题描述

我找到了几个相关的主题,但没有找到任何适合我的问题的解决方案。我需要提供两个具有不同基本 url 的改造实例。这是我的 ApiModule.kt:

@AppScope
@Module
internal class ApiModule{

    @AppScope
    @Provides
    fun provideOkHttpClient(): OkHttpClient {
        var b = OkHttpClient.Builder()

        if (BuildConfig.DEBUG){
            val logging = HttpLoggingInterceptor()
            logging.level = HttpLoggingInterceptor.Level.BODY
            b.addInterceptor(logging)
        }
        val t = 60*1000L

        b.readTimeout(t, TimeUnit.MILLISECONDS)
        b.writeTimeout(t, TimeUnit.MILLISECONDS)
        b.connectTimeout(t, TimeUnit.MILLISECONDS)
        return b.build()
    }

    @AppScope
    @Provides
    fun provideRetrofitAdapter(ok: OkHttpClient): Retrofit {
        return Retrofit.Builder()
                .client(ok)
                .baseUrl(Constants.Api.Links.BASE_URL)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }
    @AppScope
    @Provides
    fun provideEnergoApi(r: Retrofit): EnergoApi {
        return r.create(EnergoApi::class.java)
    }

    @AppScope
    @Provides
    fun provideEnergoManager(p: PrefHelper, ea: EnergoApi, pm:PayHubManager): DtecManager {
        return DtecManager(p, ea, pm)
    }
}

这是我的 InfoMudule.kt:

@ActivityScope
@Module
internal class InfoModule{

    @ActivityScope
    @Provides
    fun provideInfoPresenter(p:PrefHelper, d:DtecManager, pu: PushManager, v:ViewUtil, n:NetworkUtil,
                         a:ApiErrorHandler):InfoPresenter{
         return InfoPresenter(p, d, pu, v, n,a)
    }

    @ActivityScope
    @Provides
    @Named("Retrofit2")
    fun providePushRetrofitAdapter(ok: OkHttpClient): Retrofit {
        return Retrofit.Builder()
                .client(ok)
                .baseUrl(Constants.Api.Links.BASE_PUSH_URL)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }

    @ActivityScope
    @Provides
    fun providePushApi(r: Retrofit): PushApi {
        return r.create(PushApi::class.java)
    }

    @ActivityScope
    @Provides
    fun providePushManager(c: Context, p:PrefHelper, pa: PushApi): PushManager {
        return PushManager(c,p,pa)
    }
}

我的应用组件:

@AppScope
@Component(modules = arrayOf(AppModule::class, ApiModule::class))
internal interface AppComponent{

     fun inject(a:BaseActivity)


     fun createLoginActivityComponent(): LoginComponent
     fun createInfoComponent(): InfoComponent
     fun createPayComponent(): PayComponent
     fun createNfcScanComponent(): NfcScanComponent
     fun createSettingsComponent(): SettingsComponent


     fun getNfcAdapter():NfcAdapter?
} 

这是我的信息组件:

@ActivityScope
@Subcomponent(modules = arrayOf(InfoModule::class))
internal interface InfoComponent {
     fun injectInfoActivity(a: InfoActivity)
}

我的 PushApi 界面:

internal interface PushApi {

@POST(Links.Push.subscribe)
fun subscribeToPush(@Header ("Content-type: application/json")
                    @Body b: BodySubscribe): Observable<Response<UniqueKey>>

}

我的信息活动:

internal class InfoActivity : BaseActivity(), InfoView, SwipeRefreshLayout.OnRefreshListener {

@Inject lateinit var presenter: InfoPresenter
@Inject lateinit var viewUtil: ViewUtil

companion object {
    @JvmStatic val PUT_COUNTERS = 101
    @JvmStatic val PAYMENT = 202
    @JvmStatic val NAME = "name"
    @JvmStatic val CURRENT_COUNTER = "currentCounter"
}

private var accountsDialog: AccountsDialog? = null
private var amountDialog: AmountDialog? = null

private lateinit var date: TextView
private lateinit var usedEnergy: TextView
private lateinit var balance: TextView
private lateinit var balanceTitle: TextView
private lateinit var counter1: TextView
private lateinit var counter1Title: TextView
private lateinit var counter2: TextView
private lateinit var counter2Title: TextView
private lateinit var counter3: TextView
private lateinit var counter3Title: TextView
private lateinit var address: TextView
private lateinit var tariff: TextView
private lateinit var refreshView: SwipeRefreshContainer
private var accountsMenuItem: MenuItem? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
    setContentView(R.layout.activity_info)
    val toolbar = findViewById(R.id.toolbar) as Toolbar
    setSupportActionBar(toolbar)

    date = findViewById(R.id.activityInfo_tv_date) as TextView
    usedEnergy = findViewById(R.id.activityInfo_tv_used) as TextView
    balance = findViewById(R.id.activityInfo_tv_balance) as TextView
    balanceTitle = findViewById(R.id.activityInfo_tv_balanceTitle) as TextView
    counter1 = findViewById(R.id.activityInfo_tv_counter1) as TextView
    counter1Title = findViewById(R.id.activityInfo_tv_counter1Title) as TextView
    counter2 = findViewById(R.id.activityInfo_tv_counter2) as TextView
    counter2Title = findViewById(R.id.activityInfo_tv_counter2Title) as TextView
    counter3 = findViewById(R.id.activityInfo_tv_counter3) as TextView
    counter3Title = findViewById(R.id.activityInfo_tv_counter3Title) as TextView
    address = findViewById(R.id.activityInfo_tv_address) as TextView
    tariff = findViewById(R.id.activityInfo_tv_tariff) as TextView
    refreshView = findViewById(R.id.activityInfo_srl) as SwipeRefreshContainer


    with(refreshView) {
        setOnRefreshListener(this@InfoActivity)
        setColorSchemeColors(ContextCompat.getColor(this@InfoActivity, R.color.colorAccent),
                ContextCompat.getColor(this@InfoActivity, R.color.colorPrimaryDark))
    }

    checkComponent(savedInstanceState)
    presenter.attachView(this)

    presenter.subscribeToPush()
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == PUT_COUNTERS && resultCode == Activity.RESULT_OK) {
        updateInfoView()
    }
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.info_menu, menu)
    accountsMenuItem = menu.findItem(R.id.action_accounts)
    return true
}

override fun onBackPressed() {

}

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    when (item?.itemId) {
        R.id.action_logout -> presenter.onLogOutClicked()
        R.id.action_accounts -> presenter.onAccountsClicked()
        R.id.action_feedback -> navigator.goToFeedbackView(this, presenter.dtecManager.accountInfo.full_name,
                presenter.dtecManager.accountInfo.abcode)
        R.id.action_settings -> navigator.goToSetingsActivity(this)
    }
    return super.onOptionsItemSelected(item)
}

override fun onRefresh() {
    presenter.onUpdateAccount()
}

override fun isSessionActive(): Boolean {
    return checkActiveSession()
}

override fun initComponent() {
    App.instance.createInfoComponent().injectInfoActivity(this)
}

override fun activityIsFinishing() {
    presenter.detachView()
    accountsDialog?.onDestroy()
    amountDialog?.onDestroy()
    App.instance.releaseInfoComponent()
}
}

我的信息演示者:

internal class InfoPresenter(
    var prefHelper: PrefHelper,
    val dtecManager: DtecManager,
    val pushManager: PushManager,
    val viewUtils: ViewUtil,
    val networkUtil: NetworkUtil,
    private var apiErrorHandler: ApiErrorHandler
) : BasePresenter<InfoView>(), AccountsDialog.OnAccountDialogListener {

private var context: Context = App.instance.applicationContext
private var stringTokenizer: StringTokenizer? = null

private var getNewAccountInfoSubscription: Subscription? = null
private var uniqueKey: Observable<UniqueKey>? = null
private lateinit var simpleDateFormat: SimpleDateFormat
private lateinit var addressTitle: String
private lateinit var tariffTitle: String
lateinit var accountInfo: AccountInfo
    private set

override fun attachView(mvpView: InfoView) {
    super.attachView(mvpView)
    addressTitle = context.getString(R.string.activityInfo_tv_addressTitle)
    tariffTitle = context.getString(R.string.activityInfo_tv_tariffTitle)
    var locale: Locale
    when (Locale.getDefault().country) {
        "RU" -> locale = Locale("ru")
        else -> locale = Locale("uk")
    }
    simpleDateFormat = SimpleDateFormat("dd MMMM yyyy", locale)
    mvpView.updateInfoView()
}

override fun detachView() {
    super.detachView()
    getNewAccountInfoSubscription?.unsubscribe()
}

override fun onNewAccountSelect(abcode: String?) {
    if (mvpView!!.isSessionActive()) {
        abcode?.let {
            getNewAccountInfo(it)
            mvpView?.showLoadingDialog(true)?.subscribe {
                getNewAccountInfoSubscription?.unsubscribe()
            }
        }
    }
}

fun subscribeToPush() {

    accountInfo = prefHelper.getAccountInfo()
    uniqueKey = pushManager.subscribe(Constants.ACCOUNT_TYPE, accountInfo.abcode, accountInfo.unitId, Constants.FIREBASE_KEY)

}

fun onUpdateAccount() {
    if (mvpView!!.isSessionActive()) {
        if (networkUtil.isOnline(true)) {
            getNewAccountInfo(dtecManager.accountInfo.abcode)
        } else {
            mvpView?.finishRefreshView()
        }
    }
}

fun onPayClicked(v: View) {
    checkViewAttached()
    viewUtils.freeze(v)
    if (mvpView!!.isSessionActive() && networkUtil.isOnline(true)) {
        mvpView?.showAmountDialog()
    }
}
}

当 InfoActivity 启动时,应用程序崩溃并出现错误“找到多个改造注释,只允许一个”。为什么它不提供 InfoModule 的改造?

标签: androidkotlinretrofit2dagger-2dagger

解决方案


利用qualifiers以便 Dagger 能够知道Retrofit应该为每种情况提供哪个实例。

1) 创建限定符

RetrofitApi.kt

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class RetrofitApi

改造信息.kt

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class RetrofitInfo

2) 使用限定符

ApiModule.kt

@AppScope
@Provides
@RetrofitApi
fun provideRetrofitAdapter(ok: OkHttpClient): Retrofit {
    return Retrofit.Builder()
            .client(ok)
            .baseUrl(Constants.Api.Links.BASE_URL)
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
}

@AppScope
@Provides
fun provideEnergoApi(@RetrofitApi r: Retrofit): EnergoApi {
    return r.create(EnergoApi::class.java)
}

信息模块.kt

@ActivityScope
@Provides
@RetrofitInfo
fun providePushRetrofitAdapter(ok: OkHttpClient): Retrofit {
    return Retrofit.Builder()
            .client(ok)
            .baseUrl(Constants.Api.Links.BASE_PUSH_URL)
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
}

@ActivityScope
@Provides
fun providePushApi(@RetrofitInfo r: Retrofit): PushApi {
    return r.create(PushApi::class.java)
}

推荐阅读