استفاده کردن از Dagger در برنامه‌های Android

صفحه مبانی Dagger توضیح می‌دهد که چگونه Dagger می‌تواند به شما کمک کند تا تزریق وابستگی را در برنامه‌تان خودکار کنید. با Dagger، نیازی به نوشتن کد استاندارد خسته‌کننده و مستعد خطا ندارید.

خلاصه روال‌های مطلوب

  • هرزمان ممکن بود، از تزریق سازنده با @Inject برای افزودن انواع به نمودار Dagger استفاده کنید. وقتی این‌طور نیست:
    • از @Binds استفاده کنید تا به Dagger بگویید میانجی باید کدام پیاده‌سازی را داشته باشد.
    • از @Provides استفاده کنید تا به Dagger بگویید چگونه کلاس‌هایی را که پروژه شما مالک آن‌ها نیست ارائه دهد.
  • باید واحدها را فقط یک‌بار در مؤلفه اعلام کنید.
  • حاشیه‌نویسی‌های دامنه را براساس طول عمر جایی که از حاشیه‌نویسی استفاده می‌شود نام‌گذاری کنید. نمونه‌های آن عبارت‌اند از @ApplicationScope، @LoggedUserScope، و @ActivityScope.

درحال افزودن وابستگی‌ها

برای استفاده از Dagger در پروژه‌تان، این وابستگی‌ها را در فایل build.gradle به برنامه‌تان اضافه کنید. می‌توانید جدیدترین نسخه Dagger را در این پروژه GitHub پیدا کنید.

کاتلین

plugins {
  id 'kotlin-kapt'
}

dependencies {
    implementation 'com.google.dagger:dagger:2.x'
    kapt 'com.google.dagger:dagger-compiler:2.x'
}

جاوا

dependencies {
    implementation 'com.google.dagger:dagger:2.x'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}

Dagger in Android

یک برنامه Android نمونه را با نمودار وابستگی از شکل ۱ درنظر بگیرید.

LoginActivity به LoginViewModel وابسته است، که به UserRepository وابسته است،
  که به UserLocalDataSource و UserRemoteDataSource وابسته است، که به‌نوبه خود
  به Retrofit وابسته است.

شکل ۱. نمودار وابستگی کد نمونه

در Android، معمولاً نمودار Dagger ایجاد می‌کنید که در کلاس برنامه شما قرار دارد زیرا می‌خواهید تا زمانی که برنامه درحال اجرا است، نمونه‌ای از نمودار در حافظه باشد. به این ترتیب، نمودار به چرخه حیات برنامه پیوست می‌شود. در برخی موارد، ممکن است بخواهید بافت برنامه نیز در نمودار دردسترس باشد. برای این کار، باید نمودار در کلاس Application باشد. یکی از مزایای این رویکرد این است که نمودار برای دیگر کلاس‌های چارچوب Android دردسترس است. علاوه‌براین، با امکان استفاده از کلاس Application سفارشی در آزمایش‌ها، آزمایش کردن را ساده می‌کند.

چون میانایی که نمودار را تولید می‌کند با @Component حاشیه‌نویسی شده است، می‌توانید آن را ApplicationComponent یا ApplicationGraph بنامید. معمولاً نمونه‌ای از آن مؤلفه را در کلاس سفارشی Application خود نگه می‌دارید و هربار که به نمودار برنامه نیاز دارید، آن را فراخوانی می‌کنید، همان‌طور که در قطعه کد زیر نشان داده شده است:

کاتلین

// Definition of the Application graph
@Component
interface ApplicationComponent { ... }

// appComponent lives in the Application class to share its lifecycle
class MyApplication: Application() {
    // Reference to the application graph that is used across the whole app
    val appComponent = DaggerApplicationComponent.create()
}

جاوا

// Definition of the Application graph
@Component
public interface ApplicationComponent {
}

// appComponent lives in the Application class to share its lifecycle
public class MyApplication extends Application {

    // Reference to the application graph that is used across the whole app
    ApplicationComponent appComponent = DaggerApplicationComponent.create();
}

چون کلاس‌های چارچوب Android خاصی مثل فعالیت‌ها و قطعه‌ها توسط سیستم نمونه‌سازی می‌شوند، Dagger نمی‌تواند آن‌ها را برای شما ایجاد کند. به‌طور خاص برای فعالیت‌ها، هر کد مقداردهی اولیه باید در روش onCreate() قرار گیرد. این یعنی نمی‌توانید از حاشیه‌نویسی @Inject در سازنده کلاس (تزریق سازنده) استفاده کنید، همان‌طور که در مثال‌های قبلی انجام دادید. درعوض، باید از تزریق فیلد استفاده کنید.

به‌جای ایجاد وابستگی‌هایی که یک فعالیت در روش onCreate() نیاز دارد، می‌خواهید Dagger آن وابستگی‌ها را برای شما پر کند. برای تزریق فیلد، به‌جای آن حاشیه‌نویسی @Inject را روی فیلدهایی که می‌خواهید از نمودار Dagger دریافت کنید اعمال می‌کنید.

کاتلین

class LoginActivity: Activity() {
    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject lateinit var loginViewModel: LoginViewModel
}

جاوا

public class LoginActivity extends Activity {

    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject
    LoginViewModel loginViewModel;
}

برای سادگی، LoginViewModel یک ViewModel مؤلفه‌های معماری Android نیست؛ بلکه فقط یک کلاس معمولی است که به‌عنوان ViewModel عمل می‌کند. برای اطلاعات بیشتر درباره نحوه تزریق این کلاس‌ها، کد را در پیاده‌سازی رسمی Android Blueprints Dagger، در شاخه dev-dagger بررسی کنید.

یکی از ملاحظات مربوط به Dagger این است که فیلدهای تزریق‌شده نمی‌توانند خصوصی باشند. آن‌ها باید حداقل قابلیت رؤیت package-private داشته باشند، مانند کد قبلی.

درحال تزریق فعالیت‌ها

