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 객체는 액티비티에 상관없이 모든 액티비티 내에서 싱글턴 패턴을 유지하는걸 확인할 수 있다.
위에서 컴포넌트 간의 객체 설정을 위해 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