اصول اولیه خنجر

تزریق وابستگی دستی یا مکان یاب سرویس در یک برنامه اندرویدی بسته به اندازه پروژه شما می تواند مشکل ساز باشد. می‌توانید با استفاده از 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 می گویید:

  1. نحوه ایجاد یک نمونه UserRepository با سازنده مشروح @Inject .

  2. وابستگی های آن چیست: 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)

گاهی اوقات، شما نیاز دارید که یک نمونه منحصر به فرد از یک وابستگی در یک ظرف داشته باشید. ممکن است به چند دلیل این را بخواهید:

  1. شما می خواهید انواع دیگری که این نوع را به عنوان وابستگی دارند، نمونه مشابهی را به اشتراک بگذارند، مانند چندین شی ViewModel در جریان ورود با استفاده از LoginUserData یکسان.

  2. ایجاد یک شی گران است و شما نمی خواهید هر بار که به عنوان یک وابستگی اعلام می شود یک نمونه جدید ایجاد کنید (مثلاً تجزیه کننده 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 را به یک برنامه اندرویدی خواهید آموخت.