Dagger باید بداند که LoginActivity باید به نمودار دسترسی داشته باشد تا بتواند ViewModel موردنیازش را ارائه دهد. در صفحه مبانی Dagger، از میانای @Component برای دریافت اشیاء از نمودار استفاده کردید. این کار را با آشکار کردن توابع با نوع پاسخ آنچه می‌خواهید از نمودار دریافت کنید انجام دادید. دراین‌صورت، باید به Dagger درباره شیئی (LoginActivity دراین‌صورت) که به تزریق وابستگی نیاز دارد اطلاع دهید. برای این کار، تابعی را نشان می‌دهید که شیء درخواست‌کننده تزریق را به‌عنوان پارامتر می‌گیرد.

کاتلین

@Component
interface ApplicationComponent {
    // This tells Dagger that LoginActivity requests injection so the graph needs to
    // satisfy all the dependencies of the fields that LoginActivity is requesting.
    fun inject(activity: LoginActivity)
}

جاوا

@Component
public interface ApplicationComponent {
    // This tells Dagger that LoginActivity requests injection so the graph needs to
    // satisfy all the dependencies of the fields that LoginActivity is injecting.
    void inject(LoginActivity loginActivity);
}

این تابع به Dagger می‌گوید که LoginActivity می‌خواهد به نمودار دسترسی داشته باشد و درخواست تزریق می‌کند. Dagger باید همه وابستگی‌هایی را که LoginActivity نیاز دارد (LoginViewModel با وابستگی‌های خودش) برآورده کند. اگر چند کلاس دارید که تزریق را درخواست می‌کنند، باید همه آن‌ها را به‌طور مشخص با نوع دقیقشان در مؤلفه اعلام کنید. برای مثال، اگر LoginActivity و RegistrationActivity درخواست تزریق داشته باشید، دو روش inject() خواهید داشت به‌جای یک روش عمومی که هر دو مورد را پوشش دهد. یک روش عمومی inject() به Dagger نمی‌گوید چه چیزی باید ارائه شود. توابع در میانای کاربری می‌توانند هر نامی داشته باشند، اما فراخوانی آن‌ها با نام inject() وقتی که شیء را برای تزریق به‌عنوان پارامتر دریافت می‌کنند، یک قرارداد در Dagger است.

برای تزریق شیء در فعالیت، از appComponent تعریف‌شده در کلاس Application استفاده می‌کنید و متد inject() را فراخوانی می‌کنید و نمونه‌ای از فعالیت را که درخواست تزریق می‌کند ارسال می‌کنید.

هنگام استفاده از فعالیت‌ها، Dagger را در روش onCreate() فعالیت قبل‌از فراخوانی super.onCreate() تزریق کنید تا از بروز مشکلات مربوط به بازیابی قطعه جلوگیری شود. درطول مرحله بازیابی در super.onCreate()، فعالیتی قطعاتی را پیوست می‌کند که ممکن است بخواهند به اتصال‌های فعالیت دسترسی پیدا کنند.

هنگام استفاده از قطعه‌ها، Dagger را در روش onAttach() قطعه تزریق کنید. در این مورد، این کار را می‌توان قبل یا بعداز تماس با super.onAttach() انجام داد.

کاتلین

class LoginActivity: Activity() {
    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // Make Dagger instantiate @Inject fields in LoginActivity
        (applicationContext as MyApplication).appComponent.inject(this)
        // Now loginViewModel is available

        super.onCreate(savedInstanceState)
    }
}

// @Inject tells Dagger how to create instances of LoginViewModel
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

جاوا

public class LoginActivity extends Activity {

    // You want Dagger to provide an instance of LoginViewModel from the graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Make Dagger instantiate @Inject fields in LoginActivity
        ((MyApplication) getApplicationContext()).appComponent.inject(this);
        // Now loginViewModel is available

        super.onCreate(savedInstanceState);
    }
}

public class LoginViewModel {

    private final UserRepository userRepository;

    // @Inject tells Dagger how to create instances of LoginViewModel
    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

بیایید به Dagger بگوییم که چگونه بقیه وابستگی‌ها را برای ساختن نمودار ارائه دهد:

کاتلین

class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

class UserLocalDataSource @Inject constructor() { ... }
class UserRemoteDataSource @Inject constructor(
    private val loginService: LoginRetrofitService
) { ... }

جاوا

public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

    @Inject
    public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
        this.userLocalDataSource = userLocalDataSource;
        this.userRemoteDataSource = userRemoteDataSource;
    }
}

public class UserLocalDataSource {

    @Inject
    public UserLocalDataSource() {}
}

public class UserRemoteDataSource {

    private final LoginRetrofitService loginRetrofitService;

    @Inject
    public UserRemoteDataSource(LoginRetrofitService loginRetrofitService) {
        this.loginRetrofitService = loginRetrofitService;
    }
}

پیمانه‌های Dagger

برای این مثال، از کتابخانه شبکه‌سازی Retrofit استفاده می‌کنید. ‫UserRemoteDataSource به LoginRetrofitService وابسته است. بااین‌حال، روش ایجاد نمونه‌ای از LoginRetrofitService با آنچه تاکنون انجام می‌داده‌اید متفاوت است. این یک نمونه‌سازی کلاس نیست؛ بلکه نتیجه فراخوانی Retrofit.Builder() و ارسال پارامترهای مختلف برای پیکربندی سرویس ورود به سیستم است.

جدااز حاشیه‌نویسی @Inject، روش دیگری برای گفتن به Dagger وجود دارد که چگونه نمونه‌ای از کلاس را ارائه دهد: اطلاعات داخل ماژول‌های Dagger. یک ماژول Dagger کلاسی است که با @Module حاشیه‌نویسی شده است. در آنجا می‌توانید وابستگی‌ها را با حاشیه‌نویسی @Provides تعریف کنید.

کاتلین

// @Module informs Dagger that this class is a Dagger Module
@Module
class NetworkModule {

    // @Provides tell Dagger how to create instances of the type that this function
    // returns (i.e. LoginRetrofitService).
    // Function parameters are the dependencies of this type.
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService {
        // Whenever Dagger needs to provide an instance of type LoginRetrofitService,
        // this code (the one inside the @Provides method) is run.
        return Retrofit.Builder()
                .baseUrl("https://example.com")
                .build()
                .create(LoginService::class.java)
    }
}

