代码之家  ›  专栏  ›  技术社区  ›  Abhishek Jha

尝试访问方法时出现错误,因为“如果没有@Provides带注释的方法,则无法提供”

  •  0
  • Abhishek Jha  · 技术社区  · 6 年前

    我声明了一个ApplicationComponent,它有几个方法。当我试图在任何片段或活动中访问应用程序组件的方法时,我可以使用应用程序类的build component,但是当我将此ApplicationComponent作为依赖项放入其他组件时,现在当我试图访问该方法时 它说“没有@Provides注释的方法就不能提供”。

    以前,当我在Java中像这样使用时,我能够访问 其他任何组件类中的ApplicationComponent类的方法,但在kotlin中,我无法访问。请帮我解决这个问题。!!

    这是我试过的密码。

    硅酸盐组分

    package com.satincreditcare.satin.android.dagger.component
    
    import android.arch.lifecycle.ViewModelProvider
    import com.satincreditcare.satin.android.dagger.module.RepositoryModule
    import com.satincreditcare.satin.android.dagger.module.RetrofitServiceModule
    import com.satincreditcare.satin.android.dagger.qualifier.LMSApplicationQualifier
    import com.satincreditcare.satin.android.dagger.qualifier.LMSAuthQualifier
    import com.satincreditcare.satin.android.dagger.scope.LmsAndroidApplicationScope
    import com.satincreditcare.satin.android.network.rest_controller.RestInterface
    import dagger.Component
    
    /**
     * Created by Abhishek Jha on 1/28/2019.
     */
    @LmsAndroidApplicationScope
    @Component(modules = [(RetrofitServiceModule::class), (RepositoryModule::class)])
    open interface LmsApplicationComponent {
    
        @LMSApplicationQualifier
        fun getRestInterface():RestInterface
    
        @LMSApplicationQualifier
        fun provideViewModelFactory(): ViewModelProvider.Factory
    
        @LMSAuthQualifier
        fun getAuthRestInterface():RestInterface
    
        @LMSAuthQualifier
        fun provideAuthViewModelFactory(): ViewModelProvider.Factory
    
    }
    

    在这里,我包含了两个模块,即reformationservicemodule和RepositoryModule。

    改造服务模块.kt

    package com.satincreditcare.satin.android.dagger.module
    
    import android.content.Context
    import com.fatboyindustrial.gsonjodatime.DateTimeConverter
    import com.google.gson.Gson
    import com.google.gson.GsonBuilder
    import com.satincreditcare.satin.android.constants.REST_API
    import com.satincreditcare.satin.android.network.rest_controller.RestInterface
    import com.satincreditcare.satin.android.dagger.qualifier.LMSApplicationQualifier
    import com.satincreditcare.satin.android.dagger.qualifier.LMSAuthQualifier
    import com.satincreditcare.satin.android.dagger.scope.LmsAndroidApplicationScope
    import dagger.Module
    import dagger.Provides
    import okhttp3.OkHttpClient
    import org.joda.time.DateTime
    import retrofit2.Retrofit
    import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
    import retrofit2.converter.gson.GsonConverterFactory
    
    /**
     * Created by Abhishek Jha on 1/29/2019.
     */
    @Module(includes = [(NetworkModule::class)])
    open class RetrofitServiceModule {
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSApplicationQualifier
        fun restInterface(@LMSApplicationQualifier retrofit: Retrofit): RestInterface{
            return retrofit.create(RestInterface::class.java)
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSApplicationQualifier
        fun retrofit(gson: Gson, @LMSApplicationQualifier okHttpClient: OkHttpClient,
                              @LMSApplicationQualifier baseUrl: String): Retrofit{
            return Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .client(okHttpClient)
                    .baseUrl(baseUrl)
                    .build()
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSApplicationQualifier
        fun baseUrl(context: Context): String{
            return REST_API.getInstance(context).API
        }
    
        @Provides
        @LmsAndroidApplicationScope
        fun gson(): Gson{
            val gsonBuilder: GsonBuilder = GsonBuilder()
            gsonBuilder.registerTypeAdapter(DateTime::class.java, DateTimeConverter())
            return gsonBuilder.create()
        }
    
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSAuthQualifier
        fun restInterfaceAuth(@LMSAuthQualifier retrofit: Retrofit): RestInterface{
            return retrofit.create(RestInterface::class.java)
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSAuthQualifier
        fun retrofitAuth(gson: Gson, @LMSAuthQualifier okHttpClient: OkHttpClient,
                                  @LMSAuthQualifier baseUrl: String): Retrofit{
            return Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .client(okHttpClient)
                    .baseUrl(baseUrl)
                    .build()
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSAuthQualifier
        fun baseUrlAuth(context: Context): String{
            return REST_API.getInstance(context).OAUTH_URL
        }
    }
    

    reformationservicemodule包含NetworkModule作为其依赖项 所以, 网络模块.kt

        package com.satincreditcare.satin.android.dagger.module
    
    import android.content.Context
    import android.os.Build
    import android.util.Base64
    import com.satincreditcare.satin.android.R
    import com.satincreditcare.satin.android.constants.AppConstants
    import com.satincreditcare.satin.android.constants.REST_API
    import com.satincreditcare.satin.android.events.AllEvents
    import com.satincreditcare.satin.android.events.Event
    import com.satincreditcare.satin.android.network.exception.InvalidRefreshTokenException
    import com.satincreditcare.satin.android.network.exception.InvalidTokenException
    import com.satincreditcare.satin.android.network.exception.NoConnectivityException
    import com.satincreditcare.satin.android.dagger.qualifier.LMSApplicationQualifier
    import com.satincreditcare.satin.android.dagger.qualifier.LMSAuthQualifier
    import com.satincreditcare.satin.android.dagger.scope.LmsAndroidApplicationScope
    import com.satincreditcare.satin.android.utils.AppUtility
    import com.satincreditcare.satin.android.utils.DataHolder
    import com.satincreditcare.satin.android.utils.NetworkUtils
    import dagger.Module
    import dagger.Provides
    import okhttp3.*
    import okhttp3.logging.HttpLoggingInterceptor
    import org.greenrobot.eventbus.EventBus
    import timber.log.Timber
    import java.io.File
    import java.io.IOException
    import java.io.InputStream
    import java.nio.charset.Charset
    import java.security.KeyStore
    import java.security.cert.Certificate
    import java.security.cert.CertificateFactory
    import java.util.*
    import java.util.concurrent.TimeUnit
    import javax.net.ssl.*
    
    /**
     * Created by Abhishek Jha on 1/29/2019.
     */
    
    @Module(includes = [(ContextModule::class)])
    open class NetworkModule {
        companion object {
            val DEFAULT_READ_TIMEOUT: Long = 180
            val DEFAULT_CONNECT_TIMEOUT: Long = 180
            val maxRequestsPerHost: Int = 5
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSApplicationQualifier
        fun okHttpClient(interceptorLogging: HttpLoggingInterceptor, cache: Cache,
                                  sslSocketFactory: SSLSocketFactory, trustManager: X509TrustManager,
                                  @LMSApplicationQualifier interceptor: Interceptor,
                                  authenticator: Authenticator): OkHttpClient {
            var okHttpClient: OkHttpClient = OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, trustManager)
                    .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                    .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    .addInterceptor(interceptorLogging)
                    .addInterceptor(interceptor)
                    .authenticator(authenticator)
                    .cache(cache)
                    .build()
    
            if (AppConstants.SLOW_NETWORK_MODE) {
                okHttpClient.dispatcher().maxRequestsPerHost = maxRequestsPerHost / 2
            } else {
                okHttpClient.dispatcher().maxRequestsPerHost = maxRequestsPerHost
            }
            return okHttpClient
    
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSAuthQualifier
        fun okHttpClientAuth(interceptorLogging: HttpLoggingInterceptor, cache: Cache,
                                      sslSocketFactory: SSLSocketFactory,
                                      trustManager: X509TrustManager,
                                      @LMSAuthQualifier interceptor: Interceptor): OkHttpClient {
            var okHttpClient: OkHttpClient = OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, trustManager)
                    .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                    .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    .addInterceptor(interceptorLogging)
                    .addInterceptor(interceptor)
                    .cache(cache)
                    .build()
            if (AppConstants.SLOW_NETWORK_MODE) {
                okHttpClient.dispatcher().maxRequestsPerHost = maxRequestsPerHost / 2
            } else {
                okHttpClient.dispatcher().maxRequestsPerHost = maxRequestsPerHost
            }
            return okHttpClient
        }
    
        @Provides
        @LmsAndroidApplicationScope
        fun x509TrustManager(context: Context): X509TrustManager {
            lateinit var trustManager: X509TrustManager
            try {
                //Load CAs
                val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
                val cert: InputStream
    
                if (REST_API.getInstance(context).API.equals(context.getString(R.string.PROD_URL),
                                ignoreCase = true)) {
                    cert = context.resources.openRawResource(R.raw.prod)
                } else {
                    cert = context.resources.openRawResource(R.raw.dev_uat)
                }
    
                val ca: Certificate
                try {
                    ca = cf.generateCertificate(cert)
                } finally {
                    cert.close()
                }
    
                //Creating a keystore containing our trusted CAs
                val keyStoreType: String = KeyStore.getDefaultType()
                val keyStore: KeyStore = KeyStore.getInstance(keyStoreType)
                keyStore.load(null, null)
                keyStore.setCertificateEntry("ca", ca)
    
                // creating a TrustManager that trusts the CAs in our KeyStore
                val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()
                val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm)
                tmf.init(keyStore)
    
                //X509Trust Manager
                val trustManagers = tmf.trustManagers
                if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) {
                    throw IllegalStateException("Unexpected default trust managers:" + Arrays
                            .toString(trustManagers))
                }
                trustManager = trustManagers[0] as X509TrustManager
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return trustManager
        }
    
        @Provides
        @LmsAndroidApplicationScope
        fun sslSocketFactory(trustManager: X509TrustManager): SSLSocketFactory {
            lateinit var sslContext: SSLContext
            lateinit var sslSocketFactory: SSLSocketFactory
            try {
                sslContext = SSLContext.getInstance("SSL")
                sslContext.init(null, arrayOf(trustManager), null)
                sslSocketFactory = sslContext.socketFactory
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return sslSocketFactory
        }
    
        @Provides
        @LmsAndroidApplicationScope
        fun httpLoggingInterceptor(): HttpLoggingInterceptor {
            val interceptor: HttpLoggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
                Timber.i(it)
            })
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
            return interceptor
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSAuthQualifier
        fun interceptorAuth(context: Context): Interceptor {
            val interceptor: Interceptor = Interceptor {
                //Applying two interceptors work together
                //Network Interceptor and OAuthTokenInterceptor
    
                if (NetworkUtils.isNetworkAvailable(context)) {
                    //Here, we will check for the SecureHeaderInterceptor start
                    val deviceHeader: String = Build.MODEL + "$$" + AppUtility.getImei(context)
                    val headerValue: String = Credentials.basic(AppUtility.getOAuthClientId(context),
                            AppUtility.getOAuthClientSecret(context), Charset.forName(AppConstants.CHARSET_UTF8))
                    var rawRequest: Request = it.request()
                    rawRequest = rawRequest
                            .newBuilder()
                            .addHeader(AppConstants.HTTP_HEADER_AUTHORIZATION, headerValue)
                            .addHeader(AppConstants.HTTP_HEADER_DEVICE_AUTH, AppUtility
                                    .encodeBase64(deviceHeader.trim(), Base64.NO_WRAP))
                            .build()
    
                    //Here, we will check for the OAuthTokenInterceptor end
    
                    try {
                        return@Interceptor it.proceed(rawRequest)
                    } catch (e: Exception) {
                        e.printStackTrace()
                        throw IOException(e)
                    }
                } else {
                    throw NoConnectivityException()
                }
            }
            return interceptor
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSApplicationQualifier
        fun interceptor(context: Context): Interceptor {
            return Interceptor { chain ->
                //Applying two interceptors work together
                //Network Interceptor and OAuthTokenInterceptor
                if (NetworkUtils.isNetworkAvailable(context)) {
    
                    //Here, we will check for the OAuthTokenInterceptor start
    
                    var rawRequest = chain.request()
                    //getting token
                    if (DataHolder.getInstance() == null) {
                        throw InvalidTokenException()
                    }
    
                    val resOAuthToken = DataHolder.getInstance().oauthToken
                            ?: throw InvalidTokenException()
    
                    if (!DataHolder.getInstance().isRefreshingToken) {
                        if (!DataHolder.getInstance().isRefreshTokenExpired) {
                            Timber.i("Sending request")
    
                            //locally checking life of access token
                            if (System.currentTimeMillis() < resOAuthToken.expiresOn) {
                                if (!AppConstants.IS_CUSTOM_IP) {
                                    rawRequest = rawRequest.newBuilder()
                                            .addHeader("Authorization", AppUtility
                                                    .createOAuthHeader(resOAuthToken.accessToken))
                                            .build()
                                }
                            } else {
                                //Sending Signal to refresh access token from server
                                DataHolder.getInstance().isRefreshingToken = true
                                EventBus.getDefault().post(Event(AllEvents
                                        .OAUTH_ACCESS_TOKEN_EXPIRED, false, resOAuthToken))
                                //dropping ongoing request(because access_token is expired)
                                throw InvalidTokenException()
                            }
                        } else {
                            //refresh token is expired (user should be logged out
                            throw InvalidRefreshTokenException(context)
                        }
                    } else {
                        //access token is getting refreshed so drop this request
                        Timber.i("access toke is already getting refreshed")
                        throw InvalidTokenException()
                    }
    
                    //Here, we will check for the OAuthTokenInterceptor end
    
                    //If everything goes well we can proceed to the below statement and satisfy
                    // the condition for the Network availability.
    
                    try {
                        return@Interceptor chain.proceed(rawRequest)//chain.request()
                    } catch (e: Exception) {
                        e.printStackTrace()
                        throw IOException(e)
                    }
    
                } else {
                    throw NoConnectivityException()
                }
                //return chain.proceed(chain.request());
            }
        }
    
        @Provides
        @LmsAndroidApplicationScope
        fun getAuthenticator(context: Context): Authenticator {
            return Authenticator { route, response ->
                val resOAuthToken = DataHolder.getInstance().oauthToken
                //Checking if access token is getting refreshed.
                if (!DataHolder.getInstance().isRefreshingToken) {
                    if (!DataHolder.getInstance().isRefreshTokenExpired) {
                        Timber.i("Now refreshing token")
                        //refreshing access_token from oauth server
                        DataHolder.getInstance().isRefreshingToken = true
                        EventBus.getDefault().post(Event(AllEvents
                                .OAUTH_ACCESS_TOKEN_EXPIRED, false, resOAuthToken))
                    } else {
                        //refresh token is expired (user should be logged out
                        throw InvalidRefreshTokenException(context)
                    }
                } else {
                    Timber.i("already refreshing token")
                    //access token is getting refreshed so drop this request
                    //throw new InvalidTokenException();
                }
                //we are dropping every request
                throw InvalidTokenException()
            }
        }
    
        @Provides
        @LmsAndroidApplicationScope
        fun cache(cacheFile: File): Cache {
            return Cache(cacheFile, 10 * 1000 * 1000)
        }
    
        @Provides
        @LmsAndroidApplicationScope
        fun file(context: Context): File{
            return File(context.cacheDir, "okhttp_cache")
        }
    
    }
    

    而NetworkModule需要依赖于 上下文模块.kt

    package com.satincreditcare.satin.android.dagger.module
    
    import android.content.Context
    import com.satincreditcare.satin.android.dagger.scope.LmsAndroidApplicationScope
    import dagger.Module
    import dagger.Provides
    
    /**
     * Created by Abhishek Jha on 1/30/2019.
     */
    
    @Module
    open class ContextModule(context: Context) {
        private val mContext: Context = context
    
        @Provides
        @LmsAndroidApplicationScope
        fun context(): Context{
            return mContext
        }
    
    }
    

    和RepositoryModule.kt

    package com.satincreditcare.satin.android.dagger.module
    
    import android.arch.lifecycle.ViewModelProvider
    import android.content.Context
    import com.satincreditcare.satin.android.mvvm.data.AppRepository
    import com.satincreditcare.satin.android.mvvm.viewmodel.CustomViewModelFactory
    import com.satincreditcare.satin.android.mvvm.viewmodel.CustomViewModelFactoryAuth
    import com.satincreditcare.satin.android.network.rest_controller.RestInterface
    import com.satincreditcare.satin.android.dagger.qualifier.LMSApplicationQualifier
    import com.satincreditcare.satin.android.dagger.qualifier.LMSAuthQualifier
    import com.satincreditcare.satin.android.dagger.scope.LmsAndroidApplicationScope
    import dagger.Module
    import dagger.Provides
    
    /**
     * Created by Abhishek Jha on 1/30/2019.
     */
    @Module(includes = [(RetrofitServiceModule::class), (ContextModule::class)])
    open class RepositoryModule {
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSApplicationQualifier
        fun appRepository(@LMSApplicationQualifier restInterface: RestInterface, context: Context): AppRepository{
            return AppRepository(restInterface, context)
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSApplicationQualifier
        fun provideViewModelFactory(@LMSApplicationQualifier appRepository: AppRepository): ViewModelProvider.Factory{
            return CustomViewModelFactory(appRepository)
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSAuthQualifier
        fun appRepositoryAuth(@LMSAuthQualifier restInterface: RestInterface, context: Context): AppRepository{
            return AppRepository(restInterface, context)
        }
    
        @Provides
        @LmsAndroidApplicationScope
        @LMSAuthQualifier
        fun provideViewModelFactoryAuth(@LMSAuthQualifier appRepository: AppRepository): ViewModelProvider.Factory{
            return CustomViewModelFactoryAuth(appRepository)
        }
    
    }
    

    因此,当我在应用程序类中获取ApplicationComponent时:

    public class LmsAndroidApplication extends Application {
        public static String TAG = BuildConfig.VERSION_NAME + AppConstants.EMPTY_SPACE;
    
        public LmsApplicationComponent component;
    
        public static LmsAndroidApplication get(Activity activity){
            return (LmsAndroidApplication) activity.getApplication();
        }
    
        public LmsApplicationComponent component(){
            return component;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            //Leak Canary should first be installed.
            /*if(LeakCanary.isInAnalyzerProcess(this)){
                // This process is dedicated to LeakCanary for heap analysis.
                // You should not init your app in this process.
                return;
            }
            LeakCanary.install(this);*/
            //Dagger Basic setup for Api and repository setup
            if(BuildConfig.DEBUG){
                Timber.plant(new Timber.DebugTree());
            }
            component = DaggerLmsApplicationComponent
                    .builder()
                    .contextModule(new ContextModule(this))
                    .build();
    }
    }
    

    我在片段类中访问ApplicationComponent的方法如下:

    @Inject
    @LMSApplicationQualifier
    lateinit var viewModelFactory: ViewModelProvider.Factory
    
    var lmsApplicationComponent: LmsApplicationComponent = LmsAndroidApplication.get(activity).component()
    
    viewModelFactory = lmsApplicationComponent.provideViewModelFactory()
    

    我访问它没有任何问题,但当我试图通过Fragment的组件访问同一个对象时,它会给出一个丢失的绑定错误。请找到片段的组件代码pendpycomponent.kt

    package com.satincreditcare.satin.android.dagger.component
    
    import com.satincreditcare.satin.android.dagger.module.PendPsychoModule
    import com.satincreditcare.satin.android.dagger.scope.PendPsychoScope
    import com.satincreditcare.satin.android.pendPsychoMvvm.PendPsychoFragment
    import dagger.Component
    
    /**
     * Created by Abhishek Jha on 1/30/2019.
     */
    @PendPsychoScope
    @Component(dependencies = [(LmsApplicationComponent::class)], modules = [(PendPsychoModule::class)])
    interface PendPsychoComponent {
    
        fun injectPendingPsycho(pendPsychoFragment: PendPsychoFragment)
    
    }
    

    如您所见,我已经用AppComponent的值添加了dependencies标记,但是我无法通过这种方式访问LmsApplicationComponent的方法,但是当我在Java中使用时,我能够访问它。请帮我解决这个问题。!!提前谢谢。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Ismael Di Vita    6 年前

    您需要构建片段组件并将应用程序组件用作依赖项。而且,您不需要为 viewModelFactory 匕首会帮你的。

    像这样的:

    DaggerPendPsychoComponent.builder()
                .lmsApplicationComponent(LmsAndroidApplication.get(activity).component())
                .pendPsychoModule(PendPsychoModule())
                .build()
                .inject(this)