تزریق وابستگی با Hilt (نماها)

مفاهیم و پیاده‌سازی Jetpack Compose

Hilt یک کتابخانه تزریق وابستگی برای Android است که کد تکراری تزریق وابستگی دستی را در پروژه شما کاهش می‌دهد. برای انجام تزریق وابستگی دستی، باید هر کلاس و وابستگی‌های آن را به‌صورت دستی بسازید و از محفظه‌ها برای استفاده مجدد و مدیریت وابستگی‌ها استفاده کنید.

Hilt با ارائه محفظه‌هایی برای هر کلاس Android در پروژه شما و مدیریت خودکار چرخه‌های حیات آن‌ها، روشی استاندارد برای استفاده از تزریق وابستگی در برنامه‌تان ارائه می‌دهد. Hilt بر روی کتابخانه محبوب DI Dagger ساخته شده است تا از صحت زمان ترجمه، عملکرد زمان اجرا، مقیاس‌پذیری، و پشتیبانی Android Studio که Dagger ارائه می‌دهد بهره‌مند شود. برای اطلاعات بیشتر، Hilt و Dagger را ببینید.

این راهنما مفاهیم پایه Hilt و محفظه‌های تولیدشده آن را توضیح می‌دهد. همچنین نمونه‌ای از نحوه راه‌اندازی برنامه موجود برای استفاده از Hilt را شامل می‌شود.

کلاس برنامه Hilt

همه برنامه‌هایی که از Hilt استفاده می‌کنند باید حاوی کلاس Application باشند که با @HiltAndroidApp حاشیه‌نویسی شده است.

@HiltAndroidApp تولید کد Hilt را راه‌اندازی می‌کند، ازجمله کلاس پایه برای برنامه شما که به‌عنوان محفظه وابستگی سطح برنامه عمل می‌کند.

کاتلین

@HiltAndroidApp
class ExampleApplication : Application() { ... }

جاوا

@HiltAndroidApp
public class ExampleApplication extends Application { ... }

این مؤلفه Hilt تولیدشده به چرخه حیات شیء Application متصل است و وابستگی‌هایی را برای آن فراهم می‌کند. علاوه‌براین، این مؤلفه والد برنامه است، به این معنی که سایر مؤلفه‌ها می‌توانند به وابستگی‌هایی که ارائه می‌دهد دسترسی داشته باشند.

تزریق وابستگی‌ها به کلاس‌های Android

وقتی Hilt در کلاس Application شما راه‌اندازی شد و مؤلفه سطح برنامه دردسترس بود، Hilt می‌تواند وابستگی‌ها را برای کلاس‌های Android دیگری که حاشیه‌نویسی @AndroidEntryPoint دارند فراهم کند:

کاتلین

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }

جاوا

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity { ... }