جاوا

// @Module informs Dagger that this class is a Dagger Module
@Module
public class NetworkModule {

    // @Provides tell Dagger how to create instances of the type that this function
    // returns (i.e. LoginRetrofitService).
    // Function parameters are the dependencies of this type.
    @Provides
    public LoginRetrofitService provideLoginRetrofitService() {
        // Whenever Dagger needs to provide an instance of type LoginRetrofitService,
        // this code (the one inside the @Provides method) is run.
        return new Retrofit.Builder()
                .baseUrl("https://example.com")
                .build()
                .create(LoginService.class);
    }
}

ماژول‌ها روشی برای کپسوله‌سازی معنایی اطلاعات درباره نحوه ارائه اشیاء هستند. همان‌طور که می‌بینید، کلاس NetworkModule را برای گروه‌بندی منطق ارائه اشیای مربوط به شبکه‌سازی فراخوانی کردید. اگر برنامه گسترش یابد، می‌توانید نحوه ارائه OkHttpClient را نیز در اینجا اضافه کنید، یا نحوه پیکربندی Gson یا Moshi را اضافه کنید.

وابستگی‌های روش @Provides پارامترهای آن روش هستند. برای روش قبلی، LoginRetrofitService را می‌توان بدون هیچ وابستگی‌ای ارائه کرد زیرا این روش هیچ پارامتری ندارد. اگر OkHttpClient را به‌عنوان پارامتر اعلام کرده بودید، Dagger باید نمونه OkHttpClient را از نمودار ارائه می‌کرد تا وابستگی‌های LoginRetrofitService را برآورده کند. برای مثال:

کاتلین

@Module
class NetworkModule {
    // Hypothetical dependency on LoginRetrofitService
    @Provides
    fun provideLoginRetrofitService(
        okHttpClient: OkHttpClient
    ): LoginRetrofitService { ... }
}

جاوا

@Module
public class NetworkModule {

    @Provides
    public LoginRetrofitService provideLoginRetrofitService(OkHttpClient okHttpClient) {
        ...
    }
}

برای اینکه نمودار Dagger از این ماژول مطلع شود، باید آن را به‌صورت زیر به میانای @Component اضافه کنید:

کاتلین

// The "modules" attribute in the @Component annotation tells Dagger what Modules
// to include when building the graph
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
    ...
}

جاوا

// The "modules" attribute in the @Component annotation tells Dagger what Modules
// to include when building the graph
@Component(modules = NetworkModule.class)
public interface ApplicationComponent {
    ...
}

روش توصیه‌شده برای افزودن انواع به نمودار Dagger استفاده از تزریق سازنده است (یعنی با حاشیه‌نویسی @Inject در سازنده کلاس). گاهی‌اوقات این کار امکان‌پذیر نیست و باید از واحدهای Dagger استفاده کنید. یک نمونه زمانی است که می‌خواهید Dagger از نتیجه یک محاسبه برای تعیین نحوه ایجاد نمونه‌ای از یک شیء استفاده کند. هرزمان که لازم باشد نمونه‌ای از آن نوع ارائه شود، Dagger کد را در روش @Provides اجرا می‌کند.

نمودار Dagger در مثال درحال‌حاضر به این شکل است:

نمودار گراف وابستگی LoginActivity

شکل ۲. نمایش گراف با LoginActivity که توسط Dagger تزریق شده است

نقطه ورودی به گراف LoginActivity است. چون LoginActivity LoginViewModel را تزریق می‌کند، Dagger نموداری می‌سازد که می‌داند چگونه نمونه‌ای از LoginViewModel و به‌صورت بازگشتی، وابستگی‌های آن را ارائه دهد. Dagger به‌دلیل حاشیه‌نویسی @Inject در سازنده کلاس‌ها می‌داند چگونه این کار را انجام دهد.

درون ApplicationComponent تولیدشده توسط Dagger، یک روش از نوع کارخانه وجود دارد تا نمونه‌هایی از تمام کلاس‌هایی که می‌داند چگونه ارائه دهد را دریافت کند. در این مثال، Dagger به NetworkModule موجود در ApplicationComponent واگذار می‌کند تا نمونه‌ای از LoginRetrofitService را دریافت کند.

حوزه‌های Dagger

در صفحه مبانی Dagger، از دامنه‌ها به‌عنوان روشی برای داشتن نمونه‌ای منحصربه‌فرد از یک نوع در مؤلفه نام برده شد. منظور از محدود کردن نوع به چرخه حیات مؤلفه همین است.

چون ممکن است بخواهید از UserRepository در ویژگی‌های دیگر برنامه استفاده کنید و شاید نخواهید هربار که به آن نیاز دارید شیء جدیدی ایجاد کنید، می‌توانید آن را به‌عنوان نمونه‌ای منحصربه‌فرد برای کل برنامه تعیین کنید. این موضوع برای LoginRetrofitService نیز صادق است: ایجاد آن می‌تواند پرهزینه باشد و همچنین می‌خواهید نمونه‌ای منحصربه‌فرد از آن شیء برای استفاده مجدد وجود داشته باشد. ایجاد نمونه‌ای از UserRemoteDataSource چندان پرهزینه نیست، بنابراین محدود کردن آن به چرخه حیات مؤلفه ضروری نیست.

@Singleton تنها حاشیه‌نویسی دامنه است که با بسته javax.inject ارائه می‌شود. می‌توانید از آن برای حاشیه‌نویسی ApplicationComponent و اشیایی که می‌خواهید در سراسر برنامه دوباره استفاده کنید استفاده کنید.

کاتلین

@Singleton
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
    fun inject(activity: LoginActivity)
}

@Singleton
class UserRepository @Inject constructor(
    private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource
) { ... }

@Module
class NetworkModule {
    // Way to scope types inside a Dagger Module
    @Singleton
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService { ... }
}

جاوا

@Singleton
@Component(modules = NetworkModule.class)
public interface ApplicationComponent {
    void inject(LoginActivity loginActivity);
}

@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;
    }
}

