:: فقط تا پایان فروردین 👈 ۳۵٪ تخفیف ویژه به همراه هدیه 5 میلیون تومانی
۰ ثانیه
۰ دقیقه
۰ ساعت
۴ دیدگاه نظر AmirHossien Heydari
Koin چیست؟ مقایسه Koin و Dagger
Koin چیست؟ مقایسه Koin و Dagger
در برنامه نویسی اندروید، مدیریت وابستگی‌ها می‌تواند یک نگرانی بزرگ باشد. اگر از Dagger2 در پرژوه‌های خود استفاده کرده باشید، می‌دانید که برای هر پروژه به کد‌های تکراری بسیار زیادی نیاز دارید. با اضافه کردن ویژگی‌های جدید به پروژه، این کدهای تکراری بیشتر  هم می‌شوند و شما مجبور هستید برای هر ماژول جدید، کدهای تکراری بنویسید. Dagger2 برای زبان جاوا نوشته شده است و Koin برای زبان کاتلین. همچنین شما می‌توانید از هر دو کتابخانه در پروژه‌ی کاتلین خود استفاده کنید. اما استفاده از Koin در جاوا می‌تواند برای شما مشکل ساز باشد. یک نکته‌ی قابل توجه در مقایسه Koin و Dagger این است که Dagger2 یک Dependency Injection است، اما Koin یک Service Locator می‌باشد. طی چند سال گذشته، عمدتا از Dagger2 برای تزریق وابستگی استفاده می‌شد. در این مقاله این به مقایسه Koin و Dagger خواهیم پرداخت. با ما همراه باشید.
اگر شما هم به برنامه نویسی اندروید علاقه‌مندید پیشنهاد میکنیم به صفحات دوره های آموزش فلاتر و آموزش اندروید سر بزنید.

Koin چیست؟

Koin یک فریمورک سبک تزریق وابستگی برای توسعه دهندگان کاتلین است. Koin فقط با استفاده از عملکرد کاتلین نوشته شده است: no proxy, no code generation, no reflection.

مقایسه Koin و Dagger

ما به منظور مقایسه Koin و Dagger، بر روی پروژه‌ای تمرکز خواهیم کرد که ابتدا با Dagger2 و سپس با Koin پیاده سازی شده است. معماری این پروژه MVVM است و در آن از Retrofit و LiveData استفاده شده است. این پروژه دارای یک اکتیویتی، چهار فرگمنت، پنج ViewModel، یک Repository و یک web service است، بنابراین در این پروژه فقط پایه‌های اصلی طراحی شده است.
 پکیج DI را در تصویر زیر مشاهده کنید. عکس سمت راست پیاده سازی برای Dagger و عکس سمت راست برای Koin می‌باشد.
مقایسه فایل‌های koin و dagger
همانطور که در تصویر مشاهده می‌کنید، برای استفاده از dagger2 شما به فایل‌ها و کد‌های زیادی نیاز دارید. در صورتی که همین کار با Koin خیلی ساده‌تر شده و فایل‌ها و کدهای بسیار کم‌تری لازم دارد.

مقایسه تعداد خطوط کد در koin و dagger بعد از کامپایل شدن

برای به دست آوردن این اعداد از آمار استفاده کردیم. نتیجه جالب است.
مقایسه تعداد خطوط کد koin و dagger
این تعداد کد قبل و بعد از کامپایل می‌باشد. همانطور که می‌بینید تعداد خط کد ایجاد شده توسط dagger دو برابر بیشتر از Koin است.
مقایسه آماری koin و dagger در گوگل
مقایسه آماری در گوگل را در تصویر بالا مشاهده می‌کنید.
از مشاهدات آماری فوق کاملاً مشخص است که Koin امتیاز بیشتری نسبت به Dagger2 کسب می‌کند.

مقایسه Koin و Dagger در زمان build شدن