Hilt درحال‌حاضر از کلاس‌های Android زیر پشتیبانی می‌کند:

  • ‫Application (بااستفاده از @HiltAndroidApp)
  • ‫ViewModel (بااستفاده از @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

اگر کلاس Android را با @AndroidEntryPoint حاشیه‌نویسی کنید، باید کلاس‌های Android وابسته به آن را نیز حاشیه‌نویسی کنید. برای مثال، اگر حاشیه‌نویسی برای قطعه‌ای اضافه کنید، باید برای هر فعالیتی که در آن از آن قطعه استفاده می‌کنید نیز حاشیه‌نویسی اضافه کنید.

@AndroidEntryPoint برای هر کلاس Android در پروژه شما یک مؤلفه Hilt جداگانه تولید می‌کند. این مؤلفه‌ها می‌توانند وابستگی‌ها را از کلاس‌های والد مربوطه خود دریافت کنند، همان‌طور که در سلسله‌مراتب مؤلفه توضیح داده شده است.

برای دریافت وابستگی‌ها از مؤلفه، از یادداشت @Inject برای انجام تزریق فیلد استفاده کنید:

کاتلین

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

جاوا

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

کلاس‌هایی که Hilt تزریق می‌کند می‌توانند کلاس‌های پایه دیگری داشته باشند که آن‌ها هم از تزریق استفاده می‌کنند. اگر این کلاس‌ها انتزاعی باشند، به حاشیه‌نویسی @AndroidEntryPoint نیاز ندارند.

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

تعریف کردن اتصال‌های Hilt

برای انجام تزریق فیلد، Hilt باید بداند چگونه نمونه‌هایی از وابستگی‌های لازم را از مؤلفه مربوطه ارائه دهد. اتصال حاوی اطلاعات لازم برای ارائه نمونه‌های یک نوع به‌عنوان وابستگی است.

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

کاتلین

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

جاوا

public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

پارامترهای سازنده حاشیه‌نویسی‌شده یک کلاس، وابستگی‌های آن کلاس هستند. در این مثال، AnalyticsAdapter دارای AnalyticsService به‌عنوان وابستگی است. بنابراین، Hilt باید بداند که چگونه نمونه‌های AnalyticsService را ارائه دهد.

پیمانه‌های Hilt

گاهی‌اوقات نمی‌توان نوعی را تزریق سازنده کرد. این اتفاق می‌تواند به دلایل متعددی رخ دهد. برای مثال، نمی‌توانید میانایی را تزریق سازنده کنید. همچنین نمی‌توانید نوعی را که مالک آن نیستید، مانند کلاسی از یک کتابخانه خارجی، تزریق سازنده کنید. در این موارد، می‌توانید بااستفاده از مدول‌های Hilt، اطلاعات اتصال را به Hilt ارائه دهید.

یک ماژول Hilt کلاسی است که با @Module حاشیه‌نویسی شده است. مانند ماژول Dagger، به Hilt اطلاع می‌دهد که چگونه نمونه‌هایی از انواع خاصی را ارائه دهد. برخلاف پیمانه‌های Dagger، باید پیمانه‌های Hilt را با @InstallIn حاشیه‌نویسی کنید تا به Hilt بگویید هر پیمانه در کدام کلاس Android استفاده یا نصب خواهد شد.

وابستگی‌هایی که در پیمانه‌های Hilt ارائه می‌دهید در همه مؤلفه‌های تولیدشده که با کلاس Android مرتبط هستند و پیمانه Hilt را در آن نصب می‌کنید دردسترس هستند.

تزریق نمونه‌های میانای با @Binds

مثال AnalyticsService را درنظر بگیرید. اگر AnalyticsService یک میانای کاربری باشد، نمی‌توانید آن را تزریق سازنده کنید. درعوض، با ایجاد یک تابع انتزاعی حاشیه‌نویسی‌شده با @Binds در داخل یک ماژول Hilt، اطلاعات اتصال را به Hilt ارائه دهید.

حاشیه‌نویسی @Binds به Hilt می‌گوید که وقتی نیاز به ارائه نمونه‌ای از میانای کاربری دارد، از کدام پیاده‌سازی استفاده کند.

تابع حاشیه‌نویسی‌شده اطلاعات زیر را به Hilt ارائه می‌دهد:

  • نوع پاسخ تابع به Hilt می‌گوید که تابع نمونه‌های کدام میانای را ارائه می‌دهد.
  • پارامتر تابع به Hilt می‌گوید کدام پیاده‌سازی را ارائه دهد.

کاتلین

interface AnalyticsService {
  fun analyticsMethods()
}

// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
class AnalyticsServiceImpl @Inject constructor(
  ...
) : AnalyticsService { ... }

@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {

  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

جاوا

public interface AnalyticsService {
  void analyticsMethods();
}

// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
public class AnalyticsServiceImpl implements AnalyticsService {
  ...
  @Inject
  AnalyticsServiceImpl(...) {
    ...
  }
}

@Module
@InstallIn(ActivityComponent.class)
public abstract class AnalyticsModule {

  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

ماژول Hilt AnalyticsModule با @InstallIn(ActivityComponent.class) حاشیه‌نویسی شده است زیرا می‌خواهید Hilt آن وابستگی را به ExampleActivity تزریق کند. این حاشیه‌نویسی یعنی همه وابستگی‌ها در AnalyticsModule در همه فعالیت‌های برنامه دردسترس است.

تزریق نمونه‌ها با ‎ @Provides

رابط‌ها تنها موردی نیستند که نمی‌توانید نوعی را تزریق سازنده کنید. اگر مالک کلاس نباشید، تزریق سازنده نیز امکان‌پذیر نیست زیرا از یک کتابخانه خارجی می‌آید (کلاس‌هایی مانند Retrofit، OkHttpClient، یا پایگاه‌های داده Room)، یا اگر نمونه‌ها باید با الگوی سازنده ایجاد شوند.

مثال قبلی را درنظر بگیرید. اگر مستقیماً مالک کلاس AnalyticsService نیستید، می‌توانید با ایجاد تابعی در ماژول Hilt و حاشیه‌نویسی آن تابع با @Provides، به Hilt بگویید چگونه نمونه‌هایی از این نوع را ارائه دهد.

تابع حاشیه‌نویسی‌شده اطلاعات زیر را به Hilt ارائه می‌دهد:

  • نوع پاسخ تابع به Hilt می‌گوید که تابع نمونه‌های چه نوعی را ارائه می‌دهد.
  • پارامترهای تابع وابستگی‌های نوع مربوطه را به Hilt می‌گویند.
  • بدنه تابع به Hilt می‌گوید که چگونه نمونه‌ای از نوع مربوطه را ارائه دهد. Hilt هر بار که لازم باشد نمونه‌ای از آن نوع را ارائه دهد، بدنه تابع را اجرا می‌کند.

کاتلین

@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    // Potential dependencies of this type
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService::class.java)
  }
}