@Module
public class NetworkModule {

    @Singleton
    @Provides
    public LoginRetrofitService provideLoginRetrofitService() { ... }
}

مراقب باشید هنگام اعمال کردن دامنه‌ها به اشیاء، نشت حافظه ایجاد نشود. تا زمانی که مؤلفه محدودشده در حافظه باشد، شیء ایجادشده نیز در حافظه است. چون ApplicationComponent هنگام راه‌اندازی برنامه (در کلاس Application) ایجاد می‌شود، وقتی برنامه ازبین می‌رود، آن هم ازبین می‌رود. بنابراین، نمونه منحصربه‌فرد UserRepository همیشه در حافظه باقی می‌ماند تا زمانی که برنامه ازبین برود.

زیرمؤلفه‌های Dagger

اگر جریان ورود به سیستم شما (که توسط یک LoginActivity مدیریت می‌شود) از چند قطعه تشکیل شده است، باید از همان نمونه LoginViewModel در همه قطعه‌ها استفاده کنید. @Singleton نمی‌تواند LoginViewModel را برای استفاده مجدد از نمونه حاشیه‌نویسی کند به دلایل زیر:

  1. نمونه LoginViewModel پس‌از اتمام جریان در حافظه باقی می‌ماند.

  2. برای هر جریان ورود به سیستم، نمونه متفاوتی از LoginViewModel می‌خواهید. برای مثال، اگر کاربر از سیستم خارج شود، شما نمونه متفاوتی از LoginViewModel را می‌خواهید، نه همان نمونه‌ای که کاربر برای اولین‌بار به سیستم وارد شده است.

برای محدود کردن LoginViewModel به چرخه حیات LoginActivity، باید مؤلفه جدیدی (زیرگراف جدیدی) برای جریان ورود به سیستم و محدوده جدیدی ایجاد کنید.

بیایید نموداری مختص جریان ورود به سیستم ایجاد کنیم.

کاتلین

@Component
interface LoginComponent {}

جاوا

@Component
public interface LoginComponent {
}

اکنون، LoginActivity باید تزریق‌ها را از LoginComponent دریافت کند زیرا پیکربندی مختص ورود به سیستم دارد. این کار مسئولیت تزریق LoginActivity را از کلاس ApplicationComponent برمی‌دارد.

کاتلین

@Component
interface LoginComponent {
    fun inject(activity: LoginActivity)
}

جاوا

@Component
public interface LoginComponent {
    void inject(LoginActivity loginActivity);
}

‫LoginComponent باید بتواند به اشیاء از ApplicationComponent دسترسی داشته باشد زیرا LoginViewModel به UserRepository وابسته است. برای اینکه به Dagger بگویید می‌خواهید مؤلفه جدیدی از بخشی از مؤلفه دیگری استفاده کند، باید از زیرمؤلفه‌های Dagger استفاده کنید. مؤلفه جدید باید زیرمؤلفه مؤلفه حاوی منابع مشترک باشد.

زیرمؤلفه‌ها مؤلفه‌هایی هستند که نمودار شیء مؤلفه والد را به ارث می‌برند و گسترش می‌دهند. بنابراین، همه اشیای ارائه‌شده در مؤلفه والد در مؤلفه فرعی نیز ارائه می‌شوند. به‌این‌ترتیب، شیئی از زیرمؤلفه می‌تواند به شیء ارائه‌شده توسط مؤلفه ولی وابسته باشد.

برای ایجاد نمونه‌های زیرمؤلفه، به نمونه‌ای از مؤلفه والد نیاز دارید. بنابراین، اشیایی که مؤلفه والد به زیرمؤلفه ارائه می‌دهد همچنان در محدوده مؤلفه والد قرار دارند.

در این مثال، باید LoginComponent را به‌عنوان زیرمؤلفه ApplicationComponent تعریف کنید. برای انجام این کار، LoginComponent را با @Subcomponent حاشیه‌نویسی کنید:

کاتلین

// @Subcomponent annotation informs Dagger this interface is a Dagger Subcomponent
@Subcomponent
interface LoginComponent {

    // This tells Dagger that LoginActivity requests injection from LoginComponent
    // so that this subcomponent graph needs to satisfy all the dependencies of the
    // fields that LoginActivity is injecting
    fun inject(loginActivity: LoginActivity)
}

جاوا

// @Subcomponent annotation informs Dagger this interface is a Dagger Subcomponent
@Subcomponent
public interface LoginComponent {

    // This tells Dagger that LoginActivity requests injection from LoginComponent
    // so that this subcomponent graph needs to satisfy all the dependencies of the
    // fields that LoginActivity is injecting
    void inject(LoginActivity loginActivity);
}

همچنین باید یک کارخانه زیرمؤلفه در LoginComponent تعریف کنید تا ApplicationComponent بداند چگونه نمونه‌های LoginComponent را ایجاد کند.

کاتلین

@Subcomponent
interface LoginComponent {

    // Factory that is used to create instances of this subcomponent
    @Subcomponent.Factory
    interface Factory {
        fun create(): LoginComponent
    }

    fun inject(loginActivity: LoginActivity)
}

جاوا

@Subcomponent
public interface LoginComponent {

    // Factory that is used to create instances of this subcomponent
    @Subcomponent.Factory
    interface Factory {
        LoginComponent create();
    }

    void inject(LoginActivity loginActivity);
}

