
koinچارچوب تزریق وابستگی است که کاملاً در کاتلین ساخته شده است. در مقایسه باDagger2، می توانیم بسیاری از ویژگی های توسعه دهنده را درKoinپیدا کنیم. منحنی یادگیری برایKoinباDagger2کمتر مقایسه می شود.
بسیاری از ویژگی های قابل بحث وجود دارد. از آنجا کهkoinتازه کار است و هنوز در حال رشد است ، مقایسه با غولDagger ناعادلانه خواهد بود! بیایید جزئیات اجرایKoinرا طی کنیم.
Koinطبق اسناد رسمی ، یک چارچوب تزریق وابستگی سبک و عملگرا برای توسعه دهندگانKotlinاست. فقط با استفاده از functional resolution نوشته شده درKotlinخالص: بدونهproxy، بدونهcode generation، بدونهreflection
جالب به نظر می رسد ، اما آیا واقعاً به یک توسعه دهنده کمک می کند تا با سهولت درbuilding applicationمتمرکز شود.
قبل از ادامه کار ، توضیح خواهیم داد که دقیقاً چگونهDependency Injection (DI)به توسعه کمک می کند.DIباResponsibility Principle وDependency Inversion ارتباط نزدیکی دارد.
- Single Responsibility Principle: کلاس / گروه کلاسها / ماژول در یکsingle programوظیفه خاصی را بر عهده دارد که مشکلsingle problemرا برطرف می کند.
- Dependency Inversion: ماژول های سطح بالا نباید به ماژول های سطح پایین تر بستگی داشته باشند.
اگر این اصول را رعایت کنیم ، برنامه دارای ماژول های کاملاً مشترک است. حالا باید ببینیم چگونه شروع می شود!
وابستگی ها را درbuild.gradleبا نسخه مربوطه اضافه کنید.
dependencies {
//Koin
implementation "org.koin:koin-android:2.1.6"
implementation "org.koin:koin-androidx-viewmodel:2.1.6"
}
کلمات کلیدی و توابع مهم در koin
- startKoin: نمونهKoinApplicationرا ایجاد و ثبت میکند
- modules: ماژول های استفاده شده را اعلام میکند
- androidContext :ازandroid contextاستفاده میکند
- by inject(): اجازه میدهد تاinstanceها را باlazilyبازیابی کنید
- get(): تابعی برای باز یابی مستقیم یکinstance(non lazy)
- koinComponent: برای استفاده از فیچر هایkoin کلاس را با همان نام برچسب بزنید تا بتوانید به توابع آن دسترسی داشته باشید
Koin Scopes
- single: یک شیء پایدار را ایجاد می کند
- factory: هر بار یک شی جدید ایجاد می کند
- scoped: شئ را ایجاد میکند که به طول عمرscopeوابسته است
تمام وابستگی ها باید در کلاسApplicationباstartKoinوproviding Android context(بر اساس مستندات رسمی) شروع شوند.
class MainApplication : Application(){
override fun onCreate() {
super.onCreate()
initKoin()
}
private fun initKoin() {
startKoin {
androidContext(this@MainApplication)
modules(provideDependency())
}
}
//List of Koin dependencies
open fun provideDependency() = appComponent
}
برای شروعKoinباید بهAndroid Contextو لیست ماژول ها توجه کنیم. اکنون یک لیست مشترک برای وابستگی ها ایجاد میکنیم (appComponent). می توانید تمام وابستگی های مورد نیاز را از اینجا اضافه کنید. برای شروع ، بگذارید کلاسNetworkوUtilityرا به عنوان وابستگیهای هدفمند در نظر بگیریم. هنگامی که مراحل و رویهKoinرا درک کردیم ، می توانیم به وابستگی های پیچیده تری برویم. درمجموع در این آموزش وابستگی های زیر را پوشش خواهیم داد.
val appComponent = listOf(
NetworkDependency,
AppUtilDependency,
UseCaseDependency,
viewModelDependency
)
برایNetwork dependencyکلاسNetworkDependency.ktرا ایجاد میکنیم. مطابق گفتهKoin، تمام وابستگی ها در ماژول هایKoinبایدsingleشرح داده شود. برای همین ، بگذارید نحوه ایجاد یک ماژولKoinرا ببینیم. سپس در مورد چگونگی شروع آن درAndroidصحبت خواهیم کرد. ماژولNetwork dependencyمانند زیر خواهد بود.
Network Dependency
val NetworkDependency = module {
single {
Retrofit.Builder().addConverterFactory
(GsonConverterFactory.create
(GsonBuilder().create()))
.addCallAdapterFactory
(CoroutineCallAdapterFactory())
.baseUrl(BuildConfig.BASE_URL).build()
}
single { get<Retrofit>().create(SampleService::class.java)}
}
بگذارید جزئیات را توضیح دهم. همانطور که در بالا نشان داده شده است ، ما حداقل جزئیات موردنیاز نمونهRetrofitرا ایجاد خواهیم کرد. به عنوان مثال ، برای تجزیه و تحلیلresponseباGsonConverterبه مبدلjsonنیاز داریم. بعد بهadapter factoryنیاز داریم. در صورت استفاده از کوروتین ها ، می توانیم ازCoroutineCallAdapterFactoryاستفاده کنیم. اگر ازRxاستفاده می کنید ، می توانید به جای یک کوروتین از RxJava2CallAdapterFactoryاستفاده کنید. بعدی برای پیکربندی اولیه بهbaseUrlمی رود. و سپس ما باید سرویسAPIمربوط بهRetrofitرابنویسیم که به صورت زیر است
interface SampleService{
@GET("/loginapi")
suspend fun getRemoteData(@Query("parameter") param:String)
:DemoDataClass
}
Utility Class
کلاسUtilityرا مانند زیر مینویسیم. در بعضی موارد ، ما به روش های مختلف نیاز بهcontextداریم.
بنابراین برای ایجاد کلاسUtility، ما به عنوان یک ورودی اجباری بهcontextنیاز خواهیم داشت ، به طوری که هر متد قادر به ادامه همین روش خواهد بود. بگذارید ببینیم چگونه این کار را می کنیم
class AppUtility(var context: Context) {
//Use the context object as common reusable throughout methods.
//Utility method which needs context
fun utilityMethodOne(){
}
}
در حال حاضر برای ایجادutility Koin dependency، به روش زیر عمل میکنیم.
این امر ضمن ایجاد وابستگی ،contextرا هم برای کلاس فراهم می کند
koinدر هنگام تنظیمDIمراقب تنظیمcontextخواهد بود. ما لازم نیست که هرcontextرا تنظیم کنیم.
val AppUtilDependency = module {
single { AppUtility(androidContext()) }
}
UI (Base Fragment)
به منظور تزریقAppUtilsدر یک بخش ، می توانیم موارد زیر را انجام دهیم.
class BaseFragment : Fragment(){
//Inject the app utility in base fragment
val mAppUtils: AppUtility by inject()
}
همانطور کهutilityدر BaseFragment موجود است سایرfragmentها میتوانند به متد هایapp utilityدسترسی داشته باشند.
بگذارید به کلاسهایی برویم که تحت کلاسهای مرتبط با رابط کاربری قرار نگیرد. در صورت نیاز به وابستگی در کلاسهای غیرUI، می توانیم ازKoinComponentاستفاده کنیم. (طبق اسنادKoin) می توانید موارد زیر را بیابید.
هنگامی که کلاس خود را به عنوانKoinComponentنشان کردید ، به این موارد دسترسی پیدا خواهید کرد:
by inject():lazy instance ارزیابی شده ازkoin container
get():fetch instance مشتق شده ازkoin container
getProperty()/setProperty() - get/set property
Use Case
برایUseCase(مطابق معماریclean) ، ما می توانیم یکBaseUseCaseاستفاده کنیم کهKoinComponentرا گسترش می دهد.
interface BaseUseCase : KoinComponent
اکنون برایUseCase، این کلاسBaseUseCaseرا گسترش دهید. همانطور که می بینید ، مامیتوانیمUtilityرا به KoinComponentتزریق کینم.
class LandingUseCase : BaseUseCase {
//Utility is available for usage in this class
val mAppUtils: AppUtility by inject()
fun someFunction(){
}
}
ما باید وابستگی بهkoinایجاد کنیم که می تواند به همراه سایر وابستگی ها آغاز شود.
val UseCaseDependency = module {
factory {
LandingUseCase()
}
}
View Model
برایViewmodel، یکBaseViewModel abstractایجاد خواهیم کرد که به گسترشAndroidViewModelوKoinComponentمیپردازد.
abstract class BaseViewModel(appContext:Application)
: AndroidViewModel(appContext), KoinComponent
اکنون ،LandingViewModelما ازBaseViewModelارث بری میکند که برای شروع نیاز بهcontextدارد. از این رو می توانیم مانند زیر LandingViewModelرا ایجاد کنیم.
class LandingViewModel(context:Application) : BaseViewModel(context) {
//Inject use case using Koin
private val landingUseCase : LandingUseCase by inject()
fun someFunction(){
}
}
Landing Use Caseبا همان الگو تزریق می شود. در مرحله بعد ، وابستگیViewModelرا ایجاد کنید که می تواندcontextرا در سازنده بگیرد وVMرا آغاز کند.
val viewModelDependency = module {
viewModel { LandingViewModel(androidApplication()) }
}
با تمام لایه ها و مفاهیم توضیح داده شده ، می توانیم وابستگی های مورد نیاز را درfragmentبنویسیم
class LandingFragment : BaseFragment() {
//View model injection using Koin way
private val viewModel by viewModel<LandingViewModel>()
//Utility is available for usage in this class
val mAppUtils: AppUtility by inject()
override fun onViewCreated(view: View,
savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btnTwo.setOnClickListener {
viewModel.someFunction()
}
}
}
همانطور که مشاهده می کنید ، فراخوانی یک تابع درView Model(استفاده از تزریق وابستگی) ازFragmentبسیار ساده است. و متدViewModelبا استفاده از توابعUse Caseنیز به کار می رود.به همین ترتیب می توانید از دیگر لایه های معماریClean نیز استفاده کنید. ما موارد استفاده از تزریق وابستگی را در هر دو بخشUIو سایر کلاسها که در سناریوهای معمول استفاده می شود را آموزش دادیم امیدواریم که مفید واقع شده باشند.
جدیدترین ویدئوهای آموزشی
در بخش TV باگتو، آموزش های کوتاه و جدید را مشاهده نمایید