إدخال التبعية أو الخدمة يدويًا محددات مواقع الويب في تطبيق Android يمكن أن تكون مشكلة حسب حجم مشروعك. يمكنك الحد من تعقيد مشروعك أثناء زيادة حجمه باستخدام Dagger لإدارة التبعيات:
ينشئ Dagger تلقائيًا رمزًا يحاكي الرمز الذي كنت تستخدمه بخلاف ذلك. تكون مكتوبة بخط اليد. نظرًا لأنه يتم إنشاء التعليمات البرمجية في وقت التجميع، يمكن تتبعها وأكثر فعالية من الحلول الأخرى القائمة على الانعكاس، مثل Guice.
مزايا استخدام Dagger
يخلصك Dagger من كتابة رمز نموذجي مملّ ومعرض للأخطاء من خلال:
جارٍ إنشاء رمز
AppContainer
(الرسم البياني للتطبيق) الذي تريده يدويًا تنفيذها في قسم DI اليدوي.إنشاء مصانع للفئات المتاحة في الرسم البياني للتطبيقات. هذا النمط هو كيفية إرضاء التبعيات داخليًا.
يُعد تحديد ما إذا كان سيتم إعادة استخدام تبعية أو إنشاء مثيل جديد من خلال استخدام النطاقات.
يمكنك إنشاء حاويات لتدفقات معيّنة كما فعلت مع تدفق تسجيل الدخول في القسم السابق باستخدام المكوّنات الفرعية لأداة Dagger. ويعمل ذلك على تحسين الأداء من خلال تحرير الكائنات في الذاكرة عندما لا تكون هناك حاجة إليها.
تفعل Dagger كل هذا تلقائيًا في وقت التصميم طالما أنك إعلان تبعيات إحدى الفئات وتحديد كيفية تلبيتها باستخدام التعليقات التوضيحية. ينشئ Dagger رمزًا برمجيًا مشابهًا لما كنت ستكتبه. يدويًا. داخليًا، ينشئ Dagger رسمًا بيانيًا للكائنات التي يمكنه الرجوع إليها لإيجاد طريقة لتوفير مثيل لأي فئة. لكل فئة في الرسم البياني، ينشئ Dagger فئة من نوع المصنع تستخدمها. داخليًا للحصول على مثيلات من هذا النوع.
في وقت التصميم، يرشدك Dagger إلى التعليمات البرمجية الخاصة بك، بالإضافة إلى:
ينشئ الرسوم البيانية للتبعية ويتحقق من صحتها، ويضمن ما يلي:
- يمكن تلبية تبعيات كل كائن، وبالتالي لا يكون هناك بيئة تشغيل والاستثناءات.
- ليس هناك دورات تبعية، لذلك لا توجد حلقات لانهائية.
إنشاء الفئات التي يتم استخدامها في وقت التشغيل لإنشاء الكائنات الفعلية وتبعياتها.
حالة استخدام بسيطة في Dagger: إنشاء مصنع
لتوضيح كيف يمكنك العمل باستخدام Dagger، لننشئ مخططًا بسيطًا
فاكتوري للفئة UserRepository
المعروضة في
الرسم التخطيطي التالي:
تعريف UserRepository
على النحو التالي:
Kotlin
class UserRepository( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) { ... }
Java
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
:
Kotlin
// @Inject lets Dagger know how to create instances of this object class UserRepository @Inject constructor( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) { ... }
Java
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 كيفية إنشائها:
Kotlin
// @Inject lets Dagger know how to create instances of these objects class UserLocalDataSource @Inject constructor() { ... } class UserRemoteDataSource @Inject constructor() { ... }
Java
public class UserLocalDataSource { @Inject public UserLocalDataSource() { } } public class UserRemoteDataSource { @Inject public UserRemoteDataSource() { } }
مكوّنات Dagger
يمكن أن ينشئ Dagger رسمًا بيانيًا للتبعيات في مشروعك يمكنه
استخدامها لمعرفة أين يجب أن تحصل على تلك التبعيات عندما تكون هناك حاجة إليها.
لتنفيذ ذلك على Dagger، عليك إنشاء واجهة وإضافة تعليقات توضيحية إليها باستخدام
@Component
ينشئ Dagger حاوية كما تفعل عادةً باستخدام دليل
حقن التبعية.
داخل الواجهة @Component
، يمكنك تحديد دوال تعرض
الافتراضية للفصول التي تحتاج إليها (أي UserRepository
). يقول @Component
:
Dagger لإنشاء حاوية بجميع التبعيات المطلوبة لتلبية
التي يعرضها. وهذا ما يُسمّى مكون Dagger؛ تحتوي على
رسم بياني يتكون من الكائنات التي يعرف Dagger كيفية
تقدمهم وتبعياتهم الخاصة.
Kotlin
// @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 }
Java
// @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
. يمكنك استخدامها على النحو التالي:
Kotlin
// 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()
Java
// 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
في كل مرة يتم فيها طلبه.
Kotlin
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create() val userRepository: UserRepository = applicationGraph.repository() val userRepository2: UserRepository = applicationGraph.repository() assert(userRepository != userRepository2)
Java
ApplicationGraph applicationGraph = DaggerApplicationGraph.create(); UserRepository userRepository = applicationGraph.userRepository(); UserRepository userRepository2 = applicationGraph.userRepository(); assert(userRepository != userRepository2)
في بعض الأحيان، تحتاج إلى مثيل فريد لتبعية في الحاوية. قد تحتاج إلى هذا لعدة أسباب:
فأنت تريد أن تشارك الأنواع الأخرى التي لها هذا النوع كتبعية نفس مثل عدة كائنات
ViewModel
خلال مسار تسجيل الدخول باستخدامLoginUserData
يعد إنشاء الكائن أمرًا مكلفًا ولا تريد إنشاء كائن جديد في كل مرة يتم تعريفها كتبعية (على سبيل المثال، محلل JSON).
في المثال، قد ترغب في الحصول على مثيل فريد لـ UserRepository
.
المتاحة في الرسم البياني بحيث في كل مرة تطلب فيها UserRepository
،
نفس المثيل دائمًا. يكون هذا مفيدًا في مثالك لأنه في
تطبيق واقعي باستخدام رسم بياني أكثر تعقيدًا للتطبيق، فربما يكون لديك
عدة كائنات ViewModel
بناءً على UserRepository
ولا تريده
لإنشاء مثيلات جديدة من UserLocalDataSource
وUserRemoteDataSource
في كل مرة يجب فيها تقديم UserRepository
.
وفي إدخال التبعية اليدوية، يمكنك إجراء ذلك من خلال تمرير
مثال UserRepository
لدوال الإنشاء لفئات ViewModel، لكن
في Dagger، لأنك لا تكتب هذه التعليمة البرمجية يدويًا، فيجب عليك السماح
يعرف Dagger أنك تريد استخدام المثيل نفسه. يمكن إجراء ذلك باستخدام النطاق
التعليقات التوضيحية.
تحديد النطاق باستخدام Dagger
يمكنك استخدام التعليقات التوضيحية للنطاق لقصر مدة بقاء الكائن على الفترة منذ الإنشاء. مكونها. هذا يعني أنه يتم استخدام نفس مثيل التبعية في كل مرة يجب فيها توفير هذا النوع.
الحصول على مثيل فريد من UserRepository
عند طلب المستودع
في ApplicationGraph
، استخدِم التعليق التوضيحي نفسه للنطاق في @Component
وUserRepository
. يمكنك استخدام تعليق @Singleton
التوضيحي الذي
يتضمّن حزمة javax.inject
التي تستخدمها Dagger:
Kotlin
// 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 ) { ... }
Java
// 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; } }
يمكنك بدلاً من ذلك إنشاء تعليق توضيحي للنطاق المخصّص واستخدامه. يمكنك إنشاء تعليق توضيحي للنطاق على النحو التالي:
Kotlin
// Creates MyCustomScope @Scope @MustBeDocumented @Retention(value = AnnotationRetention.RUNTIME) annotation class MyCustomScope
Java
// Creates MyCustomScope @Scope @Retention(RetentionPolicy.RUNTIME) public @interface MyCustomScope {}
بعد ذلك، يمكنك استخدامه على النحو التالي:
Kotlin
@MyCustomScope @Component interface ApplicationGraph { fun repository(): UserRepository } @MyCustomScope class UserRepository @Inject constructor( private val localDataSource: UserLocalDataSource, private val service: UserService ) { ... }
Java
@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
Kotlin
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create() val userRepository: UserRepository = applicationGraph.repository() val userRepository2: UserRepository = applicationGraph.repository() assert(userRepository == userRepository2)
Java
ApplicationGraph applicationGraph = DaggerApplicationGraph.create(); UserRepository userRepository = applicationGraph.userRepository(); UserRepository userRepository2 = applicationGraph.userRepository(); assert(userRepository == userRepository2)
الخاتمة
من المهم أن تكون على دراية بمزايا Dagger وأساسيات كيفية عملها قبل استخدامها في سيناريوهات أكثر تعقيدًا.
في الصفحة التالية، ستتعرف على كيفية إضافة Dagger إلى تطبيق Android.