برای اینکه به Dagger بگویید LoginComponent زیرمؤلفه‌ای از ApplicationComponent است، باید آن را با این روش نشان دهید:

  1. ایجاد یک ماژول Dagger جدید (مثلاً SubcomponentsModule) با ارسال کلاس زیرمؤلفه به مشخصه subcomponents حاشیه‌نویسی.

    کاتلین

    // The "subcomponents" attribute in the @Module annotation tells Dagger what
    // Subcomponents are children of the Component this module is included in.
    @Module(subcomponents = LoginComponent::class)
    class SubcomponentsModule {}

    جاوا

    // The "subcomponents" attribute in the @Module annotation tells Dagger what
    // Subcomponents are children of the Component this module is included in.
    @Module(subcomponents = LoginComponent.class)
    public class SubcomponentsModule {
    }
  2. افزودن واحد جدید (یعنی SubcomponentsModule) به ApplicationComponent:

    کاتلین

    // Including SubcomponentsModule, tell ApplicationComponent that
    // LoginComponent is its subcomponent.
    @Singleton
    @Component(modules = [NetworkModule::class, SubcomponentsModule::class])
    interface ApplicationComponent {
    }

    جاوا

    // Including SubcomponentsModule, tell ApplicationComponent that
    // LoginComponent is its subcomponent.
    @Singleton
    @Component(modules = {NetworkModule.class, SubcomponentsModule.class})
    public interface ApplicationComponent {
    }

    توجه داشته باشید که ApplicationComponent دیگر نیازی به تزریق LoginActivity ندارد زیرا این مسئولیت اکنون متعلق به LoginComponent است، بنابراین می‌توانید روش inject() را از ApplicationComponent بردارید.

    مصرف‌کنندگان ApplicationComponent باید بدانند چگونه نمونه‌های LoginComponent را ایجاد کنند. مؤلفه والد باید روشی را در میانای خود اضافه کند تا به مصرف‌کنندگان اجازه دهد نمونه‌هایی از مؤلفه فرعی را از نمونه‌ای از مؤلفه والد ایجاد کنند:

  3. Expose the factory that creates instances of LoginComponentin the interface:

    کاتلین

    @Singleton
    @Component(modules = [NetworkModule::class, SubcomponentsModule::class])
    interface ApplicationComponent {
    // This function exposes the LoginComponent Factory out of the graph so consumers
    // can use it to obtain new instances of LoginComponent
    fun loginComponent(): LoginComponent.Factory
    }

    جاوا

    @Singleton
    @Component(modules = { NetworkModule.class, SubcomponentsModule.class} )
    public interface ApplicationComponent {
    // This function exposes the LoginComponent Factory out of the graph so consumers
    // can use it to obtain new instances of LoginComponent
    LoginComponent.Factory loginComponent();
    }

اختصاص دادن دامنه‌ها به زیرمؤلفه‌ها

اگر پروژه را بسازید، می‌توانید نمونه‌هایی از هر دو ApplicationComponent و LoginComponent ایجاد کنید. ‫ApplicationComponent به چرخه حیات برنامه متصل است زیرا می‌خواهید تا زمانی که برنامه در حافظه است، از همان نمونه گراف استفاده کنید.

چرخه عمر LoginComponent چیست؟ یکی از دلایلی که به LoginComponent نیاز داشتید این است که باید همان نمونه LoginViewModel را بین قطعه‌های مربوط به ورود به سیستم هم‌رسانی می‌کردید. اما همچنین، هر زمان که جریان ورود به سیستم جدیدی وجود دارد، نمونه‌های متفاوتی از LoginViewModel می‌خواهید. LoginActivity طول عمر مناسبی برای LoginComponent است: برای هر فعالیت جدید، به نمونه جدیدی از LoginComponent و قطعه‌هایی نیاز دارید که بتوانند از آن نمونه LoginComponent استفاده کنند.

چون LoginComponent به چرخه حیات LoginActivity پیوست شده است، باید مرجعی به مؤلفه در فعالیت نگه دارید، همان‌طور که مرجع applicationComponent را در کلاس Application نگه داشتید. به این ترتیب، تکه‌ها می‌توانند به آن دسترسی داشته باشند.

کاتلین

class LoginActivity: Activity() {
    // Reference to the Login graph
    lateinit var loginComponent: LoginComponent
    ...
}

جاوا

public class LoginActivity extends Activity {

    // Reference to the Login graph
    LoginComponent loginComponent;

    ...
}

توجه داشته باشید که متغیر loginComponent با @Inject حاشیه‌نویسی نشده است زیرا انتظار ندارید که آن متغیر توسط Dagger ارائه شود.

می‌توانید از ApplicationComponent برای دریافت مرجع LoginComponent استفاده کنید و سپس LoginActivity را به‌صورت زیر تزریق کنید:

کاتلین

class LoginActivity: Activity() {
    // Reference to the Login graph
    lateinit var loginComponent: LoginComponent

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        // Creation of the login graph using the application graph
        loginComponent = (applicationContext as MyDaggerApplication)
                            .appComponent.loginComponent().create()

        // Make Dagger instantiate @Inject fields in LoginActivity
        loginComponent.inject(this)

        // Now loginViewModel is available

        super.onCreate(savedInstanceState)
    }
}

جاوا

public class LoginActivity extends Activity {

    // Reference to the Login graph
    LoginComponent loginComponent;

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Creation of the login graph using the application graph
        loginComponent = ((MyApplication) getApplicationContext())
                                .appComponent.loginComponent().create();

        // Make Dagger instantiate @Inject fields in LoginActivity
        loginComponent.inject(this);

        // Now loginViewModel is available

        super.onCreate(savedInstanceState);
    }
}

‫LoginComponent در روش onCreate() فعالیت ایجاد می‌شود و وقتی فعالیت ازبین برود، به‌طور ضمنی ازبین خواهد رفت.

‫LoginComponent باید همیشه نمونه یکسانی از LoginViewModel را هربار که درخواست می‌شود ارائه دهد. با ایجاد کردن دامنه حاشیه‌نویسی سفارشی و حاشیه‌نویسی کردن LoginComponent و LoginViewModel با آن می‌توانید از این موضوع مطمئن شوید. توجه داشته باشید که نمی‌توانید از حاشیه‌نویسی @Singleton استفاده کنید زیرا قبلاً توسط مؤلفه والد استفاده شده است و این کار باعث می‌شود شیء به یک سینگلتون برنامه (نمونه منحصربه‌فرد برای کل برنامه) تبدیل شود. باید دامنه حاشیه‌نویسی دیگری ایجاد کنید.

در این مورد، می‌توانستید این دامنه را @LoginScope بنامید اما این کار خوبی نیست. نام حاشیه‌نویسی دامنه نباید صریحاً به هدفی که برآورده می‌کند اشاره کند. درعوض، باید بسته به طول عمرش نام‌گذاری شود زیرا حاشیه‌نویسی‌ها را می‌توان توسط مؤلفه‌های هم‌نیا مانند RegistrationComponent و SettingsComponent دوباره استفاده کرد. به همین دلیل است که باید آن را @ActivityScope بنامید نه @LoginScope.

