تزریق وابستگی دستی یا مکان یاب سرویس در یک برنامه اندرویدی بسته به اندازه پروژه شما می تواند مشکل ساز باشد. میتوانید با استفاده از Dagger برای مدیریت وابستگیها، پیچیدگی پروژه خود را با افزایش مقیاس محدود کنید.
Dagger به طور خودکار کدی را تولید می کند که کدی را که در غیر این صورت با دست می نوشتید تقلید می کند. از آنجایی که کد در زمان کامپایل تولید می شود، نسبت به سایر راه حل های مبتنی بر انعکاس مانند Guice قابل ردیابی و کارایی بیشتری است.
مزایای استفاده از Dagger
Dagger شما را از نوشتن کدهای خسته کننده و مستعد خطا در دیگ بخار با استفاده از:
ایجاد کد
AppContainer
(گراف برنامه) که به صورت دستی در بخش DI دستی پیاده سازی کرده اید.ایجاد کارخانه برای کلاس های موجود در نمودار برنامه. اینگونه است که وابستگی ها در داخل ارضا می شوند.
تصمیم گیری در مورد استفاده مجدد از یک وابستگی یا ایجاد یک نمونه جدید از طریق استفاده از دامنه .
ایجاد کانتینرهایی برای جریان های خاص همانطور که با جریان ورود به سیستم در بخش قبل با استفاده از اجزای فرعی Dagger انجام دادید. این کار با رها کردن اشیا در حافظه زمانی که دیگر به آنها نیاز نیست، عملکرد برنامه شما را بهبود می بخشد.
تا زمانی که شما وابستگیهای یک کلاس را اعلام کرده و نحوه برآورده کردن آنها را با استفاده از حاشیهنویسی مشخص کنید، Dagger بهطور خودکار همه این کارها را در زمان ساخت انجام میدهد. Dagger کدی شبیه به آنچه که به صورت دستی می نوشتید تولید می کند. در داخل، Dagger یک نمودار از اشیاء ایجاد می کند که می تواند برای یافتن راهی برای ارائه نمونه ای از یک کلاس به آن ارجاع دهد. برای هر کلاس در نمودار، Dagger یک کلاس کارخانه ای تولید می کند که به صورت داخلی برای دریافت نمونه هایی از آن نوع استفاده می کند.
در زمان ساخت، Dagger از کد شما عبور می کند و:
نمودارهای وابستگی را می سازد و تأیید می کند و اطمینان حاصل می کند که:
- وابستگی های هر شی را می توان برآورده کرد، بنابراین هیچ استثنایی در زمان اجرا وجود ندارد.
- هیچ چرخه وابستگی وجود ندارد، بنابراین هیچ حلقه بی نهایتی وجود ندارد.
کلاس هایی را تولید می کند که در زمان اجرا برای ایجاد اشیاء واقعی و وابستگی های آنها استفاده می شوند.
یک مورد استفاده ساده در Dagger: ایجاد یک کارخانه
برای نشان دادن نحوه کار با Dagger، اجازه دهید یک کارخانه ساده برای کلاس UserRepository
که در نمودار زیر نشان داده شده است ایجاد کنیم:
UserRepository
به صورت زیر تعریف کنید:
کاتلین
class UserRepository( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) { ... }
جاوا
public class UserRepository { private final UserLocalDataSource userLocalDataSource; private final UserRemoteDataSource userRemoteDataSource; public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) { this.userLocalDataSource = userLocalDataSource; this.userRemoteDataSource = userRemoteDataSource; } ... }
یک حاشیه نویسی @Inject
به سازنده UserRepository
اضافه کنید تا Dagger بداند چگونه یک UserRepository
ایجاد کند:
کاتلین
// @Inject lets Dagger know how to create instances of this object class UserRepository @Inject constructor( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) { ... }
جاوا
public class UserRepository { private final UserLocalDataSource userLocalDataSource; private final UserRemoteDataSource userRemoteDataSource; // @Inject lets Dagger know how to create instances of this object @Inject public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) { this.userLocalDataSource = userLocalDataSource; this.userRemoteDataSource = userRemoteDataSource; } }
در قطعه کد بالا، به Dagger می گویید:
نحوه ایجاد یک نمونه
UserRepository
با سازنده مشروح@Inject
.وابستگی های آن چیست:
UserLocalDataSource
وUserRemoteDataSource
.
اکنون Dagger می داند که چگونه یک نمونه از UserRepository
ایجاد کند، اما نمی داند چگونه وابستگی های آن را ایجاد کند. اگر کلاس های دیگر را نیز حاشیه نویسی کنید، Dagger می داند که چگونه آنها را ایجاد کند:
کاتلین
// @Inject lets Dagger know how to create instances of these objects class UserLocalDataSource @Inject constructor() { ... } class UserRemoteDataSource @Inject constructor() { ... }
جاوا
public class UserLocalDataSource { @Inject public UserLocalDataSource() { } } public class UserRemoteDataSource { @Inject public UserRemoteDataSource() { } }
اجزای خنجر
Dagger میتواند نموداری از وابستگیها در پروژه شما ایجاد کند که میتواند از آن برای پیدا کردن این وابستگیها در صورت نیاز استفاده کند. برای اینکه Dagger این کار را انجام دهد، باید یک رابط ایجاد کنید و آن را با @Component
حاشیه نویسی کنید. Dagger مانند تزریق وابستگی دستی یک ظرف ایجاد می کند.
در داخل واسط @Component
، میتوانید توابعی را تعریف کنید که نمونههایی از کلاسهای مورد نیازتان را برمیگرداند (یعنی UserRepository
). @Component
به Dagger میگوید که ظرفی با تمام وابستگیهای مورد نیاز برای برآوردن انواعی که در معرض نمایش قرار میدهد تولید کند. این جزء خنجر نامیده می شود. این شامل یک نمودار است که شامل اشیایی است که Dagger می داند چگونه ارائه کند و وابستگی های مربوط به آنها.
کاتلین
// @Component makes Dagger create a graph of dependencies @Component interface ApplicationGraph { // The return type of functions inside the component interface is // what can be provided from the container fun repository(): UserRepository }
جاوا
// @Component makes Dagger create a graph of dependencies @Component public interface ApplicationGraph { // The return type of functions inside the component interface is // what can be consumed from the graph UserRepository userRepository(); }
وقتی پروژه را میسازید، Dagger پیادهسازی رابط ApplicationGraph
را برای شما ایجاد میکند: DaggerApplicationGraph
. Dagger با پردازشگر حاشیه نویسی خود یک نمودار وابستگی ایجاد می کند که از روابط بین سه کلاس ( UserRepository
، UserLocalDatasource
و UserRemoteDataSource
) با تنها یک نقطه ورودی تشکیل شده است: گرفتن نمونه UserRepository
. می توانید به صورت زیر از آن استفاده کنید:
کاتلین
// Create an instance of the application graph val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create() // Grab an instance of UserRepository from the application graph val userRepository: UserRepository = applicationGraph.repository()
جاوا
// Create an instance of the application graph ApplicationGraph applicationGraph = DaggerApplicationGraph.create(); // Grab an instance of UserRepository from the application graph UserRepository userRepository = applicationGraph.userRepository();
Dagger هر بار که درخواست می شود یک نمونه جدید از UserRepository
ایجاد می کند.
کاتلین
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create() val userRepository: UserRepository = applicationGraph.repository() val userRepository2: UserRepository = applicationGraph.repository() assert(userRepository != userRepository2)
جاوا
ApplicationGraph applicationGraph = DaggerApplicationGraph.create(); UserRepository userRepository = applicationGraph.userRepository(); UserRepository userRepository2 = applicationGraph.userRepository(); assert(userRepository != userRepository2)
گاهی اوقات، شما نیاز دارید که یک نمونه منحصر به فرد از یک وابستگی در یک ظرف داشته باشید. ممکن است به چند دلیل این را بخواهید:
شما می خواهید انواع دیگری که این نوع را به عنوان وابستگی دارند، نمونه مشابهی را به اشتراک بگذارند، مانند چندین شی
ViewModel
در جریان ورود با استفاده ازLoginUserData
یکسان.ایجاد یک شی گران است و شما نمی خواهید هر بار که به عنوان یک وابستگی اعلام می شود یک نمونه جدید ایجاد کنید (مثلاً تجزیه کننده JSON).
در مثال، ممکن است بخواهید یک نمونه منحصر به فرد از UserRepository
در نمودار موجود داشته باشید تا هر بار که یک UserRepository
درخواست میکنید، همیشه همان نمونه را دریافت کنید. این در مثال شما مفید است زیرا در یک برنامه واقعی با یک نمودار برنامه پیچیده تر، ممکن است چندین شی ViewModel
بسته به UserRepository
داشته باشید و نمی خواهید هر بار که نیاز است UserRepository
ارائه شود، نمونه های جدیدی از UserLocalDataSource
و UserRemoteDataSource
ایجاد کنید. .
در تزریق وابستگی دستی، این کار را با ارسال همان نمونه از UserRepository
به سازندههای کلاسهای ViewModel انجام میدهید. اما در Dagger، چون شما آن کد را به صورت دستی نمی نویسید، باید به Dagger اطلاع دهید که می خواهید از همان نمونه استفاده کنید. این را می توان با حاشیه نویسی دامنه انجام داد.
محدوده با خنجر
می توانید از حاشیه نویسی های محدوده استفاده کنید تا طول عمر یک شی را به طول عمر جزء آن محدود کنید. این بدان معنی است که هر بار که نیاز به ارائه آن نوع باشد، از همان نمونه وابستگی استفاده می شود.
برای داشتن یک نمونه منحصر به فرد از یک UserRepository
هنگامی که شما برای مخزن در ApplicationGraph
درخواست می کنید، از همان حاشیه نویسی محدوده برای واسط @Component
و UserRepository
استفاده کنید. می توانید از حاشیه نویسی @Singleton
استفاده کنید که قبلاً با بسته javax.inject
که Dagger استفاده می کند ارائه شده است:
کاتلین
// Scope annotations on a @Component interface informs Dagger that classes annotated // with this annotation (i.e. @Singleton) are bound to the life of the graph and so // the same instance of that type is provided every time the type is requested. @Singleton @Component interface ApplicationGraph { fun repository(): UserRepository } // Scope this class to a component using @Singleton scope (i.e. ApplicationGraph) @Singleton class UserRepository @Inject constructor( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) { ... }
جاوا
// Scope annotations on a @Component interface informs Dagger that classes annotated // with this annotation (i.e. @Singleton) are scoped to the graph and the same // instance of that type is provided every time the type is requested. @Singleton @Component public interface ApplicationGraph { UserRepository userRepository(); } // Scope this class to a component using @Singleton scope (i.e. ApplicationGraph) @Singleton public class UserRepository { private final UserLocalDataSource userLocalDataSource; private final UserRemoteDataSource userRemoteDataSource; @Inject public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) { this.userLocalDataSource = userLocalDataSource; this.userRemoteDataSource = userRemoteDataSource; } }
از طرف دیگر، می توانید یک حاشیه نویسی دامنه سفارشی ایجاد و استفاده کنید. شما می توانید یک حاشیه نویسی دامنه به صورت زیر ایجاد کنید:
کاتلین
// Creates MyCustomScope @Scope @MustBeDocumented @Retention(value = AnnotationRetention.RUNTIME) annotation class MyCustomScope
جاوا
// Creates MyCustomScope @Scope @Retention(RetentionPolicy.RUNTIME) public @interface MyCustomScope {}
سپس می توانید مانند قبل از آن استفاده کنید:
کاتلین
@MyCustomScope @Component interface ApplicationGraph { fun repository(): UserRepository } @MyCustomScope class UserRepository @Inject constructor( private val localDataSource: UserLocalDataSource, private val service: UserService ) { ... }
جاوا
@MyCustomScope @Component public interface ApplicationGraph { UserRepository userRepository(); } @MyCustomScope public class UserRepository { private final UserLocalDataSource userLocalDataSource; private final UserRemoteDataSource userRemoteDataSource; @Inject public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) { this.userLocalDataSource = userLocalDataSource; this.userRemoteDataSource = userRemoteDataSource; } }
در هر دو مورد، شی با همان محدوده مورد استفاده برای حاشیه نویسی رابط @Component
ارائه می شود. بنابراین، هر بار که applicationGraph.repository()
را فرا میخوانید، همان نمونه UserRepository
را دریافت میکنید.
کاتلین
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create() val userRepository: UserRepository = applicationGraph.repository() val userRepository2: UserRepository = applicationGraph.repository() assert(userRepository == userRepository2)
جاوا
ApplicationGraph applicationGraph = DaggerApplicationGraph.create(); UserRepository userRepository = applicationGraph.userRepository(); UserRepository userRepository2 = applicationGraph.userRepository(); assert(userRepository == userRepository2)
نتیجه گیری
قبل از اینکه بتوانید از آن در سناریوهای پیچیده تر استفاده کنید، مهم است که از مزایای Dagger و اصول اولیه نحوه عملکرد آن آگاه باشید.
در صفحه بعد ، نحوه اضافه کردن Dagger را به یک برنامه اندرویدی خواهید آموخت.