جاوا

@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    // Potential dependencies of this type
  ) {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

ارائه چندین اتصال برای یک نوع

در مواردی که نیاز دارید Hilt پیاده‌سازی‌های مختلفی از یک نوع را به‌عنوان وابستگی ارائه دهد، باید چندین اتصال به Hilt ارائه دهید. می‌توانید بااستفاده از توصیف‌کننده‌ها، چندین اتصال را برای یک نوع تعریف کنید.

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

مثال را درنظر بگیرید. اگر لازم است تماس‌های AnalyticsService را رهگیری کنید، می‌توانید از شیء OkHttpClient با رهگیر استفاده کنید. برای سرویس‌های دیگر، ممکن است لازم باشد تماس‌ها را به روش دیگری رهگیری کنید. در این صورت، باید به Hilt بگویید که چگونه دو پیاده‌سازی متفاوت از OkHttpClient ارائه دهد.

ابتدا، توصیف‌کننده‌هایی را که برای حاشیه‌نویسی روش‌های @Binds یا @Provides استفاده خواهید کرد تعریف کنید:

کاتلین

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient

جاوا

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface AuthInterceptorOkHttpClient {}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface OtherInterceptorOkHttpClient {}

سپس، Hilt باید بداند که چگونه نمونه‌ای از نوعی را که با هر واجدشرایط مطابقت دارد ارائه دهد. در این مورد، می‌توانید از یک ماژول Hilt با @Provides استفاده کنید. هر دو روش نوع پاسخ یکسانی دارند، اما واجدشرایط‌کننده‌ها آن‌ها را به‌عنوان دو اتصال متفاوت برچسب‌گذاری می‌کنند:

کاتلین

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

  @AuthInterceptorOkHttpClient
  @Provides
  fun provideAuthInterceptorOkHttpClient(
    authInterceptor: AuthInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(authInterceptor)
               .build()
  }

  @OtherInterceptorOkHttpClient
  @Provides
  fun provideOtherInterceptorOkHttpClient(
    otherInterceptor: OtherInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(otherInterceptor)
               .build()
  }
}

جاوا

@Module
@InstallIn(ActivityComponent.class)
public class NetworkModule {

  @AuthInterceptorOkHttpClient
  @Provides
  public static OkHttpClient provideAuthInterceptorOkHttpClient(
    AuthInterceptor authInterceptor
  ) {
      return new OkHttpClient.Builder()
                   .addInterceptor(authInterceptor)
                   .build();
  }