کاتلین

// Definition of a custom scope called ActivityScope
@Scope
@Retention(value = AnnotationRetention.RUNTIME)
annotation class ActivityScope

// Classes annotated with @ActivityScope are scoped to the graph and the same
// instance of that type is provided every time the type is requested.
@ActivityScope
@Subcomponent
interface LoginComponent { ... }

// A unique instance of LoginViewModel is provided in Components
// annotated with @ActivityScope
@ActivityScope
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

جاوا

// Definition of a custom scope called ActivityScope
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {}

// Classes annotated with @ActivityScope are scoped to the graph and the same
// instance of that type is provided every time the type is requested.
@ActivityScope
@Subcomponent
public interface LoginComponent { ... }

// A unique instance of LoginViewModel is provided in Components
// annotated with @ActivityScope
@ActivityScope
public class LoginViewModel {

    private final UserRepository userRepository;

    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

اکنون، اگر دو قطعه داشتید که به LoginViewModel نیاز داشتند، هر دو آن‌ها با نمونه یکسانی ارائه می‌شوند. برای مثال، اگر LoginUsernameFragment و LoginPasswordFragment دارید، باید توسط LoginComponent تزریق شوند:

کاتلین

@ActivityScope
@Subcomponent
interface LoginComponent {

    @Subcomponent.Factory
    interface Factory {
        fun create(): LoginComponent
    }

    // All LoginActivity, LoginUsernameFragment and LoginPasswordFragment
    // request injection from LoginComponent. The graph needs to satisfy
    // all the dependencies of the fields those classes are injecting
    fun inject(loginActivity: LoginActivity)
    fun inject(usernameFragment: LoginUsernameFragment)
    fun inject(passwordFragment: LoginPasswordFragment)
}

جاوا

@ActivityScope
@Subcomponent
public interface LoginComponent {

    @Subcomponent.Factory
    interface Factory {
        LoginComponent create();
    }

