Dagger2 시작하기 (Java) part4- 스코프, 컴포넌트 간의 관계 설정

TPark·2020년 6월 24일
0

안드로이드

목록 보기
9/10

Dagger2에서 스코핑은 객체의 인스탄스를 보존하고 특정 구간의 싱글턴 패턴을 적용할때 사용된다. 예를 들어, A 컴포넌트가 Activity의 라이프타임 동안 사용할 수 있는 @PerActivity 스코프를 가지고 있고 해당 컴포넌트에서 같은 스코프를 가진 B 객체를 제공한다면 해당 액티비티가 살아 있는 동안 B 객체는 항상 같은 인스턴스를 가지게 된다. @Singleton 또한 스코프의 한 종류이고 Dagger2에서 가장 자주 사용되는 스코프이다.

이번 예제에서는 Car 객체와 DieselEngine 객체를 액티비티 라이프사이클 동안 싱글턴 패턴을 유지하게 하고 Driver 객체를 애플리케이션이 살아있는 동안 싱글턴 패턴을 유지하게 만들어 보려고 한다.

우선 애플리케이션이 살아있는 동안 의존성을 제공하는 AppComponent를 만들고 Driver를 제공하게끔 Privision Method를 선언해준다. 그리고 Driver에 싱글턴 패턴을 적용할 것이기 때문에, @Singleton 어노테이션을 붙혀준다.

@Singleton
@Component
public interface AppComponent {
    Driver getDriver();
}

Driver 객체에도 마찬가지로 @Singleton 을 적용한다.

@Singleton
public class Driver {
    @Inject
    public Driver(){}
}

@PerActivity라는 커스텀 어노테이션 클래스를 만든 뒤, Activity가 살아있는 동안 의존성을 제공할 ActivityComponent를 만들고 @PerActivity를 적용한다. 여기서 ActivityCoponent에서 제공하는 Car객체에 AppComponent에서 제공하는 Driver객체도 필요하기 때문에 컴포넌트 간의 관계를 설정해줘야 한다. 아래 예제에서는 dependencies를 설정하는 방법을 사용했는데, 이 방법 외에도 SubComponent를 사용하는 방법이 있다.

@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}

@PerActivity
@Component(modules = {WheelsModule.class, DieselEngineModule.class}, dependencies = AppComponent.class)
// ActivityComponent는 AppComponent를 의존하기 때문에 dependencies에 AppComponent.class를 추가해준다.
public interface ActivityComponent {
    void inject(MainActivity activity);
    void inject(SecondActivity activity);

    @Component.Builder
    interface Builder {
        ActivityComponent build();

        @BindsInstance
        Builder horsePower(@Named("horse power") int horsePower);

        @BindsInstance
        Builder engineCap(@Named("engine capacity") int engineCap);

        @BindsInstance
        Builder airPressure(@Named("air pressure") int airPressure);

		// ActivityComponent는 AppComponent를 의존하기 때문에 빌더에 AppComponent를 포함시켜야 한다.
        Builder appComponent(AppComponent appComponent);
    }
}

마찬가지로 같은 스코프를 적용시킬 Car 객체와 DieselEngine 객체에도 @PerActivity 어노테이션을 붙혀준다.

@PerActivity
public class Car {
    private static final String TAG = "Car";

    private Driver driver;
    private Engine engine;
    private Wheels wheels;

    @Inject
    public Car(Engine engine, Wheels wheels, Driver driver) {
        this.engine = engine;
        this.wheels = wheels;
        this.driver = driver;
    }

    @Inject
    public void connectRemote(Remote remote) {
        remote.setRemote();
    }

    public void drive() {
        Log.d(TAG, "driving..." + this + ", driver is : " + driver + ", engine is: " + engine);
    }
}

@Module
public class DieselEngineModule {
    @PerActivity
    @Provides
    static Engine provideEngine(DieselEngine engine) {
        return engine;
    }
}

MainActivity와 SecondActivity

public class MainActivity extends AppCompatActivity {
    @Inject
    public Car car1;
    @Inject
    public Car car2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActivityComponent component = DaggerActivityComponent.builder()
                .appComponent(((MyApplication) getApplication()).getComponent())
                .airPressure(100)
                .engineCap(200)
                .horsePower(300)
                .build();
        component.inject(this);
        car1.drive();
        car2.drive();

        Button myBtn = findViewById(R.id.myBtn);
        myBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

public class SecondActivity extends AppCompatActivity {
    @Inject
    public Car car1;
    @Inject
    public Car car2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        ActivityComponent component = DaggerActivityComponent.builder()
                .appComponent(((MyApplication) getApplication()).getComponent())
                .airPressure(100)
                .engineCap(200)
                .horsePower(300)
                .build();
        component.inject(this);
        car1.drive();
        car2.drive();
    }
}

결과

MainActivity
D/Car: driving...com.example.dagger2tutorial.car.Car@1ce8dc1, driver is : com.example.dagger2tutorial.car.Driver@8695966, engine is: com.example.dagger2tutorial.car.DieselEngine@8fc54a7
driving...com.example.dagger2tutorial.car.Car@1ce8dc1, driver is : com.example.dagger2tutorial.car.Driver@8695966, engine is: com.example.dagger2tutorial.car.DieselEngine@8fc54a7

SecondActivity
D/Car: driving...com.example.dagger2tutorial.car.Car@f10204d, driver is : com.example.dagger2tutorial.car.Driver@8695966, engine is: com.example.dagger2tutorial.car.DieselEngine@c436702
D/Car: driving...com.example.dagger2tutorial.car.Car@f10204d, driver is : com.example.dagger2tutorial.car.Driver@8695966, engine is: com.example.dagger2tutorial.car.DieselEngine@c436702

Car객체와 DieselEngine 객체는 같은 액티비티 내에서만 싱글턴 패턴을 유지하는 반면, Driver 객체는 액티비티에 상관없이 모든 액티비티 내에서 싱글턴 패턴을 유지하는걸 확인할 수 있다.

Subcomponent

위에서 컴포넌트 간의 객체 설정을 위해 dependencies를 설정하는 방법을 사용했는데, ActivityComponent를 Subcomponent로 선언해서 같은 결과를 만들어낼 수 있다.

ActivityComponent를 AppComponent의 Subcomponent로 설정하면 AppComponent에서 ProvisionMethod를 제공할 필요없이 Driver 객체를 사용할 수 있다

예제

@Singleton
@Component
public interface AppComponent {
    ActivityComponent.Builder getActivityComponentBuilder();
}

@PerActivity
@Subcomponent(modules = {WheelsModule.class, DieselEngineModule.class})
public interface ActivityComponent {
    void inject(MainActivity activity);
    void inject(SecondActivity activity);

    @Subcomponent.Builder
    interface Builder {
        ActivityComponent build();

        @BindsInstance
        Builder horsePower(@Named("horse power") int horsePower);

        @BindsInstance
        Builder engineCap(@Named("engine capacity") int engineCap);

        @BindsInstance
        Builder airPressure(@Named("air pressure") int airPressure);

    }
}

ActivityComponent 선언

ActivityComponent component = ((MyApplication) getApplication())
                .getComponent()
                .getActivityComponentBuilder()
                .horsePower(100)
                .engineCap(200)
                .airPressure(300)
                .build();
        component.inject(this);
        car1.drive();
        car2.drive();

참조

https://www.youtube.com/playlist?list=PLrnPJCHvNZuA2ioi4soDZKz8euUQnJW65

0개의 댓글