  @OtherInterceptorOkHttpClient
  @Provides
  public static OkHttpClient provideOtherInterceptorOkHttpClient(
    OtherInterceptor otherInterceptor
  ) {
      return new OkHttpClient.Builder()
                   .addInterceptor(otherInterceptor)
                   .build();
  }
}

می‌توانید نوع خاصی را که نیاز دارید با حاشیه‌نویسی فیلد یا پارامتر با واجدشرایط مربوطه تزریق کنید:

کاتلین

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .client(okHttpClient)
               .build()
               .create(AnalyticsService::class.java)
  }
}

// As a dependency of a constructor-injected class.
class ExampleServiceImpl @Inject constructor(
  @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
) : ...

// At field injection.
@AndroidEntryPoint
class ExampleActivity: AppCompatActivity() {

  @AuthInterceptorOkHttpClient
  @Inject lateinit var okHttpClient: OkHttpClient
}

جاوا

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    @AuthInterceptorOkHttpClient OkHttpClient okHttpClient
  ) {
      return new Retrofit.Builder()
                  .baseUrl("https://example.com")
                  .client(okHttpClient)
                  .build()
                  .create(AnalyticsService.class);
  }
}

// As a dependency of a constructor-injected class.
public class ExampleServiceImpl ... {

  private final OkHttpClient okHttpClient;

  @Inject
  ExampleServiceImpl(@AuthInterceptorOkHttpClient OkHttpClient okHttpClient) {
    this.okHttpClient = okHttpClient;
  }
}

// At field injection.
@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @AuthInterceptorOkHttpClient
  @Inject
  OkHttpClient okHttpClient;
  ...
}

به‌عنوان یک روال مطلوب، اگر توصیف‌کننده‌ای را به نوعی اضافه می‌کنید، توصیف‌کننده‌ها را به همه روش‌های ممکن برای ارائه آن وابستگی اضافه کنید. رها کردن پیاده‌سازی پایه یا مشترک بدون واجدشرایط‌کننده مستعد خطا است و می‌تواند منجر به تزریق وابستگی اشتباه توسط Hilt شود.

توصیف‌گرهای ازپیش تعریف‌شده در Hilt

Hilt برخی‌از واجدشرایط‌های ازپیش تعریف‌شده را ارائه می‌دهد. برای مثال، ازآنجایی‌که ممکن است به کلاس Context از برنامه یا فعالیت نیاز داشته باشید، Hilt اصلاح‌کننده‌های @ApplicationContext و @ActivityContext را ارائه می‌دهد.

فرض کنید کلاس AnalyticsAdapter از مثال به بافت فعالیت نیاز دارد. کد زیر نشان می‌دهد که چگونه بافت فعالیت را به AnalyticsAdapter ارائه دهید:

کاتلین

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
) { ... }

جاوا

public class AnalyticsAdapter {

  private final Context context;
  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(
    @ActivityContext Context context,
    AnalyticsService service
  ) {
    this.context = context;
    this.service = service;
  }
}

برای سایر اتصال‌های ازپیش تعریف‌شده موجود در Hilt، به اتصال‌های پیش‌فرض مؤلفه مراجعه کنید.

مؤلفه‌های تولیدشده برای کلاس‌های Android

برای هر کلاس Android که می‌توانید تزریق فیلد را در آن انجام دهید، یک مؤلفه Hilt مرتبط وجود دارد که می‌توانید در حاشیه‌نویسی @InstallIn به آن ارجاع دهید. هر مؤلفه Hilt مسئول تزریق اتصال‌های خود به کلاس Android مربوطه است.

نمونه‌های قبلی استفاده از ActivityComponent را در پیمانه‌های Hilt نشان دادند.

Hilt مؤلفه‌های زیر را ارائه می‌دهد:

مؤلفه Hilt تزریق‌کننده برای
SingletonComponent Application
ActivityRetainedComponent موجود نیست
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View با @WithFragmentBindings حاشیه‌نویسی کرد
ServiceComponent Service

طول عمر مؤلفه

Hilt به‌طور خودکار نمونه‌هایی از کلاس‌های مؤلفه تولیدشده را ایجاد و تخریب می‌کند و چرخه حیات کلاس‌های Android مربوطه را دنبال می‌کند.