مقایسه زمان build در koin و dagger برخی توسعه دهندگان، Koin را بر Dagger2 ترجیح می‌دهند. آن‌ها معتقد هستند که کار با Dagger2 دشوار می‌باشد اما ممکن است این مشکل، یک مسئله‌ی کوچک باشد. Dagger2 به دلیل کدهای زیاد تولید شده، برای توسعه دهندگان مشکل ایجاد می‌کند و رفع این مشکل، یک کابوس است. علاوه بر این، یادگیری Dagger2 برای تازه‌کارها دشوار می‌باشد، بنابراین اگر کسی به پروژه یا تیم شما بپیوندد باید زمان زیادی را صرف یادگیری Dagger2 کند.

چرا از Koin استفاده کنیم؟

  1.  No Reflection
  2. کد اضافی تولید نمی‌کند.
  3. برای انجام یک تزریق ساده نیازی به نوشتن کد زیاد نیست.
  4. ادغام و نگهداری بسیار آسان است.
  5. پروکسی رایگان است.
  6. نیازی به کد تکراری و کار اضافی نیست.
  7. تست با Koin بسیار آسان می‌باشد.

راه‌اندازی Dagger

مورد دیگر راه اندازی این کتابخانه‌ها است. فرض کنید می‌خواهید از dagger و MVVM استفاده کنید. برای راه اندازی این کتابخانه‌ها به این صورت عمل می‌کنیم:
kapt "com.google.dagger:dagger-compiler:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
implementation "com.google.dagger:dagger:$dagger_version"
پس از ایجاد ماژول‌ها و اجزای سازنده، باید Dagger را در کلاس Application یا متد‌های دیگر initialize کنید.
Class MyApplication : Application(), HasActivityInjector { 
  @Inject
  lateinit var dispatchingAndroidInjector:    DispatchingAndroidInjector<Activity>
override fun activityInjector() = dispatchingAndroidInjector
fun initDagger() {
   DaggerAppComponent
      .builder()
      .application(this)
      .build()
      .inject(this)
  }
}

به همین ترتیب، در همه اکتیویتی‌ها یا در BaseActivity، باید HasSupportFragmentInjector را پیاده‌سازی کنیم و DispatchingAndroidInjector را تزریق کنیم.

برای ViewModelها، باید ViewModelFactory را در BaseFragment تزریق کنیم و همچنین Injectable را پیاده سازی کنیم.

برای هر ViewMode Fragment و Activity باید نحوه‌ی تزریق آن‌ها را به DI اطلاع دهیم. همانطور که مشاهده می‌کنید ActivityModule ،FragmentModule و ViewModelModule را داریم.

ActivityModule:

@Module
abstract class ActivityModule {
    @ContributesAndroidInjector(modules = [FragmentModule::class])
    abstract fun contributeMainActivity(): MainActivity
    //Add your other activities here
}

برای فرگمنت‌ها:

@Module
abstract class FragmentModule {
    @ContributesAndroidInjector
    abstract fun contributeLoginFragment(): LoginFragment
    @ContributesAndroidInjector
    abstract fun contributeRegisterFragment(): RegisterFragment
    @ContributesAndroidInjector
    abstract fun contributeStartPageFragment(): StartPageFragment
}
و برای ViewModelها:
@Module
abstract class ViewModelModule {
    @Binds
    abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
    @Binds
    @IntoMap
    @ViewModelKey(loginViewModel::class)
    abstract fun bindLoginFragmentViewModel(loginViewModel: loginViewModel): ViewModel
    @Binds
    @IntoMap
    @ViewModelKey(StartPageViewModel::class)
    abstract fun bindStartPageViewModel(startPageViewModel:  StartPageViewModel): ViewModel
    ......
}
بنابراین شما باید با افزودن فرگمنت‌ها، اکتیویتی‌ها و ViewModelهای جدید به ماژول‌های DI اهمیت دهید، همچنین باید به همه‌ی توسعه دهندگان بگویید تا به این مسئله اهمیت دهند.

راه اندازی Koin

همانطور که می‌دانید ابتدا باید وابستگی Koin را اضافه کنیم.
این خط کد را به Gradle پروژه‌ی خود اضافه کنید.
implementation "org.koin:koin-android-viewmodel:$koin_version"

 شروع به کد نویسی