    // All LoginActivity, LoginUsernameFragment and LoginPasswordFragment
    // request injection from LoginComponent. The graph needs to satisfy
    // all the dependencies of the fields those classes are injecting
    void inject(LoginActivity loginActivity);
    void inject(LoginUsernameFragment loginUsernameFragment);
    void inject(LoginPasswordFragment loginPasswordFragment);
}

عناصر به نمونه عنصری که در شیء LoginActivity قرار دارد دسترسی پیدا می‌کنند. کد نمونه برای LoginUserNameFragment در تکه‌کد زیر نشان داده شده است:

کاتلین

class LoginUsernameFragment: Fragment() {

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onAttach(context: Context) {
        super.onAttach(context)

        // Obtaining the login graph from LoginActivity and instantiate
        // the @Inject fields with objects from the graph
        (activity as LoginActivity).loginComponent.inject(this)

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

جاوا

public class LoginUsernameFragment extends Fragment {

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        // Obtaining the login graph from LoginActivity and instantiate
        // the @Inject fields with objects from the graph
        ((LoginActivity) getActivity()).loginComponent.inject(this);

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

و همین‌طور برای LoginPasswordFragment:

کاتلین

class LoginPasswordFragment: Fragment() {

    // Fields that need to be injected by the login graph
    @Inject lateinit var loginViewModel: LoginViewModel

    override fun onAttach(context: Context) {
        super.onAttach(context)

        (activity as LoginActivity).loginComponent.inject(this)

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

جاوا

public class LoginPasswordFragment extends Fragment {

    // Fields that need to be injected by the login graph
    @Inject
    LoginViewModel loginViewModel;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        ((LoginActivity) getActivity()).loginComponent.inject(this);

        // Now you can access loginViewModel here and onCreateView too
        // (shared instance with the Activity and the other Fragment)
    }
}

شکل ۳ نشان می‌دهد که نمودار Dagger با زیرمؤلفه جدید چگونه به‌نظر می‌رسد. کلاس‌های دارای نقطه سفید (UserRepository،‏ LoginRetrofitService، و LoginViewModel) کلاس‌هایی هستند که نمونه‌ای منحصربه‌فرد در محدوده مؤلفه‌های مربوطه‌شان دارند.

نمودار برنامه پس‌از افزودن آخرین زیرمؤلفه

شکل ۳. نمایش گراف ساخته‌شده برای نمونه برنامه Android

بیایید بخش‌های نمودار را تجزیه کنیم:

  1. NetworkModule (و بنابراین LoginRetrofitService) در ApplicationComponent گنجانده شده است زیرا آن را در مؤلفه مشخص کرده‌اید.

  2. UserRepository در ApplicationComponent باقی می‌ماند زیرا در محدوده ApplicationComponent است. اگر پروژه رشد کند، می‌خواهید همان نمونه را در ویژگی‌های مختلف (مثلاً ثبت‌نام) هم‌رسانی کنید.

    چون UserRepository بخشی از ApplicationComponent است، وابستگی‌های آن (یعنی UserLocalDataSource و UserRemoteDataSource) نیز باید در این مؤلفه باشند تا بتوانند نمونه‌های UserRepository را ارائه دهند.

  3. LoginViewModel در LoginComponent گنجانده شده است زیرا فقط کلاس‌های تزریق‌شده توسط LoginComponent به آن نیاز دارند. LoginViewModel در ApplicationComponent گنجانده نشده است زیرا هیچ وابستگی‌ای در ApplicationComponent به LoginViewModel نیاز ندارد.

    به‌طور مشابه، اگر UserRepository را به ApplicationComponent محدود نکرده بودید، Dagger به‌طور خودکار UserRepository و وابستگی‌های آن را به‌عنوان بخشی از LoginComponent اضافه می‌کرد زیرا درحال‌حاضر تنها جایی است که UserRepository استفاده می‌شود.

Apart from scoping objects to a different lifecycle, creating subcomponents is a good practice to encapsulate different parts of your application from each other.

ساختاردهی برنامه برای ایجاد زیرگراف‌های مختلف Dagger بسته به جریان برنامه شما به برنامه‌ای با عملکرد و مقیاس‌پذیری بیشتر ازنظر حافظه و زمان راه‌اندازی کمک می‌کند.

روال‌های مطلوب هنگام ساختن نمودار Dagger

هنگام ساختن نمودار Dagger برای برنامه‌تان:

  • وقتی مؤلفه‌ای ایجاد می‌کنید، باید درنظر بگیرید که کدام عنصر مسئول طول عمر آن مؤلفه است. در این مورد، کلاس Application مسئول ApplicationComponent است و LoginActivity مسئول LoginComponent است.

  • فقط زمانی‌که منطقی است از محدود کردن استفاده کنید. استفاده بیش‌ازحد از محدوده‌بندی می‌تواند تأثیر منفی بر عملکرد زمان اجرای برنامه‌تان داشته باشد: تا زمانی که مؤلفه در حافظه باشد، شیء در حافظه است و دریافت شیء محدوده‌بندی‌شده هزینه بیشتری دارد. وقتی Dagger شیء را ارائه می‌دهد، به‌جای ارائه‌دهنده نوع کارخانه از قفل کردن DoubleCheck استفاده می‌کند.

آزمایش پروژه‌ای که از Dagger استفاده می‌کند

یکی از مزایای استفاده از چارچوب‌های تزریق وابستگی مانند Dagger این است که آزمایش کد شما را آسان‌تر می‌کند.

آزمون‌های واحد

برای آزمایش‌های واحد لازم نیست از Dagger استفاده کنید. هنگام آزمایش کلاسی که از تزریق سازنده استفاده می‌کند، نیازی به استفاده از Dagger برای نمونه‌سازی آن کلاس ندارید. می‌توانید مستقیماً سازنده آن را فراخوانی کنید و وابستگی‌های جعلی یا ساختگی را مستقیماً ارسال کنید، درست مانند زمانی که حاشیه‌نویسی نشده باشند.

برای مثال، هنگام آزمایش LoginViewModel:

کاتلین

@ActivityScope
class LoginViewModel @Inject constructor(
    private val userRepository: UserRepository
) { ... }

class LoginViewModelTest {

    @Test
    fun `Happy path`() {
        // You don't need Dagger to create an instance of LoginViewModel
        // You can pass a fake or mock UserRepository
        val viewModel = LoginViewModel(fakeUserRepository)
        assertEquals(...)
    }
}

جاوا

@ActivityScope
public class LoginViewModel {

    private final UserRepository userRepository;

    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

public class LoginViewModelTest {

    @Test
    public void happyPath() {
        // You don't need Dagger to create an instance of LoginViewModel
        // You can pass a fake or mock UserRepository
        LoginViewModel viewModel = new LoginViewModel(fakeUserRepository);
        assertEquals(...);
    }
}

آزمایش‌های سرتاسر

برای آزمایش‌های ادغام، روش خوب این است که TestApplicationComponent برای آزمایش ایجاد کنید. تولید و آزمایش از پیکربندی مؤلفه متفاوتی استفاده می‌کنند.

این کار به طراحی اولیه بیشتری برای ماژول‌ها در برنامه شما نیاز دارد. مؤلفه آزمایشی مؤلفه تولید را گسترش می‌دهد و مجموعه متفاوتی از واحدها را نصب می‌کند.

کاتلین

// TestApplicationComponent extends from ApplicationComponent to have them both
// with the same interface methods. You need to include the modules of the
// component here as well, and you can replace the ones you want to override.
// This sample uses FakeNetworkModule instead of NetworkModule
@Singleton
@Component(modules = [FakeNetworkModule::class, SubcomponentsModule::class])
interface TestApplicationComponent : ApplicationComponent {
}

جاوا

// TestApplicationComponent extends from ApplicationComponent to have them both
// with the same interface methods. You need to include the modules of the
// Component here as well, and you can replace the ones you want to override.
// This sample uses FakeNetworkModule instead of NetworkModule
@Singleton
@Component(modules = {FakeNetworkModule.class, SubcomponentsModule.class})
public interface TestApplicationComponent extends ApplicationComponent {
}

‫FakeNetworkModule پیاده‌سازی جعلی از NetworkModule اصلی دارد. در آنجا می‌توانید نمونه‌های جعلی یا شبیه‌سازی‌شده از هر چیزی را که می‌خواهید جایگزین کنید ارائه دهید.

کاتلین

// In the FakeNetworkModule, pass a fake implementation of LoginRetrofitService
// that you can use in your tests.
@Module
class FakeNetworkModule {
    @Provides
    fun provideLoginRetrofitService(): LoginRetrofitService {
        return FakeLoginService()
    }
}

جاوا

// In the FakeNetworkModule, pass a fake implementation of LoginRetrofitService
// that you can use in your tests.
@Module
public class FakeNetworkModule {

    @Provides
    public LoginRetrofitService provideLoginRetrofitService() {
        return new FakeLoginService();
    }
}

در آزمایش‌های یکپارچه‌سازی یا سرتاسر، از TestApplication استفاده می‌کنید که TestApplicationComponent را به‌جای ApplicationComponent ایجاد می‌کند.

کاتلین

// Your test application needs an instance of the test graph
class MyTestApplication: MyApplication() {
    override val appComponent = DaggerTestApplicationComponent.create()
}

جاوا

// Your test application needs an instance of the test graph
public class MyTestApplication extends MyApplication {
    ApplicationComponent appComponent = DaggerTestApplicationComponent.create();
}

سپس، از این برنامه آزمایشی در TestRunner سفارشی استفاده می‌شود که از آن برای اجرای آزمایش‌های ابزار دقیق استفاده خواهید کرد. برای اطلاعات بیشتر درباره این موضوع، کدلب استفاده از Dagger در برنامه Android را بررسی کنید.

کار کردن با ماژول‌های Dagger

ماژول‌های Dagger روشی برای کپسوله‌سازی نحوه ارائه اشیاء به‌صورت معنایی هستند. می‌توانید پیمانه‌ها را در مؤلفه‌ها قرار دهید، اما می‌توانید پیمانه‌ها را در پیمانه‌های دیگر نیز قرار دهید. این ابزار قدرتمند است، اما به‌راحتی می‌توان از آن سوءاستفاده کرد.

وقتی ماژولی به مؤلفه یا ماژول دیگری اضافه می‌شود، ازقبل در نمودار Dagger قرار دارد؛ Dagger می‌تواند آن اشیاء را در آن مؤلفه ارائه دهد. قبل‌از افزودن یک ماژول، بررسی کنید که آیا آن ماژول قبلاً بخشی از نمودار Dagger است یا نه. برای این کار، بررسی کنید که آیا قبلاً به مؤلفه اضافه شده است یا نه، یا پروژه را کامپایل کنید و ببینید آیا Dagger می‌تواند وابستگی‌های موردنیاز برای آن ماژول را پیدا کند یا نه.

تمرین خوب حکم می‌کند که ماژول‌ها فقط یک‌بار در یک مؤلفه اعلام شوند (خارج از موارد استفاده پیشرفته خاص Dagger).

فرض کنید نمودارتان را به این شکل پیکربندی کرده‌اید. ‫ApplicationComponent شامل Module1 و Module2 است و Module1 شامل ModuleX است.

کاتلین

@Component(modules = [Module1::class, Module2::class])
interface ApplicationComponent { ... }

@Module(includes = [ModuleX::class])
class Module1 { ... }

@Module
class Module2 { ... }

جاوا

@Component(modules = {Module1.class, Module2.class})
public interface ApplicationComponent { ... }

@Module(includes = {ModuleX.class})
public class Module1 { ... }

@Module
public class Module2 { ... }

اگر اکنون Module2 به کلاس‌های ارائه‌شده توسط ModuleX بستگی دارد. عملکرد بد شامل ModuleX در Module2 است زیرا ModuleX دو بار در نمودار گنجانده شده است، همان‌طور که در تکه‌کد زیر دیده می‌شود:

کاتلین

// Bad practice: ModuleX is declared multiple times in this Dagger graph
@Component(modules = [Module1::class, Module2::class])
interface ApplicationComponent { ... }

@Module(includes = [ModuleX::class])
class Module1 { ... }

@Module(includes = [ModuleX::class])
class Module2 { ... }

جاوا

// Bad practice: ModuleX is declared multiple times in this Dagger graph.
@Component(modules = {Module1.class, Module2.class})
public interface ApplicationComponent { ... }

@Module(includes = ModuleX.class)
public class Module1 { ... }

@Module(includes = ModuleX.class)
public class Module2 { ... }

درعوض، باید یکی از کارهای زیر را انجام دهید:

  1. ماژول‌ها را بازسازی کنید و ماژول مشترک را به مؤلفه استخراج کنید.
  2. ماژول جدیدی با اشیایی که هر دو ماژول هم‌رسانی می‌کنند ایجاد کنید و آن را به مؤلفه استخراج کنید.

Not refactoring in this way results in a lot of modules including each other without a clear sense of organization and making it more difficult to see where each dependency is coming from.

روش خوب (گزینه ۱): ModuleX یک‌بار در نمودار Dagger اعلام می‌شود.

کاتلین

@Component(modules = [Module1::class, Module2::class, ModuleX::class])
interface ApplicationComponent { ... }

@Module
class Module1 { ... }

@Module
class Module2 { ... }

جاوا

@Component(modules = {Module1.class, Module2.class, ModuleX.class})
public interface ApplicationComponent { ... }

@Module
public class Module1 { ... }

@Module
public class Module2 { ... }

روش خوب (گزینه ۲): وابستگی‌های مشترک از Module1 و Module2 در ModuleX به یک ماژول جدید به نام ModuleXCommon استخراج می‌شوند که در مؤلفه گنجانده شده است. سپس دو واحد دیگر به‌نام ModuleXWithModule1Dependencies و ModuleXWithModule2Dependencies با وابستگی‌های مختص هر واحد ایجاد می‌شود. همه ماژول‌ها یک‌بار در نمودار Dagger اعلام می‌شوند.

کاتلین

@Component(modules = [Module1::class, Module2::class, ModuleXCommon::class])
interface ApplicationComponent { ... }

@Module
class ModuleXCommon { ... }

@Module
class ModuleXWithModule1SpecificDependencies { ... }

@Module
class ModuleXWithModule2SpecificDependencies { ... }

@Module(includes = [ModuleXWithModule1SpecificDependencies::class])
class Module1 { ... }

@Module(includes = [ModuleXWithModule2SpecificDependencies::class])
class Module2 { ... }

جاوا

@Component(modules = {Module1.class, Module2.class, ModuleXCommon.class})
public interface ApplicationComponent { ... }

@Module
public class ModuleXCommon { ... }

@Module
public class ModuleXWithModule1SpecificDependencies { ... }

@Module
public class ModuleXWithModule2SpecificDependencies { ... }

@Module(includes = ModuleXWithModule1SpecificDependencies.class)
public class Module1 { ... }

@Module(includes = ModuleXWithModule2SpecificDependencies.class)
public class Module2 { ... }

تزریق کمکی

تزریق کمکی یک الگوی DI است که برای ساختن شیء استفاده می‌شود که در آن برخی پارامترها ممکن است توسط چارچوب DI ارائه شود و برخی دیگر باید در زمان ایجاد توسط کاربر ارسال شود.

در Android، این الگو در صفحه‌های جزئیات رایج است که شناسه عنصر برای نمایش فقط در زمان اجرا مشخص است، نه در زمان ترجمه که Dagger نمودار DI را تولید می‌کند. برای کسب اطلاعات بیشتر درباره تزریق کمکی با Dagger، به اسناد Dagger مراجعه کنید.

نتیجه‌گیری

اگر قبلاً این کار را نکرده‌اید، بخش روال‌های مطلوب را مرور کنید. برای دیدن نحوه استفاده از Dagger در برنامه Android، به استفاده از Dagger در codelab برنامه Android مراجعه کنید.