2.Dagger를 이용한 주입 기법 -4

Jamezz Dev·2020년 7월 19일
0

android-Architecture

목록 보기
5/8

10. 범위 지정하기

각 컴포넌트는 @Scope 애노테이션과 함께 범위를 지정할 수 있다.예를 들어 안드로이드에서는 애플리케이션, 액티비티, 프래그먼트 인스턴스에 대한 범위 지정을 다르게 관리함으로써 오브젝트 그래프의 생성과 소멸을 각자 관리할 수 있다.

@Singleton 사용하기

@Singleton
@Component(modules = MyModule.class)
public interface MyComponent{
	Object getObject();
}

@Module
public class MyModule{
    @Provides
    @Singleton
    Object provideObject(){
    	return new Object();
    }
}

@Test
public void testObjectIdentity(){
MyComponent myComponent = DaggerMyComponent.create();
Object temp1 = myComponent.getObject();
Object temp2 = myComponent.getObject();
asertNotNull(temp1);
asertNotNull(temp2);
assertSame(temp1,temp2);

}

실행 결과
232653
232653
true

@Reusable사용하기

특정 컴포넌트에 스코프에 종속되지 않아 컴포넌트에 @Reusable을 선언하지 않아도 된다. 즉 다른 스코프 애노테이션 처럼 인스턴스의 동일성을 보장하진 않지만 항상 동일한 인스턴스를 사용해야 하는것이 아니라면 메모리 관리 측면에서 조금 더 효율적이다.

11. 바인딩의 종류

@Binds

모듈 내의 추상 메서드에 붙일 수 있으며 , 이 메서드는 반드시 하나의 매개 변수만을 가져야 한다. 매개 변수를 반환형으로 바인드 할 수 있으며 ,@Provide 메서드 대신 좀 더 효율적으로 사용할 수 있다.

@Binds
abstract Random bindRandom(SecureRandom secureRandom);

@BindsOptionalOf

모듈 내의 추상 메서드에 붙일 수 있으며 , 이 메서드의 매개변수는 항상 가질 수 없다. void 가 아닌 특정타입을 반환형으로 가져야 하며, 예외사항을 던질수도 없다.

@Module
public abstract class CommonModule{
    @BindsOptionalOf
    abstract String bindsOptionalOfString();
}

@Module
public class HelloModule{
    @Provides
    String provideString(){
    	return "Hello";
    }
}

@BindsOptionalOf 메서드를 통한 의존성 주입은 다음과 같인 Optional 타입 등으로 주입된다.

public class Foo{
    @Inject
    public Optional<String> str;

    @Inject
    public Optional<Provider<String>> str2;

    @Inject
    public Optional<Lazy<String>> str2;

}
  • 만약 컴포넌트 내에 Foo가 바인드 된적이 있다면 Optional 의 상태는 Present 아니면 absent이다.

  • 어떤 타입의 의존성이 바인드외었는지 여부와 관계없이 @Inject를 이용해 주입할 수 있는것이 특징

  • Optional은 Null을 포함하는것을 허용하지 않는다. 그러기에 @Nullable바인딩에 대해서는 컴파일 타임에 에러를 발생함

  • 바인드 유무에 따른 Optional 상태 테스트를 위해 두개의 컴포넌트를 생성한다

@Component(modules ={CommonModule.class ,HelloModule.class})
public interface StrComponent{
	void inject(Foo foo);
}

@Component(modules = {CommonModule.class})
public interface NoStrComponent{
	void inject(Foo foo);
}

@Test
public void testFoo(){
    Foo foo = new Foo();

    DaggerStrComponent.create().inject(foo);
    System.out.println(foo.str.isPresent());
    System.out.println(foo.str.get());

    DaggerNoStrComponent.create().inject(foo);
    System.out.println(foo.str.isPresent);
    System.out.println9foo.str.get());

}

실행 결과
true
Hello
false
java.util.noSuchElemntException:No Value Present

  • Optional 타입인 foo.str이 String 이 바인드 되었을때 present 이고 String 이 바인드 되지 않았을땐 absent인것을 확인할수 있다.
  • absent일땐 get()을 하면 에러가 발생한다.

@BindsInstance

  • @BindsInstance 애노테이션은 컴포넌트 빌더의 세터 메서드 또는 컴포넌트 팩토리의 매개변수에 붙일 수 있다.
  • 모듈이 아닌 외부로부터 생성된 인스턴스를 빌더 또는 팩토리를 통해 넘겨줌으로써 컴포넌트가 해당 인스턴스를 바인드 하게 된다 이러한 인스턴스들은 모듈로부터 제공되는 인스턴스와 동일하게 @Inject가 붙은 필드, 생성자, 메서드에 주입될 수 있다.
@Component
public interface BindsComponent{
    void inject(Foo foo);
    @Component.Builder
    interface Builder{
        @BindsInstance
        Builder setString(String str);
        BindsComponent build();
    }
}

public class Foo{
    @Inject
    public String str;
}
  • Builder를 만들고 @BindsInstance가 붙은 setString 세터 메서드를 추가했다. 이 세터 메서드를 통해 외부로부터 생성한 String 객체를 바인드 할 것이다.