این نسخه از Koin با ViewModel استفاده می‌شود، وابستگی‌های دیگری نیز برای استفاده از Koin وجود دارد، اما ما می‌خواهیم از آن با MVVM استفاده کنیم.
پس از افزودن وابستگی Koin، ما می‌توانیم اولین ماژول خود را پیاده سازی کنیم، دقیقاً مانند Dagger می‌توانیم همه‌ی ماژول‌ها را در یک فایل جداگانه پیاده سازی کنیم، اما به دلیل سادگی کد، تصمیم گرفتیم همه‌ی ماژول‌ها را در یک فایل پیاده سازی کنیم. بعداً می‌توانید آن‌ها را جدا کنید.
ابتدا باید برخی از نکات مفید درباره‌ی سینتکس koin را بدانیم:
()get: برای حل یک نمونه در یک ماژول Koin، فقط از تابع ()get، به عنوان مولفه‌ی مورد نیاز استفاده کنید. تابع ()get معمولا برای تزریق مقادیر constructor در constructor استفاده می‌شود.
()factory: با هر بار درخواست از مولفه‌ی ()factory، شما نمونه جدیدی را از آن دریافت خواهید کرد.
()single: یک Singleton را برای شما فراهم می‌کند.
name = : برای نامگذاری تعاریف استفاده می‌شود. این مورد زمانی لازم است که بخواهید چندین نمونه از یک کلاس را با انواع مختلفی داشته باشید.
appinjector.kt:
package com.7learn.sevenlearn.di
import com.farshidabz.gettingstartkoin.data.remote.ImagesApi
import org.koin.dsl.module
import retrofit2.Retrofit
private val retrofit: Retrofit = createNetworkClient()
private val IMAGES_API: ImagesApi = retrofit.create(ImagesApi::class.java)
val networkModule = module {
    single { IMAGES_API }
}
NetworkMadule.kt:
package com.7learn.sevenlearn.di
import com.farshidabz.gettingstartkoin.BuildConfig
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
private val sLogLevel =
    if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
private const val baseUrl = "https://www.splashbase.co/api/v1/"
private fun getLogInterceptor() = HttpLoggingInterceptor().apply { level = sLogLevel }
fun createNetworkClient() =
    retrofitClient(baseUrl, okHttpClient(true))
private fun okHttpClient(addAuthHeader: Boolean) = OkHttpClient.Builder()
    .addInterceptor(getLogInterceptor()).apply { setTimeOutToOkHttpClient(this) }
    .addInterceptor(headersInterceptor(addAuthHeader)).build()
private fun retrofitClient(baseUrl: String, httpClient: OkHttpClient): Retrofit =
    Retrofit.Builder()
        .baseUrl(baseUrl)
        .client(httpClient)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
fun headersInterceptor(addAuthHeader: Boolean) = Interceptor { chain ->
    chain.proceed(
        chain.request().newBuilder()
            .addHeader("Content-Type", "application/json")
            .also {
                if (addAuthHeader) {
//                    it.addHeader("Authorization", wrapInBearer(UserInfoPref.bearerToken))
                }
            }
            .build()
    )
}
private fun setTimeOutToOkHttpClient(okHttpClientBuilder: OkHttpClient.Builder) =
    okHttpClientBuilder.apply {
        readTimeout(30L, TimeUnit.SECONDS)
        connectTimeout(30L, TimeUnit.SECONDS)
        writeTimeout(30L, TimeUnit.SECONDS)
    }
RepositoryMadule.kt:
package com.7learn.sevenlearn.di
import com.farshidabz.gettingstartkoin.data.repository.ImagesRepo
import com.farshidabz.gettingstartkoin.utils.LocationHandler
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val repositoryModule = module {
    single { ImagesRepo(androidContext(), imagesApi = get()) }
    single { LocationHandler() }
}
ViewModelMadule.kt:
package com.7learn.sevenlearn.di
import com.farshidabz.gettingstartkoin.view.main.MainActivityViewModel
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.dsl.module
val viewModelModule = module {
    viewModel { MainActivityViewModel(get()) }
}

توضیح کد‌ها:

