استفاده از Dagger hilt به صورت پروژه محور (قسمت سوم)

قسمت های قبلی:

https://vrgl.ir/ez4b2


https://vrgl.ir/u5BLy




در قسمت دوم، یک ماژول تعریف کردیم که قراره داخل این قسمت، وابستگی هارو بهش اضافه کنیم.


برای شروع، بیس یو ار ال ها رو تامین می کنیم:

@Provides
@Singleton
fun provideIceAndFireBaseURL(): String = &quothttps://www.anapioficeandfire.com/&quot

@Provides
@Singleton
fun provideQuotesBaseURL(): String = &quothttps://got-quotes.herokuapp.com/&quot

خب الان ی مشکلی پیش میاد ? چون که از ما از دو وب سرویس متفاوت استفاده می کنیم و اینکه هر دو تامین کننده، ی نوع داده رو بازگشت میدن و باید به دگر بفمهمونیم که کجا از کدوم بیس یو ار ال استفاده کنه! اینجاست که ما از Qualifier استفاده می کنیم.

در پوشه di-> annotation یک فایل به نام Annotations ایجاد می کنیم:

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IceAndFire

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Quotes


حالا انوتیشن IceAndFire و Quotes رو به بیس یو ار ال های خودمون اضافه می کنیم:

@IceAndFire
@Provides
@Singleton
fun provideIceAndFireBaseURL(): String = &quothttps://www.anapioficeandfire.com/&quot

@Quotes
@Provides
@Singleton
fun provideQuotesBaseURL(): String = &quothttps://got-quotes.herokuapp.com/&quot

برای مکانیزم کش و کلاینت کاستوم شده okHttp، به کلاس NetworkHelper نیاز داریم، و کلاس NetworkHelper هم برای کار، به Context نیاز داره! به این صورت تعریفش می کنیم:

@Provides
@Singleton
fun provideNetworkHelper(@ApplicationContext context: Context): NetworkHelper = NetworkHelper(context)

از انوتیشن ApplicationContext برای تزریق Context به NetworkHelper استفاده می کنیم.

حالا کلاینت OkHttp رو تعریف می کنیم:

@Provides
@Singleton
fun provideOkHttpClient(@ApplicationContext context: Context, networkHelper: NetworkHelper): OkHttpClient {

    val cacheSize = (5 * 1024 * 1024).toLong()
    val myCache = Cache(context.cacheDir, cacheSize)

    return OkHttpClient.Builder()
        .cache(myCache)
        .addInterceptor { chain ->
            var request = chain.request()
            request = if (networkHelper.isNetworkConnected())
                request.newBuilder().header(&quotCache-Control&quot, &quotpublic, max-age=&quot + 5).build()
            else
                request.newBuilder().header(&quotCache-Control&quot, &quotpublic, only-if-cached, max-stale=&quot + 60 * 60 * 24 * 7).build()
            chain.proceed(request)
        }
        .build()
}

دوباره برای تزریق Context از ApplicationContext استفاده می کنیم.

حالا برای تامین Retrofit، چون دو بیس یو ار ال داریم، پس باید دو آبجکت از رتروفیت تعریف کنیم:

@IceAndFire
@Provides
@Singleton
fun provideIceAndFireRetrofit(client: OkHttpClient, @IceAndFire baseUrl: String): Retrofit {
    return Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl(baseUrl)
        .client(client)
        .build()
}

@Quotes
@Provides
@Singleton
fun provideQuotesRetrofit(client: OkHttpClient, @Quotes baseUrl: String): Retrofit {
    return Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl(baseUrl)
        .client(client)
        .build()
}

در اینجا دوباره از انوتیشن IceAndFire و Quotes استفاده کردیم که به دگر بفهمونیم در کجا از کدومش استفاده می کنیم! اگه دقت کرده باشید برای پارامتر بیس یو ار ال در هر دو فانکشن، مشخص کردیم که کدوم بیس یو ار ال رو میخوایم.


حالا نوبت میرسه به تامین کلاس های نتورک و ApiHelper های اپلیکیشنه:

@Provides
@Singleton
fun provideIceAndFireApiService(@IceAndFire retrofit: Retrofit) = retrofit.create(IceAndFireApiService::class.java)