عنصر تولیدشده ایجادشده در تاریخ زمان تخریب:
SingletonComponent Application#onCreate() Application نابود شد
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel ایجاد شد ViewModel نابود شد
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View نابود شد
ViewWithFragmentComponent View#super() View نابود شد
ServiceComponent Service#onCreate() Service#onDestroy()

دامنه‌های مؤلفه

به‌طور پیش‌فرض، همه اتصال‌ها در Hilt بدون محدوده هستند. این یعنی هربار که برنامه‌تان درخواست اتصال می‌کند، Hilt نمونه جدیدی از نوع موردنیاز ایجاد می‌کند.

در این مثال، هر بار که Hilt‏ AnalyticsAdapter را به‌عنوان وابستگی به نوع دیگری یا ازطریق تزریق فیلد (مانند ExampleActivity) ارائه می‌دهد، Hilt نمونه جدیدی از AnalyticsAdapter را ارائه می‌دهد.

بااین‌حال، Hilt همچنین اجازه می‌دهد که یک اتصال به یک مؤلفه خاص محدود شود. Hilt فقط یک‌بار برای هر نمونه از مؤلفه‌ای که اتصال به آن محدود شده است، یک اتصال محدود ایجاد می‌کند و همه درخواست‌ها برای آن اتصال، همان نمونه را به اشتراک می‌گذارند.

جدول زیر حاشیه‌نویسی‌های دامنه را برای هر مؤلفه تولیدشده فهرست می‌کند:

کلاس Android عنصر تولیدشده حوزه
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View با @WithFragmentBindings حاشیه‌نویسی کرد ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

در این مثال، اگر بااستفاده از @ActivityScoped، AnalyticsAdapter را به ActivityComponent محدود کنید، Hilt همان نمونه AnalyticsAdapter را درطول عمر فعالیت مربوطه ارائه می‌دهد:

کاتلین

@ActivityScoped
class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

جاوا

@ActivityScoped
public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

فرض کنید AnalyticsService دارای یک وضعیت داخلی است که نیاز دارد هر بار از همان نمونه استفاده شود—نه تنها در ExampleActivity، بلکه در هر جای دیگری از برنامه. در این حالت، مناسب است که AnalyticsService را به SingletonComponent محدود کنید. نتیجه این است که هرگاه مؤلفه نیاز به ارائه نمونه‌ای از AnalyticsService داشته باشد، هربار همان نمونه را ارائه می‌دهد.

مثال زیر نشان می‌دهد که چگونه می‌توان یک اتصال را به یک مؤلفه در یک ماژول Hilt محدود کرد. محدوده اتصال باید با محدوده مؤلفه‌ای که در آن نصب شده است مطابقت داشته باشد، بنابراین در این مثال باید AnalyticsService را در SingletonComponent به‌جای ActivityComponent نصب کنید:

کاتلین

// If AnalyticsService is an interface.
@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

// If you don't own AnalyticsService.
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {

  @Singleton
  @Provides
  fun provideAnalyticsService(): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService::class.java)
  }
}

جاوا