private val retrofit: Retrofit = createNetworkClient()
createNetworkClient تابعی برای ایجاد نمونه از Retrofit است. تنظیم baseUrl، افزودن ConverterFactory و Interceptor نیز در آنجا انجام می‌شود.
private val generalApi: GeneralApi =  retrofit.create(GeneralApi::class.java)
private val authApi: AuthApi = retrofit.create(AuthApi::class.java)
AuthApi و GeneralApi رابط‌های endpoints هستند. ما فقط endpoint خود را در اینجا اضافه می‌کنیم تا endpoint خود را از هم جدا کنیم.
val viewModelModule = module {
    viewModel { LoginFragmentViewModel(get()) }
    viewModel { StartPageViewModel() }    
}

کلاس viewModels را به عنوان viewModel در یک ماژول تعریف می‌کنیم. Koin یک ViewModel به چرخه‌ی حیات ViewModelFactory می‌دهد و به اتصال آن به current component کمک می‌کند.

همانطور که می‌بینید ما یک متد ()get به سازنده LoginFragmentViewModel داریم. این تابع ساخت نمونه‌ای از LoginFragmentViewModel را تصمیم گیری می‌کند. اینجا AuthRepo است.

در کلاس Application خود در متد ()onCreate کد را بنویسید:
startKoin(this, listOf(repositoryModule, networkModule, viewModelModule))
در اینجا به سادگی متد startKoin را صدا می‌کنیم و در یک context و لیستی از ماژول‌هایی که می‌خواهیم Koin را با آن‌ها initialize کنیم، قرار می‌دهیم.
استفاده از ViewModel بسیار ساده است. در کلاس View خود (Fragment، Activity) این مورد را اضافه کنید:
private val startPageViewModel: StartPageViewModel by viewModel()
با این کد، koin یک شی از StartPageViewModel برای شما ایجاد می‌کند و startPageViewModel را با آن فراخوانی می‌کند. اکنون می‌توانید از ViewModel خود در View استفاده کنید.
جمع بندی:
در این مقاله، به مقایسه Koin و Dagger پرداختیم و آموختیم که koin مزایای خیلی بیشتری نسبت به dagger دارد و کار با آن بسیار ساده‌ است. در صورتی که کار با Dagger بسیار سخت و وقت گیر است. امیدواریم از این مقاله لذت برده باشید. اگر درباره‌ی مقایسه Koin و Dagger سوال و نظری دارید، خوشحال می‌شویم که آن را با ما و کاربران سون لرن به اشتراک بگذارید.
اگر به یادگیری بیشتر در زمینه‌ی برنامه نویسی اندروید علاقه داری، با شرکت در دوره‌ آموزش برنامه نویسی اندروید در کمتر از یکسال به یک توسعه‌دهنده اندروید همه فن حریف تبدیل می‌شوی که آماده‌ی استخدام، دریافت پروژه و حتی پیاده‌سازی اپلیکیشن خودت هستی.
۴ دیدگاه
ما همه سوالات و دیدگاه‌ها رو می‌خونیم و پاسخ میدیم
۱۴ دی ۱۴۰۰، ۰۸:۲۵

تصوریر سمت راست dagger و تصویر سمت راست koin :| سمت راست متعلق به هردوعه :|

پوریا ۱۴ بهمن ۱۳۹۹، ۰۹:۴۱

سلام مقاله عالی بود تشکر از شما

وحید گروسی ۰۵ بهمن ۱۳۹۹، ۱۳:۰۴

سلام وقتتون بخیر ممنون از شما برای تهیه این محتوا اخیرا کتابخانه ای به نام Dagger Hiltمنتشر شده که به مراتب راحت‌تر از این مورد هست لطفاْ این مورد رو هم بررسی کنید

نازنین کریمی مقدم ۰۵ بهمن ۱۳۹۹، ۱۸:۱۷

درود. حتما، یه مقاله مقایسه dagger و hilt هم در تقویم محتوایی آینده مون قرار داره. ممنون که با ما همراه هستید.

  • Koin چیست؟
  • مقایسه Koin و Dagger
  • چرا از Koin استفاده کنیم؟
  • راه اندازی Koin
  •  شروع به کد نویسی
اشتراک گذاری مقاله در :