@Provides
@Singleton
fun provideIceAndFireApiHelper(@Quotes retrofit: Retrofit) = retrofit.create(QuotesApiService::class.java)


@Provides
@Singleton
fun provideQuotesApiService(apiImpl: IceAndFireApiImpl): IceAndFireApiHelper = apiImpl

@Provides
@Singleton
fun provideQuotesApiHelper(apiImpl: QuotesApiImpl): QuotesApiHelper = apiImpl


خب، ماژول ما تموم شد و کد کامل شده:

@Module
@InstallIn(ApplicationComponent::class)
class IceAndFireModule {

    @IceAndFire
    @Provides
    @Singleton
    fun provideIceAndFireBaseURL(): String = &quothttps://www.anapioficeandfire.com/&quot

    @Quotes
    @Provides
    @Singleton
    fun provideQuotesBaseURL(): String = &quothttps://got-quotes.herokuapp.com/&quot

    @Provides
    @Singleton
    fun provideNetworkHelper(@ApplicationContext context: Context): NetworkHelper = NetworkHelper(context)

    @Provides
    @Singleton
    fun provideOkHttpClient(@ApplicationContext context: Context, networkHelper: NetworkHelper): OkHttpClient {

        val cacheSize = (5 * 1024 * 1024).toLong()
        val myCache = Cache(context.cacheDir, cacheSize)

        return OkHttpClient.Builder()
            .cache(myCache)
            .addInterceptor { chain ->
                var request = chain.request()
                request = if (networkHelper.isNetworkConnected())
                    request.newBuilder().header(&quotCache-Control&quot, &quotpublic, max-age=&quot + 5).build()
                else
                    request.newBuilder().header(&quotCache-Control&quot, &quotpublic, only-if-cached, max-stale=&quot + 60 * 60 * 24 * 7).build()
                chain.proceed(request)
            }
            .build()
    }

    @IceAndFire
    @Provides
    @Singleton
    fun provideIceAndFireRetrofit(client: OkHttpClient, @IceAndFire baseUrl: String): Retrofit {
        return Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl(baseUrl)
            .client(client)
            .build()
    }
    @Quotes
    @Provides
    @Singleton
    fun provideQuotesRetrofit(client: OkHttpClient, @Quotes baseUrl: String): Retrofit {
        return Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl(baseUrl)
            .client(client)
            .build()
    }


    @Provides
    @Singleton
    fun provideIceAndFireApiService(@IceAndFire retrofit: Retrofit) = retrofit.create(IceAndFireApiService::class.java)

    @Provides
    @Singleton
    fun provideIceAndFireApiHelper(@Quotes retrofit: Retrofit) = retrofit.create(QuotesApiService::class.java)

    @Provides
    @Singleton
    fun provideQuotesApiService(apiImpl: IceAndFireApiImpl): IceAndFireApiHelper = apiImpl

    @Provides
    @Singleton
    fun provideQuotesApiHelper(apiImpl: QuotesApiImpl): QuotesApiHelper = apiImpl
}

و برای تست میریم که از ویومدل داخل اکتیویتی خودمون استفاده کنیم، قبل از هر کاری انوتیشن AndroidEntryPoint رو به اکتیویتی خودمون اضافه میکنیم:

@AndroidEntryPoint
class MainActivity : AppCompatActivity()

با اینکار به دگر اجازه میدیم که وابستگی های درخواستی رو به اکتیویتی تزریق کنه. از انوتیشن AndroidEntryPoint در موارد زیر استفاده میشه:

  • اکتیویتی
  • فرگمنت
  • سرویس
  • ویو
  • برادکست رسیور


برای تزریق ویومدل در داخل اکتیویتی به این صورت عمل می کنیم:

private val vm by viewModels<MainViewModel>()

و یکی از لایودیتاها رو مشاهده می کنیم:

vm.quote.observe(this, Observer { 
    Log.e(&quotqoute&quot,it.toString())
})


کار ما تمام شد و با موفقیت دگر هیلت رو پیاده سازی کردیم!


سورس کد پروژه در گیتهاب موجوده و در ادامه برای استفاده از ابزارهایی نظیر MotionLayout و... از این کد استفاده می کنیم که به یک اپلیکیشن کامل تبدیل بشه!