@Test
public void testBindsInstance(){
    String hello = "Hello World";
    Foo foo = new Foo();
    BindsComponent component = DaggerBindsComponent.builder()
    .setString(hello)
    .build();
    component.inject(foo);
    assertEquals("Hello World",foo.str);
}

실행 결과
Hello World

12. 멀티 바인딩하기

Dagger의 멀티바인딩을 이용해 여러 모듈에 있는 같은 타입의 객체를 하나의 Set 또는 Map에 관리할 수 있다.

Set 멀티 바인딩

Set로 멀티바인딩을 구현하려면 @IntoSet , @ElementsIntoSet 애노테이션을 @Provides 메서드에 함께 사용할 수 있다.

@Module
public class SetModule{
    @Provides
    @IntoSet
    String provideHello(){
    	return "Hello;
    }

    @Provides 
    @IntoSet
    String provideWorld(){
    	return "World";
    }
}
  • 간단히 @IntoSet하는 것으로도 Set<String> 타입으로 멀티바인딩이 구현된다.

  • 객체를 하나씩 Set에 추가하는것이 아닌 Set<String>의 일부분을 한거번에 추가하는 방법은 @ElementsIntoSet 애노테이션을 사용하는것이다.

 @Module
 public class SetModule{
     ...
     @Provides
     @ElementsIntoSet
     Set<String> provideSet(){
     	return new HashSet<>(Arrays.asList("Charles","Runa"));
     }
 }
 
 @Component(modules =SetModule.class)
 public interface SetComponent{
	 void inject(Foo foo);
 }
 
 
 public class Foo{
     @Inject
     Set<String> strings;

     public void print(){
         for(Iterfator itr = strings.iterator();itr.hasNext();){
         	System.out.println(itr.next());
         }
     }
 }
 
 pulic class MultibindingTest{
     @Test
     public void testMultibindingSet(){
         Foo foo = new Foo();
         DaggerSetComponent.create.inject(foo);
         foo.print();
     }
 }

Map 멀티바인딩

  • @Provides 메서드에 @IntoMap을 추가해야 한다.
  • Map을 사용하는데는 Key가 필요해 @IntoMap 애노테이션과 함께 키 애노테이션을 추가해야 한다.

    기본 제공 키의 종류
    -@StringKey
    -@ClassKey
    -@IntKey
    -@LongKey

public clas Foo{
}

@Module
public class MapModule{

    @Provides 
    @IntoMap
    @StringKey("foo")
    static Long provideFooValue(){
    	return 100L;
    }

    @Provides
    @IntoMap
    @ClassKey(Foo.class)
    static String provideFooStr(){
    	return "Foo String";
    }
}

@Component(modules=MapModule.class)
public interface MapComponent{
    Map<String,Long> getLongByString();
    Map<Class<?>,String> getStringByClass();
}	


public class MultibindingMapTest{
    @Test
    public void testMultibindingMap(){
        MapComponent component = DaggerMapComponent.create();
        long value = component.getLongByString().get("foo");
        String str = component.getStringByClass().get(Foo.class);

        System.out.println(value);
        System.out.println(str);
    }
}

실행 결과
100
Foo String

사용자 정의 키 만들기

  • 기본적으로 제공하는 키가 아닌 사용자가 직접 키를 정의할 수 있다. @MapKey를 통해 사용자 정의키 선언한다.
public enum Animal{
	CAT,DOG;
}

@MapKey
public @interface AnimalKey{
	Animal value();
}

@MapKey
public @interface Numberkey{
	Class<? extends Number> value();
}
  • AnimalKey는 열거형인 Animal타입 속성을 갖으며 NumberKey는 Number 클래스의 서브 클래스 타입을 속성으로 갖는다.
@Component(modules = MapModule.class)
public interface MapKeyComponent{
    Map<Animal,String> getStringByAnimal();
    Map<Class<? extends Number> , Stirng> getStringByNumber();
}


@Module
public class MapModule{
    @IntoMap
    @AnimalKey(Animal.CAT)
    @Provides
    String provideCat(){
    	return "Meow";
    }

    @IntoMap
    @AnimalKey(Animal.DOG)
    @Provides
    String provideDog(){
    	return "Bow-wow";
    }

    @IntoMap
    @NumberKey(Float.class)
    @Provides
    String provideFloatValue(){
    	return "100f";
    }

    @IntoMap
    @NumberKey(Integer.class)
    @Provides
    String provideIntegerValue(){
    	return "1";
    }	
}


@Test
public void testCustomMapKey(){
    MapKeyComponent component = DaggerMapKeyComponent.create();
    String cat = component.getStringsByAnimal().get(Animal.CAT);
    String dog = component.getStringsByAnimal().get(Animal.DOG);
    String number = component.getStringByNumber().get(Float.class);
    String Integer = component.getStringByNumber().get(Integer.class);

    System.out.println(cat);
    System.out.println(dog);
    System.out.println(number);
    System.out.println(Integer);

}

실행 결과
meow
bow-wow
100.0f
1

profile
💻디지털 노마드를 🚀꿈꾸는 🇯🇲자메즈 🐥개발자 입니다.

0개의 댓글