HiltBinder
An annotation processor library that automatically creates Hilt's @Binds
functions and modules.
If you think this library is useful, please press ⭐️ Star
button at upside : )
- How to use
- Basic usage
- Options
- Caution
- Multibinding
- Supported
- More Sample Code
- Performance monitoring
- License
# How to use
Can be used in android project with hilt applied. And add dependency like below code.
// build.gradle(:project)
repositories {
google()
mavenCentral()
}
// build.gradle(:app)
dependencies {
def hiltBinderVersion = "1.3.0"
implementation "com.smparkworld.hiltbinder:hiltbinder:$hiltBinderVersion"
kapt "com.smparkworld.hiltbinder:hiltbinder-processor:$hiltBinderVersion"
}
# Basic usage
No longer need abstract module classes. Just add @HiltBinds
and the Binds module will be created automatically.
interface TestUseCase {
fun printTestString()
}
@HiltBinds
class TestUseCaseImpl @Inject constructor(
private val testString: String
) : TestUseCase {
override fun printTestString() {
Log.d("Test!!", "TestString is $testString in UseCase.")
}
}
And the processor automatically generates the following Java files:
@Module
@InstallIn(SingletonComponent.class)
abstract class TestUseCaseImpl_BindsModule {
@Binds
public abstract TestUseCase bindTestUseCaseImpl(TestUseCaseImpl target);
}
# Options
The return type of the Binds abstract function.
interface OtherSpec {
...
}
interface ToSampleModel {
fun printTestString()
}
@HiltBinds(to = ToSampleModel::class)
class ToSampleModelImpl @Inject constructor(
private val testString: String
) : OtherSpec, ToSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in ToSampleModelImpl class.")
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class ToSampleModelImpl_BindsModule {
@Binds
public abstract ToSampleModel bindToSampleModelImpl(ToSampleModelImpl target);
}
The argument type of the Binds abstract function. However, from an architectural point of view, this is not recommended.
@HiltBinds(from = FromSampleModelImpl::class)
interface FromSampleModel {
fun printTestString()
}
class FromSampleModelImpl @Inject constructor(
private val testString: String
) : FromSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in FromSampleModelImpl class.")
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class FromSampleModel_BindsModule {
@Binds
public abstract FromSampleModel bindFromSampleModel(FromSampleModelImpl target);
}
Specifies in which component the class to be returned will be installed.
interface ComponentSampleModel {
fun printTestString()
}
@HiltBinds(component = ActivityRetainedComponent::class)
class ComponentSampleModelImpl @Inject constructor(
private val testString: String
) : ComponentSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in ComponentSampleModelImpl class.")
}
}
// generated code
@Module
@InstallIn(ActivityRetainedComponent.class)
abstract class ComponentSampleModelImpl_BindsModule {
@Binds
public abstract ComponentSampleModel bindComponentSampleModelImpl(ComponentSampleModelImpl target);
}
To specify ranges separately, apply scope annotations as in the following code snippet. The reason this can work is that applying a scope to the implementing class works to keep the singleton in scope via the
dagger.internal.DoubleCheck
class within the Hilt.
interface ComponentSampleModel {
fun printTestString()
}
@HiltBinds(component = ActivityRetainedComponent::class)
@ActivityRetainedScope
class ComponentSampleModelImpl @Inject constructor(
private val testString: String
) : ComponentSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in ComponentSampleModelImpl class.")
}
}
or
@HiltBinds
@Singleton
class SomethingSampleModelImpl @Inject constructor(
private val testString: String
) : SomethingSampleModel {
...
}
The Qualifier annotation to be applied to the return type.
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleQualifier1
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleQualifier2
interface QualifierSampleModel {
fun printTestString()
}
@HiltBinds
@SampleQualifier1
class QualifierSampleModelImpl1 @Inject constructor(
private val testString: String
) : QualifierSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in QualifierSampleModelImpl1 class.")
}
}
@HiltBinds
@SampleQualifier2
class QualifierSampleModelImpl2 @Inject constructor(
private val testString: String
) : QualifierSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in QualifierSampleModelImpl2 class.")
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifierSampleModelImpl1_BindsModule {
@Binds
@SampleQualifier1
public abstract QualifierSampleModel bindQualifierSampleModelImpl1(QualifierSampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifierSampleModelImpl2_BindsModule {
@Binds
@SampleQualifier2
public abstract QualifierSampleModel bindQualifierSampleModelImpl2(QualifierSampleModelImpl2 target);
}
The Qualifier annotation to be applied to the return type.
interface NamedSampleModel {
fun printTestString()
}
@HiltBinds
@Named("model1")
class NamedSampleModelImpl1 @Inject constructor(
private val testString: String
) : NamedSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in NamedSampleModelImpl1 class.")
}
}
@HiltBinds
@Named("model2")
class NamedSampleModelImpl2 @Inject constructor(
private val testString: String
) : NamedSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in NamedSampleModelImpl2 class.")
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedSampleModelImpl1_BindsModule {
@Binds
@Named("model1")
public abstract NamedSampleModel bindNamedSampleModelImpl1(NamedSampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedSampleModelImpl2_BindsModule {
@Binds
@Named("model2")
public abstract NamedSampleModel bindNamedSampleModelImpl2(NamedSampleModelImpl2 target);
}
✋
CAUTION HERE parameter
to
andfrom
must not be signed together. Eitherto
orfrom
must be used. If they are signed at the same time, throws an exception. Because dependency injection can be attempted from other unrelated classes as in the code below.
@HiltBinds(
to = SampleModel::class,
from = SampleModelImpl::class
)
interface SomethingClass // throws an exception.
# MultiBinding
You must use
@HiltSetBinds
to apply Set Multibinding.
interface SetSampleModel {
fun printTestString()
}
@HiltSetBinds
class SetSampleModelImpl1 @Inject constructor(
private val testString: String
) : SetSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in SetSampleModelImpl1 class.")
}
}
@HiltSetBinds
class SetSampleModelImpl2 @Inject constructor(
private val testString: String
) : SetSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in SetSampleModelImpl2 class.")
}
}
// This is the code to get Set Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var sampleSet: @JvmSuppressWildcards Set<SetSampleModel>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sampleSet.forEach {
it.printTestString()
}
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class SetSampleModelImpl1_BindsModule {
@Binds
@IntoSet
public abstract SetSampleModel bindSetSampleModelImpl1(SetSampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class SetSampleModelImpl2_BindsModule {
@Binds
@IntoSet
public abstract SetSampleModel bindSetSampleModelImpl2(SetSampleModelImpl2 target);
}
If you want to configure multiple
Set Multibinding
of the same type, you can use @Qualifier(javax.inject.Qualifier
) annotations like this:
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleSetQualifierA
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleSetQualifierB
interface QualifiedSetSampleModel {
fun printTestString()
}
@HiltSetBinds
@SampleSetQualifierA
class QualifiedSetSampleModelImpl1 @Inject constructor(
private val testString: String
) : QualifiedSetSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in QualifiedSetSampleModelImpl1 class.")
}
}
@HiltSetBinds
@SampleSetQualifierB
class QualifiedSetSampleModelImpl2 @Inject constructor(
private val testString: String
) : QualifiedSetSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in QualifiedSetSampleModelImpl2 class.")
}
}
// This is the code to get Set Multibinding - qualifier.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
@SampleSetQualifierA
lateinit var sampleQualifiedSetA: @JvmSuppressWildcards Set<QualifiedSetSampleModel>
@Inject
@SampleSetQualifierB
lateinit var sampleQualifiedSetB: @JvmSuppressWildcards Set<QualifiedSetSampleModel>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sampleQualifiedSetA.forEach { it.printTestString() }
sampleQualifiedSetB.forEach { it.printTestString() }
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifiedSetSampleModelImpl1_BindsModule {
@Binds
@IntoSet
@SampleSetQualifierA
public abstract QualifiedSetSampleModel bindQualifiedSetSampleModelImpl1(QualifiedSetSampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifiedSetSampleModelImpl2_BindsModule {
@Binds
@IntoSet
@SampleSetQualifierB
public abstract QualifiedSetSampleModel bindQualifiedSetSampleModelImpl2(QualifiedSetSampleModelImpl2 target);
}
If you want to configure multiple
Set Multibinding
of the same type, you can use @Named(javax.inject.Named
) annotations like this:
interface NamedSetSampleModel {
fun printTestString()
}
@HiltSetBinds
@Named("sampleNamedSetA")
class NamedSetSampleModelImpl1 @Inject constructor(
private val testString: String
) : NamedSetSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in NamedSetSampleModelImpl1 class.")
}
}
@HiltSetBinds
@Named("sampleNamedSetB")
class NamedSetSampleModelImpl2 @Inject constructor(
private val testString: String
) : NamedSetSampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in NamedSetSampleModelImpl2 class.")
}
}
// This is the code to get Set Multibinding - named.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
@Named("sampleNamedSetA")
lateinit var sampleNamedSetA: @JvmSuppressWildcards Set<NamedSetSampleModel>
@Inject
@Named("sampleNamedSetB")
lateinit var sampleNamedSetB: @JvmSuppressWildcards Set<NamedSetSampleModel>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sampleNamedSetA.forEach { it.printTestString() }
sampleNamedSetB.forEach { it.printTestString() }
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedSetSampleModelImpl1_BindsModule {
@Binds
@IntoSet
@Named("sampleNamedSetA")
public abstract NamedSetSampleModel bindNamedSetSampleModelImpl1(NamedSetSampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedSetSampleModelImpl2_BindsModule {
@Binds
@IntoSet
@Named("sampleNamedSetB")
public abstract NamedSetSampleModel bindNamedSetSampleModelImpl2(NamedSetSampleModelImpl2 target);
}
You must use
@HiltMapBinds
to applyMap Multibinding
. And you must to add a Key annotation with hilt's@MapKey
applied, as in the code below. You can use the@ClassKey
,@StringKey
,@IntKey
,@LongKey
provided by hilt.
interface MapStringKeySampleModel {
fun printTestString()
}
@HiltMapBinds
@StringKey("model1")
class MapStringKeySampleModelImpl1 @Inject constructor(
private val testString: String
) : MapStringKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in MapStringKeySampleModelImpl1 class.")
}
}
@HiltMapBinds
@StringKey("model2")
class MapStringKeySampleModelImpl2 @Inject constructor(
private val testString: String
) : MapStringKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in MapStringKeySampleModelImpl2 class.")
}
}
// This is the code to get Map Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var stringKeySampleMap: @JvmSuppressWildcards Map<String, Provider<MapStringKeySampleModel>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
for ((k, v) in stringKeySampleMap) {
Log.d("Test!!", "key: $k")
v.get().printTestString()
}
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class MapStringKeySampleModelImpl1_BindsModule {
@Binds
@IntoMap
@StringKey("model1")
public abstract MapStringKeySampleModel bindMapStringKeySampleModelImpl1(MapStringKeySampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class MapStringKeySampleModelImpl2_BindsModule {
@Binds
@IntoMap
@StringKey("model2")
public abstract MapStringKeySampleModel bindMapStringKeySampleModelImpl2(MapStringKeySampleModelImpl2 target);
}
And you can define and use map key annotations. In addition to enum classes, you can define other types.
enum class SampleType {
SAMPLE1, SAMPLE2, DEFAULT
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class SampleMapCustomKey(val key: SampleType)
interface MapCustomKeySampleModel {
fun printTestString()
}
@HiltMapBinds
@SampleMapCustomKey(SampleType.SAMPLE1)
class MapCustomKeySampleModelImpl1 @Inject constructor(
private val testString: String
) : MapCustomKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in MapCustomKeySampleModelImpl1 class.")
}
}
@HiltMapBinds
@SampleMapCustomKey(SampleType.SAMPLE2)
class MapCustomKeySampleModelImpl2 @Inject constructor(
private val testString: String
) : MapCustomKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in MapCustomKeySampleModelImpl2 class.")
}
}
// This is the code to get Map Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var customKeySampleMap: @JvmSuppressWildcards Map<SampleType, Provider<MapCustomKeySampleModel>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
for ((k, v) in customKeySampleMap) {
Log.d("Test!!", "key: $k")
v.get().printTestString()
}
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class MapCustomKeySampleModelImpl1_BindsModule {
@Binds
@IntoMap
@SampleMapCustomKey(key = SampleType.SAMPLE1)
public abstract MapCustomKeySampleModel bindMapCustomKeySampleModelImpl1(MapCustomKeySampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class MapCustomKeySampleModelImpl2_BindsModule {
@Binds
@IntoMap
@SampleMapCustomKey(key = SampleType.SAMPLE2)
public abstract MapCustomKeySampleModel bindMapCustomKeySampleModelImpl2(MapCustomKeySampleModelImpl2 target);
}
You can use key annotations with multiple parameters as in the code below. Complex custom keys require dependencies from the
auto-value
andauto-value-annotation
libraries. For more information, see References.
/***
* Complex key require the following dependencies:
*
* def autoValueVersion = "1.9"
* implementation "com.google.auto.value:auto-value:$autoValueVersion"
* implementation "com.google.auto.value:auto-value-annotations:$autoValueVersion"
*/
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MapKey(unwrapValue = false)
annotation class SampleMapComplexKey(
val key1: String,
val key2: KClass<*>,
val key3: Array<String>,
val key4: IntArray,
val key5: SampleType
)
interface MapComplexKeySampleModel {
fun printTestString()
}
@HiltMapBinds
@SampleMapComplexKey(
key1 = "sample1",
key2 = MapComplexKeySampleModelImpl1::class,
key3 = ["s1", "s2", "s3"],
key4 = [1, 2, 3],
key5 = SampleType.SAMPLE1
)
class MapComplexKeySampleModelImpl1 @Inject constructor(
private val testString: String
) : MapComplexKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in MapComplexKeySampleModelImpl1 class.");
}
}
@HiltMapBinds
@SampleMapComplexKey(
key1 = "sample2",
key2 = MapComplexKeySampleModelImpl2::class,
key3 = ["s4", "s5", "s6"],
key4 = [4, 5, 6],
key5 = SampleType.SAMPLE2
)
class MapComplexKeySampleModelImpl2 @Inject constructor(
private val testString: String
) : MapComplexKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in MapComplexKeySampleModelImpl2 class.");
}
}
// This is the code to get Map Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var complexKeySampleMap: @JvmSuppressWildcards Map<SampleMapComplexKey, Provider<MapComplexKeySampleModel>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
for ((k, v) in complexKeySampleMap) {
Log.d("Test!!", "key: $k")
v.get().printTestString()
}
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class MapComplexKeySampleModelImpl1_BindsModule {
@Binds
@IntoMap
@SampleMapComplexKey(
key1 = "sample1",
key2 = MapComplexKeySampleModelImpl1.class,
key3 = {"s1","s2","s3"},
key4 = {1,2,3},
key5 = SampleType.SAMPLE1
)
public abstract MapComplexKeySampleModel bindMapComplexKeySampleModelImpl1(MapComplexKeySampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class MapComplexKeySampleModelImpl2_BindsModule {
@Binds
@IntoMap
@SampleMapComplexKey(
key1 = "sample2",
key2 = MapComplexKeySampleModelImpl2.class,
key3 = {"s4","s5","s6"},
key4 = {4,5,6},
key5 = SampleType.SAMPLE2
)
public abstract MapComplexKeySampleModel bindMapComplexKeySampleModelImpl2(MapComplexKeySampleModelImpl2 target);
}
If you want to configure multiple
Map Multibinding
of the same type, you can use @Qualifier(javax.inject.Qualifier
) annotations like this:
enum class SampleKey {
KEY1, KEY2, KEY3, KEY4, DEFAULT
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class QualifiedSampleMapCustomKey(val key: SampleKey)
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleMapQualifierA
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleMapQualifierB
interface QualifiedMapCustomKeySampleModel {
fun printTestString()
}
@HiltMapBinds
@QualifiedSampleMapCustomKey(SampleKey.KEY1)
@SampleMapQualifierA
class QualifiedMapCustomKeySampleModelImpl1 @Inject constructor(
private val testString: String
) : QualifiedMapCustomKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in QualifiedMapCustomKeySampleModelImpl1 class.")
}
}
@HiltMapBinds
@QualifiedSampleMapCustomKey(SampleKey.KEY2)
@SampleMapQualifierB
class QualifiedMapCustomKeySampleModelImpl2 @Inject constructor(
private val testString: String
) : QualifiedMapCustomKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in QualifiedMapCustomKeySampleModelImpl2 class.")
}
}
// This is the code to get Map Multibinding - qualifier.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
@SampleMapQualifierA
lateinit var qualifiedCustomKeySampleMapA: @JvmSuppressWildcards Map<SampleKey, Provider<QualifiedMapCustomKeySampleModel>>
@Inject
@SampleMapQualifierB
lateinit var qualifiedCustomKeySampleMapB: @JvmSuppressWildcards Map<SampleKey, Provider<QualifiedMapCustomKeySampleModel>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
for ((k, v) in qualifiedCustomKeySampleMapA) {
Log.d("Test!!", "key: $k")
v.get().printTestString()
}
for ((k, v) in qualifiedCustomKeySampleMapB) {
Log.d("Test!!", "key: $k")
v.get().printTestString()
}
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifiedMapCustomKeySampleModelImpl1_BindsModule {
@Binds
@IntoMap
@QualifiedSampleMapCustomKey(key = SampleKey.KEY1)
@SampleMapQualifierA
public abstract QualifiedMapCustomKeySampleModel bindQualifiedMapCustomKeySampleModelImpl1(QualifiedMapCustomKeySampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifiedMapCustomKeySampleModelImpl2_BindsModule {
@Binds
@IntoMap
@QualifiedSampleMapCustomKey(key = SampleKey.KEY2)
@SampleMapQualifierB
public abstract QualifiedMapCustomKeySampleModel bindQualifiedMapCustomKeySampleModelImpl2(QualifiedMapCustomKeySampleModelImpl2 target);
}
If you want to configure multiple
Map Multibinding
of the same type, you can use @Named(javax.inject.Named
) annotations like this:
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class NamedSampleMapCustomKey(val key: SampleKey)
interface NamedMapCustomKeySampleModel {
fun printTestString()
}
@HiltMapBinds
@NamedSampleMapCustomKey(SampleKey.KEY1)
@Named("sampleNamedMapA")
class NamedMapCustomKeySampleModelImpl1 @Inject constructor(
private val testString: String
) : NamedMapCustomKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in NamedMapCustomKeySampleModelImpl1 class.")
}
}
@HiltMapBinds
@NamedSampleMapCustomKey(SampleKey.KEY2)
@Named("sampleNamedMapB")
class NamedMapCustomKeySampleModelImpl2 @Inject constructor(
private val testString: String
) : NamedMapCustomKeySampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in NamedMapCustomKeySampleModelImpl2 class.")
}
}
// This is the code to get Map Multibinding - named.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
@Named("sampleNamedMapA")
lateinit var namedCustomKeySampleMapA: @JvmSuppressWildcards Map<SampleKey, Provider<NamedMapCustomKeySampleModel>>
@Inject
@Named("sampleNamedMapB")
lateinit var namedCustomKeySampleMapB: @JvmSuppressWildcards Map<SampleKey, Provider<NamedMapCustomKeySampleModel>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
for ((k, v) in namedCustomKeySampleMapA) {
Log.d("Test!!", "key: $k")
v.get().printTestString()
}
for ((k, v) in namedCustomKeySampleMapB) {
Log.d("Test!!", "key: $k")
v.get().printTestString()
}
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedMapCustomKeySampleModelImpl1_BindsModule {
@Binds
@IntoMap
@NamedSampleMapCustomKey(key = SampleKey.KEY1)
@Named("sampleNamedMapA")
public abstract NamedMapCustomKeySampleModel bindNamedMapCustomKeySampleModelImpl1(NamedMapCustomKeySampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedMapCustomKeySampleModelImpl2_BindsModule {
@Binds
@IntoMap
@NamedSampleMapCustomKey(key = SampleKey.KEY2)
@Named("sampleNamedMapB")
public abstract NamedMapCustomKeySampleModel bindNamedMapCustomKeySampleModelImpl2(NamedMapCustomKeySampleModelImpl2 target);
}
# Supported
You can set the return type to a single generic type through @HiltBinds.
interface SingleGenericSampleModel<T> {
fun printTestString(data: T)
}
@HiltBinds
class SingleGenericSampleModelImpl1 @Inject constructor(
private val testString: String
) : SingleGenericSampleModel<Int> {
override fun printTestString(data: Int) {
Log.d("Test!!", "TestString is `$testString` in GenericSampleModelImpl1 class. :: Generic type is <Int>")
}
}
@HiltBinds
class SingleGenericSampleModelImpl2 @Inject constructor(
private val testString: String
) : SingleGenericSampleModel<String> {
override fun printTestString(model: String) {
Log.d("Test!!", "TestString is `$testString` in GenericSampleModelImpl2 class. :: Generic type is <String>")
}
}
@HiltBinds
class SingleGenericSampleModelImpl3 @Inject constructor(
private val testString: String
) : SingleGenericSampleModel<Any> {
override fun printTestString(data: Any) {
Log.d("Test!!", "TestString is `$testString` in GenericSampleModelImpl3 class. :: Generic type is <Any>")
}
}
// This is the code to get Generic Type - single.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var singleGenericSampleModel1: SingleGenericSampleModel<Int>
@Inject
lateinit var singleGenericSampleModel2: SingleGenericSampleModel<String>
@Inject
lateinit var singleGenericSampleModel3: SingleGenericSampleModel<Any>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
singleGenericSampleModel1.printTestString(1205)
singleGenericSampleModel2.printTestString("String")
singleGenericSampleModel3.printTestString(1205.97)
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class SingleGenericSampleModelImpl1_BindsModule {
@Binds
public abstract SingleGenericSampleModel<Integer> bindSingleGenericSampleModelImpl1(SingleGenericSampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class SingleGenericSampleModelImpl2_BindsModule {
@Binds
public abstract SingleGenericSampleModel<String> bindSingleGenericSampleModelImpl2(SingleGenericSampleModelImpl2 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class SingleGenericSampleModelImpl3_BindsModule {
@Binds
public abstract SingleGenericSampleModel<Object> bindSingleGenericSampleModelImpl3(SingleGenericSampleModelImpl3 target);
}
You can set the return type to multiple generic types through @HiltBinds.
interface MultipleGenericSampleModel<T1, T2> {
fun printTestString(data1: T1, data2: T2)
}
@HiltBinds
class MultipleGenericSampleModelImpl @Inject constructor(
private val testString: String
) : MultipleGenericSampleModel<Int, Any> {
override fun printTestString(data1: Int, data2: Any) {
Log.d("Test!!", "TestString is `$testString` in GenericSampleModelImpl1 class. :: Generic type is <Int, Any>")
}
}
// This is the code to get Generic Type - multiple.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var multipleGenericSampleModel: MultipleGenericSampleModel<Int, Any>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
multipleGenericSampleModel.printTestString(97, 1205)
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class MultipleGenericSampleModelImpl_BindsModule {
@Binds
public abstract MultipleGenericSampleModel<Integer, Object> bindMultipleGenericSampleModelImpl(MultipleGenericSampleModelImpl target);
}
You can set the return type as a generic type through @HiltSetBinds. Of course, multiple generic types are possible.
interface SetGenericSampleModel<T> {
fun printTestString(data: T)
}
@HiltSetBinds
class SetGenericSampleModelImpl1 @Inject constructor(
private val testString: String
) : SetGenericSampleModel<Int> {
override fun printTestString(data: Int) {
Log.d("Test!!", "TestString is `$testString` in SetGenericSampleModelImpl1 class. :: Generic type is <Int>")
}
}
@HiltSetBinds
class SetGenericSampleModelImpl2 @Inject constructor(
private val testString: String
) : SetGenericSampleModel<Int> {
override fun printTestString(data: Int) {
Log.d("Test!!", "TestString is `$testString` in SetGenericSampleModelImpl2 class. :: Generic type is <Int>")
}
}
@HiltSetBinds
class SetGenericSampleModelImpl3 @Inject constructor(
private val testString: String
) : SetGenericSampleModel<String> {
override fun printTestString(data: String) {
Log.d("Test!!", "TestString is `$testString` in SetGenericSampleModelImpl3 class. :: Generic type is <String>")
}
}
@HiltSetBinds
class SetGenericSampleModelImpl4 @Inject constructor(
private val testString: String
) : SetGenericSampleModel<String> {
override fun printTestString(data: String) {
Log.d("Test!!", "TestString is `$testString` in SetGenericSampleModelImpl4 class. :: Generic type is <String>")
}
}
// This is the code to get Generic Type - Set Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var setGenericSampleModelA: @JvmSuppressWildcards Set<SetGenericSampleModel<Int>>
@Inject
lateinit var setGenericSampleModelB: @JvmSuppressWildcards Set<SetGenericSampleModel<String>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setGenericSampleModelA.forEach {
it.printTestString(1)
}
setGenericSampleModelB.forEach {
it.printTestString("String1")
}
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class SetGenericSampleModelImpl1_BindsModule {
@Binds
@IntoSet
public abstract SetGenericSampleModel<Integer> bindSetGenericSampleModelImpl1(SetGenericSampleModelImpl1 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class SetGenericSampleModelImpl2_BindsModule {
@Binds
@IntoSet
public abstract SetGenericSampleModel<Integer> bindSetGenericSampleModelImpl2(SetGenericSampleModelImpl2 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class SetGenericSampleModelImpl3_BindsModule {
@Binds
@IntoSet
public abstract SetGenericSampleModel<String> bindSetGenericSampleModelImpl3(SetGenericSampleModelImpl3 target);
}
@Module
@InstallIn(SingletonComponent.class)
abstract class SetGenericSampleModelImpl4_BindsModule {
@Binds
@IntoSet
public abstract SetGenericSampleModel<String> bindSetGenericSampleModelImpl4(SetGenericSampleModelImpl4 target);
}
It also supports nested class as below code.
interface NestedSampleModel {
interface SampleModel {
fun printTestString()
}
}
@HiltBinds
class NestedSampleModelImpl @Inject constructor(
private val testString: String
) : NestedSampleModel.SampleModel {
override fun printTestString() {
Log.d("Test!!", "TestString is `$testString` in NestedSampleModelImpl class.")
}
}
// This is the code to get Nested Type.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var nestedSampleModel: NestedSampleModel.SampleModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nestedSampleModel.printTestString()
}
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class NestedSampleModelImpl_BindsModule {
@Binds
public abstract SampleModel bindNestedSampleModelImpl(NestedSampleModelImpl target);
}
# Performance monitoring
You can monitor the elapsed time during annotation processing in the Build
> Build Output
tab of Android Studio.
# License
Copyright 2022 ParkSM
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.