Hilt là một thư viện chèn phần phụ thuộc cho Android, giúp giảm bớt mã nguyên mẫu trong quá trình thực hiện thao tác chèn phần phụ thuộc thủ công vào dự án của bạn. Để chèn phần phụ thuộc theo cách thủ công, bạn cần tự tay tạo từng lớp và các phần phụ thuộc tương ứng, đồng thời dùng vùng chứa để sử dụng lại cũng như quản lý các phần phụ thuộc.
Hilt cung cấp cách thức tiêu chuẩn để sử dụng DI trong ứng dụng của bạn bằng cách cung cấp các vùng chứa cho mọi lớp Android trong dự án, đồng thời tự động quản lý vòng đời của các vùng chứa đó. Hilt được xây dựng dựa trên thư viện DI phổ biến Dagger để hưởng lợi từ độ chính xác của thời gian biên dịch, hiệu suất trong thời gian chạy, khả năng có thể mở rộng và Hỗ trợ Android Studio mà Dagger cung cấp. Để biết thêm thông tin chi tiết, vui lòng xem nội dung Hilt và Dagger.
Hướng dẫn này giải thích các khái niệm cơ bản về Hilt và các vùng chứa đã tạo. Nội dung này cũng bao gồm một hướng dẫn cách khởi động một ứng dụng hiện có để sử dụng Hilt.
Thêm phần phụ thuộc
Trước tiên, hãy thêm trình bổ trợ hilt-android-gradle-plugin
vào tệp
build.gradle
gốc trong dự án của bạn:
Groovy
plugins { ... id 'com.google.dagger.hilt.android' version '2.44' apply false }
Kotlin
plugins { ... id("com.google.dagger.hilt.android") version "2.44" apply false }
Sau đó, áp dụng trình bổ trợ Gradle và thêm các phần phụ thuộc này vào tệp
app/build.gradle
của bạn:
Groovy
... plugins { id 'kotlin-kapt' id 'com.google.dagger.hilt.android' } android { ... } dependencies { implementation "com.google.dagger:hilt-android:2.44" kapt "com.google.dagger:hilt-compiler:2.44" } // Allow references to generated code kapt { correctErrorTypes true }
Kotlin
plugins { id("kotlin-kapt") id("com.google.dagger.hilt.android") } android { ... } dependencies { implementation("com.google.dagger:hilt-android:2.44") kapt("com.google.dagger:hilt-android-compiler:2.44") } // Allow references to generated code kapt { correctErrorTypes = true }
Hilt sử dụng các tính năng của Java 8. Để bật Java 8 trong dự án, hãy thêm đoạn mã sau vào tệp app/build.gradle
:
Groovy
android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
Kotlin
android { ... compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } }
Lớp ứng dụng Hilt
Tất cả ứng dụng sử dụng Hilt phải chứa một lớp Application
được chú thích bằng @HiltAndroidApp
.
@HiltAndroidApp
kích hoạt việc tạo mã của Hilt, bao gồm một lớp cơ sở cho ứng dụng của bạn, đóng vai trò là vùng chứa phần phụ thuộc cấp ứng dụng.
Kotlin
@HiltAndroidApp class ExampleApplication : Application() { ... }
Java
@HiltAndroidApp public class ExampleApplication extends Application { ... }
Thành phần Hilt đã tạo này được gắn vào vòng đời của đối tượng Application
và cung cấp các phần phụ thuộc tương ứng. Ngoài ra, đây là thành phần mẹ của ứng dụng, nghĩa là các thành phần khác có thể truy cập vào các phần phụ thuộc mà thành phần Hilt cung cấp.
Chèn phần phụ thuộc vào các lớp Android
Sau khi được thiết lập trong lớp Application
và có sẵn một thành phần cấp ứng dụng, Hilt có thể cung cấp phần phụ thuộc cho các lớp Android khác với chú thích @AndroidEntryPoint
:
Kotlin
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { ... }
Java
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { ... }
Hilt hiện hỗ trợ các lớp Android sau:
Application
(bằng cách sử dụng@HiltAndroidApp
)ViewModel
(bằng cách sử dụng@HiltViewModel
)Activity
Fragment
View
Service
BroadcastReceiver
Nếu chú thích một lớp Android bằng @AndroidEntryPoint
, thì bạn cũng phải chú giải các lớp Android phụ thuộc vào lớp đó. Chẳng hạn như nếu bạn chú thích một mảnh, thì bạn cũng phải chú thích mọi hoạt động mà bạn sử dụng mảnh đó.
@AndroidEntryPoint
tạo một thành phần Hilt riêng lẻ cho từng lớp Android trong dự án. Các thành phần này có thể nhận được phần phụ thuộc từ các lớp cha tương ứng như mô tả trong Hệ phân cấp thành phần.
Để lấy các phần phụ thuộc từ một thành phần, hãy sử dụng chú thích @Inject
để thực hiện thao tác chèn trường:
Kotlin
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var analytics: AnalyticsAdapter ... }
Java
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { @Inject AnalyticsAdapter analytics; ... }
Các lớp do Hilt chèn vào có thể có các lớp cơ sở khác cũng sử dụng tính năng chèn.
Các lớp đó không cần chú thích @AndroidEntryPoint
nếu chúng mang tính trừu tượng.
Để tìm hiểu thêm về phương thức gọi lại trong vòng đời mà một lớp Android được chèn vào, vui lòng xem phần Vòng đời của thành phần.
Xác định các liên kết Hilt
Để thực hiện thao tác chèn trường, Hilt cần biết cách cung cấp thực thể của các phần phụ thuộc cần thiết từ thành phần tương ứng. Liên kết chứa thông tin cần thiết để cung cấp các phiên bản của một loại dưới dạng phần phụ thuộc.
Một cách để cung cấp thông tin liên kết cho Hilt là sử dụng tính năng chèn hàm khởi tạo. Hãy sử dụng chú thích @Inject
trên hàm khởi tạo của một lớp để cho Hilt biết cách cung cấp các bản sao của lớp đó:
Kotlin
class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
Java
public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
Các tham số của một hàm khởi tạo có chú thích của một lớp là các phần phụ thuộc của lớp đó. Trong ví dụ, AnalyticsAdapter
có AnalyticsService
là phần phụ thuộc. Do đó, Hilt cũng phải biết cách cung cấp các bản sao của AnalyticsService
.
Mô-đun Hilt
Đôi khi không thể chèn một loại vào hàm khởi tạo. Điều này có thể xảy ra vì nhiều lý do. Ví dụ như bạn không thể chèn một giao diện vào hàm khởi tạo. Bạn cũng không thể chèn một loại mà bạn không sở hữu vào hàm khởi tạo, chẳng hạn như một lớp từ thư viện bên ngoài. Trong những trường hợp này, bạn có thể cung cấp cho Hilt thông tin liên kết bằng cách dùng mô-đun Hilt.
Mô-đun Hilt là một lớp được chú thích bằng @Module
. Giống như một mô-đun Dagger, mô-đun này sẽ thông báo cho Hilt cách cung cấp các phiên bản của một số loại nhất định. Không giống như mô-đun Dagger, bạn phải chú thích các mô-đun Hilt bằng @InstallIn
để cho Hilt biết mỗi mô-đun sẽ được sử dụng hoặc cài đặt trong lớp Android nào.
Các phần phụ thuộc mà bạn cung cấp trong mô-đun Hilt có sẵn trong tất cả các thành phần được tạo liên kết với lớp Android nơi bạn cài đặt mô-đun Hilt.
Chèn thực thể giao diện bằng @Binds
Hãy xem xét ví dụ về AnalyticsService
. Nếu AnalyticsService
là một giao diện, thì bạn không thể dùng nó để chèn hàm khởi tạo. Thay vào đó, hãy cung cấp thông tin liên kết cho Hilt bằng cách tạo một hàm trừu tượng có chú thích bằng @Binds
bên trong mô-đun Hilt.
Chú thích @Binds
sẽ cho Hilt biết phương thức triển khai nào được sử dụng khi cần cung cấp một bản sao của giao diện.
Hàm được chú thích sẽ cung cấp các thông tin sau cho Hilt:
- Loại dữ liệu trả về của hàm cho Hilt biết bản sao của giao diện mà hàm cung cấp.
- Tham số hàm cho Hilt biết phương thức triển khai nào cần cung cấp.
Kotlin
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 }
Java
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 ); }
Mô-đun Hilt AnalyticsModule
được chú thích bằng @InstallIn(ActivityComponent.class)
vì bạn muốn Hilt chèn phần phụ thuộc đó vào ExampleActivity
. Chú thích này có nghĩa là tất cả phần phụ thuộc trong AnalyticsModule
đều có trong mọi hoạt động của ứng dụng.
Chèn thực thể bằng @Provides
Giao diện không phải là trường hợp duy nhất nơi bạn không thể chèn hàm khởi tạo.
Bạn cũng không thể chèn hàm khởi tạo nếu bạn không sở hữu lớp đó vì nó đến từ thư viện bên ngoài (chẳng hạn như các lớp
Retrofit, OkHttpClient
hoặc Cơ sở dữ liệu phòng), hoặc nếu bản sao phải được tạo bằngmẫu trình tạo .
Hãy xem xét ví dụ trước. Nếu không trực tiếp sở hữu lớp AnalyticsService
, thì bạn có thể cho Hilt biết cách cung cấp các bản sao của loại này bằng cách tạo một hàm bên trong mô-đun Hilt và chú thích hàm đó bằng @Provides
.
Hàm được chú thích cung cấp các thông tin sau cho Hilt:
- Loại dữ liệu trả về của hàm cho Hilt biết loại mà hàm cung cấp các bản sao.
- Các tham số hàm cho Hilt biết các phần phụ thuộc của loại tương ứng.
- Phần nội dung hàm cho Hilt biết cách cung cấp một bản sao của loại tương ứng. Hilt thực thi phần nội dung hàm mỗi khi cần cung cấp một phiên bản của loại đó.
Kotlin
@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) } }
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); } }
Cung cấp nhiều liên kết cho cùng một loại
Trong trường hợp bạn cần Hilt cung cấp các cách triển khai khác nhau của cùng một loại làm phần phụ thuộc, bạn phải cung cấp cho Hilt nhiều đường liên kết. Bạn có thể xác định nhiều liên kết cho cùng một loại bằng bộ hạn định.
Bộ hạn định là chú thích mà bạn dùng để xác định một mối liên kết cụ thể cho một loại khi loại đó có nhiều liên kết được xác định.
Hãy xem xét ví dụ. Nếu cần chặn các lệnh gọi đến AnalyticsService
, bạn có thể sử dụng đối tượng OkHttpClient
bằng một trình chặn. Đối với các dịch vụ khác, bạn có thể cần chặn lệnh gọi theo một cách khác. Trong trường hợp đó, bạn cần cho Hilt biết cách cung cấp hai cách triển khai OkHttpClient
khác nhau.
Trước tiên, hãy xác định những bộ hạn định bạn sẽ sử dụng để chú thích các phương thức @Binds
hoặc
@Provides
:
Kotlin
@Qualifier @Retention(AnnotationRetention.BINARY) annotation class AuthInterceptorOkHttpClient @Qualifier @Retention(AnnotationRetention.BINARY) annotation class OtherInterceptorOkHttpClient
Java
@Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface AuthInterceptorOkHttpClient {} @Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface OtherInterceptorOkHttpClient {}
Sau đó, Hilt cần biết cách cung cấp một bản sao của loại tương ứng với từng bộ hạn định. Trong trường hợp này, bạn có thể sử dụng mô-đun Hilt với @Provides
.
Cả hai phương thức đều có cùng một loại dữ liệu trả về, nhưng những bộ hạn định này gắn nhãn chúng dưới dạng hai liên kết khác nhau:
Kotlin
@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() } }
Java
@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(); } }
Bạn có thể chèn loại cụ thể mà bạn cần bằng cách chú thích trường hoặc tham số bằng bộ hạn định tương ứng:
Kotlin
// 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 }
Java
// 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; ... }
Phương pháp hay nhất là nếu bạn thêm bộ hạn định vào một loại, hãy thêm bộ hạn định vào bằng mọi cách có thể để cung cấp phần phụ thuộc đó. Nếu bạn rời khỏi việc triển khai cơ sở hoặc triển khai chung mà không có bộ hạn định thì rất dễ xảy ra lỗi, ngoài ra còn có thể dẫn đến tình trạng chèn sai phần phụ thuộc.
Bộ hạn định được xác định trước trong Hilt
Hilt cung cấp một số bộ hạn định được xác định trước. Chẳng hạn như vì bạn có thể cần lớp Context
từ ứng dụng hoặc hoạt động, nên Hilt sẽ cung cấp bộ hạn định @ApplicationContext
và @ActivityContext
.
Giả sử lớp AnalyticsAdapter
từ ví dụ cần ngữ cảnh của hoạt động. Đoạn mã sau đây minh hoạ cách cung cấp ngữ cảnh hoạt động cho AnalyticsAdapter
:
Kotlin
class AnalyticsAdapter @Inject constructor( @ActivityContext private val context: Context, private val service: AnalyticsService ) { ... }
Java
public class AnalyticsAdapter { private final Context context; private final AnalyticsService service; @Inject AnalyticsAdapter( @ActivityContext Context context, AnalyticsService service ) { this.context = context; this.service = service; } }
Để biết các liên kết được xác định trước khác có sẵn trong Hilt, vui lòng xem nội dung phần Liên kết mặc định của thành phần.
Các thành phần đã tạo cho lớp Android
Đối với mỗi lớp Android mà bạn có thể thực hiện thao tác chèn trường, có một thành phần Hilt đi kèm mà bạn có thể tham khảo trong chú thích @InstallIn
.
Mỗi thành phần Hilt chịu trách nhiệm chèn các liên kết vào lớp Android tương ứng.
Các ví dụ trước cho thấy cách sử dụng ActivityComponent
trong các mô-đun Hilt.
Hilt cung cấp các thành phần sau:
Thành phần Hilt | Trình chèn cho |
---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
Không áp dụng |
ViewModelComponent |
ViewModel |
ActivityComponent |
Activity |
FragmentComponent |
Fragment |
ViewComponent |
View |
ViewWithFragmentComponent |
View được chú thích bằng @WithFragmentBindings |
ServiceComponent |
Service |
Vòng đời của thành phần
Hilt tự động tạo và huỷ các phiên bản của lớp thành phần được tạo theo vòng đời của các lớp Android tương ứng.
Thành phần đã tạo | Được tạo ở | Bị hủy ở |
---|---|---|
SingletonComponent |
Application#onCreate() |
Application đã huỷ bỏ |
ActivityRetainedComponent |
Activity#onCreate() |
Activity#onDestroy() |
ViewModelComponent |
Đã tạo ViewModel |
Đã huỷ bỏ ViewModel |
ActivityComponent |
Activity#onCreate() |
Activity#onDestroy() |
FragmentComponent |
Fragment#onAttach() |
Fragment#onDestroy() |
ViewComponent |
View#super() |
Đã huỷ bỏ View |
ViewWithFragmentComponent |
View#super() |
Đã hủy bỏ View |
ServiceComponent |
Service#onCreate() |
Service#onDestroy() |
Các phạm vi thành phần
Theo mặc định, tất cả các đường liên kết trong Hilt đều không có phạm vi. Tức là mỗi khi ứng dụng yêu cầu liên kết, Hilt sẽ tạo một bản sao mới của loại cần thiết.
Ở ví dụ này, mỗi khi Hilt cung cấp AnalyticsAdapter
dưới dạng một phần phụ thuộc vào một loại khác hoặc thông qua tính năng chèn trường (như trong ExampleActivity
), Hilt sẽ cung cấp một thực thể mới của AnalyticsAdapter
.
Tuy nhiên, Hilt cũng cho phép đưa liên kết vào một thành phần cụ thể. Hilt chỉ tạo liên kết theo phạm vi một lần cho mỗi thực thể của thành phần mà liên kết đó được xác định phạm vi và mọi yêu cầu cho liên kết đó đều chia sẻ cùng một thực thể.
Bảng dưới đây liệt kê các chú thích phạm vi cho từng thành phần được tạo:
Lớp Android | Thành phần đã tạo | Phạm vi |
---|---|---|
Application |
SingletonComponent |
@Singleton |
Activity |
ActivityRetainedComponent |
@ActivityRetainedScoped |
ViewModel |
ViewModelComponent |
@ViewModelScoped |
Activity |
ActivityComponent |
@ActivityScoped |
Fragment |
FragmentComponent |
@FragmentScoped |
View |
ViewComponent |
@ViewScoped |
View được chú thích bằng @WithFragmentBindings |
ViewWithFragmentComponent |
@ViewScoped |
Service |
ServiceComponent |
@ServiceScoped |
Trong ví dụ, nếu bạn đặt phạm vi của AnalyticsAdapter
thành ActivityComponent
bằng @ActivityScoped
, thì Hilt sẽ cung cấp cùng một phiên bản của AnalyticsAdapter
trong suốt thời gian của hoạt động tương ứng:
Kotlin
@ActivityScoped class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
Java
@ActivityScoped public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
Giả sử AnalyticsService
có trạng thái nội bộ yêu cầu sử dụng cùng một phiên bản mọi lúc — không chỉ trong ExampleActivity
mà còn ở mọi nơi trong ứng dụng. Trong trường hợp này, bạn nên đặt phạm vi của AnalyticsService
thành SingletonComponent
. Theo đó, bất cứ khi nào thành phần cần cung cấp một phiên bản của AnalyticsService
, thành phần đó sẽ cung cấp cùng một phiên bản mọi lúc.
Ví dụ sau minh họa cách xác định phạm vi liên kết với một thành phần trong
mô-đun Hilt. Phạm vi của liên kết phải khớp với phạm vi của thành phần nơi nó được cài đặt, vì vậy trong ví dụ này, bạn phải cài đặt AnalyticsService
trong SingletonComponent
thay vì ActivityComponent
:
Kotlin
// 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) } }
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); } }
Để tìm hiểu thêm về phạm vi thành phần trong Hilt, vui lòng xem nội dung phần Xác định phạm vi trong Android và Hilt.
Hệ phân cấp thành phần
Việc cài đặt mô-đun vào một thành phần cho phép các liên kết của mô-đun đó được truy cập dưới dạng phần phụ thuộc của các liên kết khác trong thành phần đó hoặc trong bất kỳ thành phần con nào bên dưới nó trong hệ thống phân cấp thành phần:
Liên kết mặc định của thành phần
Mỗi thành phần Hilt đi kèm với một tập hợp các liên kết mặc định mà Hilt có thể chèn dưới dạng phần phụ thuộc vào các liên kết tuỳ chỉnh của riêng bạn. Lưu ý là các liên kết này tương ứng với hoạt động và các loại mảnh chung chứ không phải bất kỳ lớp con cụ thể nào. Điều này là do Hilt sử dụng một định nghĩa thành phần hoạt động duy nhất để chèn tất cả các hoạt động. Mỗi hoạt động có một bản sao khác của thành phần này.
Thành phần Android | Các liên kết mặc định |
---|---|
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 |
Liên kết ngữ cảnh ứng dụng cũng có sẵn bằng cách sử dụng @ApplicationContext
.
Ví dụ:
Kotlin
class AnalyticsServiceImpl @Inject constructor( @ApplicationContext context: Context ) : AnalyticsService { ... } // The Application binding is available without qualifiers. class AnalyticsServiceImpl @Inject constructor( application: Application ) : AnalyticsService { ... }
Java
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; } }
Bạn cũng có thể liên kết ngữ cảnh hoạt động bằng cách sử dụng @ActivityContext
. Ví dụ:
Kotlin
class AnalyticsAdapter @Inject constructor( @ActivityContext context: Context ) { ... } // The Activity binding is available without qualifiers. class AnalyticsAdapter @Inject constructor( activity: FragmentActivity ) { ... }
Java
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; } }
Chèn các phần phụ thuộc vào các lớp không được Hilt hỗ trợ
Hilt đi kèm với tính năng hỗ trợ cho các lớp phổ biến nhất trên Android. Tuy nhiên, bạn có thể cần chèn trường trong các lớp mà Hilt không hỗ trợ.
Trong những trường hợp đó, bạn có thể tạo một điểm truy cập bằng cách sử dụng chú thích @EntryPoint
. Điểm truy cập là ranh giới giữa mã do Hilt quản lý và mã không phải do Hilt quản lý. Đây là điểm đầu tiên mã nhập vào biểu đồ của các đối tượng mà Hilt quản lý. Điểm truy cập cho phép Hilt sử dụng mã mà Hilt không quản lý để cung cấp các phần phụ thuộc trong biểu đồ phần phụ thuộc.
Chẳng hạn như Hilt không trực tiếp hỗ trợ các nhà cung cấp nội dung. Nếu muốn nhà cung cấp nội dung sử dụng Hilt để lấy một số phần phụ thuộc, thì bạn cần xác định giao diện được chú thích bằng @EntryPoint
cho từng loại liên kết mà bạn muốn và đưa bộ hạn định vào. Sau đó, hãy thêm @InstallIn
để chỉ định thành phần cần cài đặt điểm truy cập như sau:
Kotlin
class ExampleContentProvider : ContentProvider() { @EntryPoint @InstallIn(SingletonComponent::class) interface ExampleContentProviderEntryPoint { fun analyticsService(): AnalyticsService } ... }
Java
public class ExampleContentProvider extends ContentProvider { @EntryPoint @InstallIn(SingletonComponent.class) interface ExampleContentProviderEntryPoint { public AnalyticsService analyticsService(); } ... }
Để truy cập vào một điểm truy cập, hãy sử dụng phương thức tĩnh thích hợp từ EntryPointAccessors
. Tham số này phải là bản sao của thành phần hoặc đối tượng @AndroidEntryPoint
, đóng vai trò là chủ thể thành phần. Hãy đảm bảo là thành phần bạn truyền dưới dạng tham số và phương thức tĩnh EntryPointAccessors
đều khớp với lớp Android trong chú thích @InstallIn
trên giao diện @EntryPoint
:
Kotlin
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() ... } }
Java
public class ExampleContentProvider extends ContentProvider { @Override public Cursor query(...) { Context appContext = getContext().getApplicationContext(); ExampleContentProviderEntryPoint hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class); AnalyticsService analyticsService = hiltEntryPoint.analyticsService(); } }
Trong ví dụ này, bạn phải sử dụng ApplicationContext
để truy xuất điểm truy cập vì điểm truy cập đó được cài đặt trong SingletonComponent
. Nếu liên kết mà bạn muốn truy xuất nằm trong ActivityComponent
, bạn nên dùng ActivityContext
.
Hilt và Dagger
Hilt được xây dựng dựa trên thư viện chèn phần phụ thuộc Dagger, cung cấp cách thức tiêu chuẩn để tích hợp Dagger vào ứng dụng Android.
Đối với Dagger, mục tiêu của Hilt sẽ như sau:
- Đơn giản hóa cơ sở hạ tầng liên quan đến Dagger cho các ứng dụng Android.
- Tạo một nhóm thành phần và phạm vi tiêu chuẩn để dễ dàng thiết lập, dễ đọc và chia sẻ mã giữa các ứng dụng.
- Mang đến phương thức dễ dàng để cung cấp nhiều mối liên kết cho nhiều loại bản dựng, chẳng hạn như kiểm thử, gỡ lỗi hoặc phát hành.
Vì hệ điều hành Android tạo bản sao của nhiều lớp khung cho riêng nó, nên việc sử dụng Dagger trong ứng dụng Android sẽ yêu cầu bạn viết một lượng lớn các mã nguyên mẫu. Hilt sẽ giúp giảm mã nguyên mẫu có liên quan đến việc sử dụng Dagger trong ứng dụng Android. Hilt tự động tạo và cung cấp các mục sau:
- Các thành phần để tích hợp lớp khung Android với Dagger mà bạn cần phải tạo theo cách thủ công nếu không có các thành phần này.
- Chú thích phạm vi để sử dụng với các thành phần mà Hilt tạo tự động.
- Các đường liên kết được xác định trước để đại diện cho các lớp Android, chẳng hạn như
Application
hoặcActivity
. - Bộ hạn định xác định trước đại diện cho
@ApplicationContext
và@ActivityContext
.
Mã Dagger và Hilt có thể cùng tồn tại trong cùng một cơ sở mã. Tuy nhiên, trong hầu hết các trường hợp, bạn nên sử dụng Hilt để quản lý toàn bộ việc sử dụng Dagger trên Android. Để di chuyển một dự án sử dụng Dagger sang Hilt, vui lòng xem hướng dẫn di chuyển và Lớp học lập trình về di chuyển ứng dụng Dagger sang Hilt.
Tài nguyên khác
Để tìm hiểu thêm về Hilt, vui lòng xem các tài nguyên bổ sung sau đây:
Mẫu
Lớp học lập trình
Blog
- Chèn phần phụ thuộc trên Android bằng Hilt
- Xác định phạm vi trong Android và Hilt
- Thêm các thành phần vào hệ phân cấp Hilt
- Di chuyển ứng dụng Google I/O sang Hilt