// If AnalyticsService is an interface.
@Module
@InstallIn(SingletonComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

// If you don't own AnalyticsService.
@Module
@InstallIn(SingletonComponent.class)
public class AnalyticsModule {

  @Singleton
  @Provides
  public static AnalyticsService provideAnalyticsService() {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

برای کسب اطلاعات بیشتر درباره دامنه‌های مؤلفه Hilt، به Scoping in Android and Hilt مراجعه کنید.

سلسله مراتب مؤلفه

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

ViewWithFragmentComponent زیر FragmentComponent است. FragmentComponent
    و ViewComponent زیر ActivityComponent هستند. ActivityComponent is under
    ActivityRetainedComponent. ViewModelComponent is under
    ActivityRetainedComponent. ActivityRetainedComponent و ServiceComponent
    زیر SingletonComponent هستند.
شکل ۱. سلسله‌مراتب مؤلفه‌هایی که Hilt تولید می‌کند.

بست‌های پیش‌فرض مؤلفه

هر مؤلفه Hilt با مجموعه‌ای از اتصال‌های پیش‌فرض ارائه می‌شود که Hilt می‌تواند آن‌ها را به‌عنوان وابستگی به اتصال‌های سفارشی شما تزریق کند. توجه داشته باشید که این اتصال‌ها با انواع فعالیت و قطعه عمومی مطابقت دارند و نه با هیچ زیرکلاس خاصی. دلیلش این است که Hilt از یک تعریف مؤلفه فعالیت واحد برای تزریق همه فعالیت‌ها استفاده می‌کند. هر فعالیت نمونه متفاوتی از این مؤلفه دارد.

مؤلفه Android اتصال‌های پیش‌فرض
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application، Activity
FragmentComponent Application، Activity، Fragment
ViewComponent Application، Activity، View
ViewWithFragmentComponent Application، Activity، Fragment، View
ServiceComponent Application، Service

اتصال زمینه برنامه نیز بااستفاده از @ApplicationContext دردسترس است. برای مثال:

کاتلین

class AnalyticsServiceImpl @Inject constructor(
  @ApplicationContext context: Context
) : AnalyticsService { ... }

// The Application binding is available without qualifiers.
class AnalyticsServiceImpl @Inject constructor(
  application: Application
) : AnalyticsService { ... }

جاوا

public class AnalyticsServiceImpl implements AnalyticsService {

  private final Context context;

  @Inject
  AnalyticsAdapter(@ApplicationContext Context context) {
    this.context = context;
  }
}

// The Application binding is available without qualifiers.
public class AnalyticsServiceImpl implements AnalyticsService {

  private final Application application;

  @Inject
  AnalyticsAdapter(Application application) {
    this.application = application;
  }
}

اتصال بافت فعالیت بااستفاده از @ActivityContext نیز دردسترس است. برای مثال:

کاتلین

class AnalyticsAdapter @Inject constructor(
  @ActivityContext context: Context
) { ... }

// The Activity binding is available without qualifiers.
class AnalyticsAdapter @Inject constructor(
  activity: FragmentActivity
) { ... }

جاوا

public class AnalyticsAdapter {

  private final Context context;

  @Inject
  AnalyticsAdapter(@ActivityContext Context context) {
    this.context = context;
  }
}

// The Activity binding is available without qualifiers.
public class AnalyticsAdapter {

  private final FragmentActivity activity;

  @Inject
  AnalyticsAdapter(FragmentActivity activity) {
    this.activity = activity;
  }
}

تزریق وابستگی‌ها در کلاس‌هایی که Hilt از آن‌ها پشتیبانی نمی‌کند

Hilt با پشتیبانی از رایج‌ترین کلاس‌های Android ارائه می‌شود. بااین‌حال، ممکن است لازم باشد تزریق فیلد را در کلاس‌هایی که Hilt پشتیبانی نمی‌کند انجام دهید.

در این موارد، می‌توانید بااستفاده از حاشیه‌نویسی @EntryPoint نقطه ورودی ایجاد کنید. نقطه ورود مرز بین کدی است که توسط Hilt مدیریت می‌شود و کدی که مدیریت نمی‌شود. این نقطه‌ای است که کد برای اولین‌بار وارد نمودار اشیایی می‌شود که Hilt مدیریت می‌کند. نقاط ورودی به Hilt اجازه می‌دهند از کدی استفاده کند که Hilt آن را مدیریت نمی‌کند تا وابستگی‌ها را در نمودار وابستگی ارائه دهد.

برای مثال، Hilt مستقیماً از ارائه‌دهندگان محتوا پشتیبانی نمی‌کند. اگر می‌خواهید یک ارائه‌دهنده محتوا از Hilt برای دریافت برخی وابستگی‌ها استفاده کند، باید یک میانای حاشیه‌نویسی‌شده با @EntryPoint برای هر نوع اتصال موردنظرتان تعریف کنید و واجدشرایط‌ها را اضافه کنید. سپس @InstallIn را اضافه کنید تا مؤلفه‌ای را که نقطه ورودی باید در آن نصب شود به‌صورت زیر مشخص کنید:

کاتلین

class ExampleContentProvider : ContentProvider() {

  @EntryPoint
  @InstallIn(SingletonComponent::class)
  interface ExampleContentProviderEntryPoint {
    fun analyticsService(): AnalyticsService
  }

  ...
}

جاوا

public class ExampleContentProvider extends ContentProvider {

  @EntryPoint
  @InstallIn(SingletonComponent.class)
  interface ExampleContentProviderEntryPoint {
    public AnalyticsService analyticsService();
  }
  ...
}

برای دسترسی به نقطه ورودی، از روش ایستا مناسب از EntryPointAccessors استفاده کنید. پارامتر باید نمونه مؤلفه یا شیء @AndroidEntryPoint باشد که به‌عنوان نگهدارنده مؤلفه عمل می‌کند. مطمئن شوید که مؤلفه‌ای که به‌عنوان پارامتر ارسال می‌کنید و روش ثابت EntryPointAccessors هر دو با کلاس Android در حاشیه‌نویسی @InstallIn در رابط @EntryPoint مطابقت داشته باشند:

کاتلین

class ExampleContentProvider: ContentProvider() {
    ...

  override fun query(...): Cursor {
    val appContext = context?.applicationContext ?: throw IllegalStateException()
    val hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java)

    val analyticsService = hiltEntryPoint.analyticsService()
    ...
  }
}

جاوا

public class ExampleContentProvider extends ContentProvider {

  @Override
  public Cursor query(...) {
    Context appContext = getContext().getApplicationContext();
    ExampleContentProviderEntryPoint hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class);
    AnalyticsService analyticsService = hiltEntryPoint.analyticsService();
  }
}

در این مثال، باید از ApplicationContext برای بازیابی نقطه ورودی استفاده کنید زیرا نقطه ورودی در SingletonComponent نصب شده است. اگر اتصالی که می‌خواستید بازیابی کنید در ActivityComponent بود، به‌جای آن از ActivityContext استفاده می‌کردید.

Hilt و Dagger

Hilt بر روی کتابخانه تزریق وابستگی Dagger ساخته شده است و روشی استاندارد برای ادغام Dagger در برنامه Android ارائه می‌دهد.

با توجه به Dagger، اهداف Hilt به شرح زیر است:

  • برای ساده کردن زیرساخت‌های مرتبط با Dagger برای برنامه‌های Android.
  • برای ایجاد مجموعه‌ای استاندارد از مؤلفه‌ها و دامنه‌ها برای تسهیل راه‌اندازی، خوانایی، و هم‌رسانی کد بین برنامه‌ها.
  • برای ارائه روشی آسان جهت تأمین اتصال‌های مختلف برای انواع مختلف ساخت، مانند آزمایش، اشکال‌زدایی، یا انتشار.

ازآنجایی‌که سیستم‌عامل Android بسیاری از کلاس‌های چارچوب خود را نمونه‌سازی می‌کند، استفاده از Dagger در برنامه Android مستلزم نوشتن مقدار قابل‌توجهی کد تکراری است. Hilt کد متن استاندارد را که در استفاده از Dagger در برنامه Android دخیل است کاهش می‌دهد. Hilt به‌طور خودکار موارد زیر را تولید و ارائه می‌کند:

  • مؤلفه‌هایی برای ادغام کلاس‌های چارچوب Android با Dagger که درغیراین‌صورت باید به‌صورت دستی ایجاد کنید.
  • حاشیه‌نویسی‌های محدوده برای استفاده با مؤلفه‌هایی که Hilt به‌طور خودکار تولید می‌کند.
  • اتصال‌های ازپیش‌تعریف‌شده برای نمایش کلاس‌های Android مانند Application یا Activity.
  • توصیف‌کننده‌های ازپیش تعریف‌شده برای نشان دادن @ApplicationContext و @ActivityContext.

کد Dagger و Hilt می‌توانند در یک پایگاه کد باهم وجود داشته باشند. بااین‌حال، در اکثر موارد بهتر است از Hilt برای مدیریت همه موارد استفاده از Dagger در Android استفاده کنید. برای انتقال دادن پروژه‌ای که از Dagger به Hilt استفاده می‌کند، راهنمای انتقال را ببینید.