@Singleton 어노테이션은 Dagger2가 해당 객체를 제공할때 단 하나의 인스턴스만 만들수 있도록 한다.
사용하는 방법은 간단하다. 싱글턴 패턴을 적용하고자 하는 객체 클래스에 @Singleton 어노테이션을 붙히거나 @Provides 혹은 @Binds 메소드에 @Singleton 어노테이션을 붙힌 뒤 해당 컴포넌트 클래스에 @Singleton 어노테이션을 붙혀주면 된다.
@Singleton
public class Driver {
@Inject
public Driver(){}
}
@Module
public class DieselEngineModule {
@Singleton
@Provides
static Engine provideEngine(DieselEngine engine) {
return engine;
}
}
@Singleton
@Component(modules = {WheelsModule.class, DieselEngineModule.class})
public interface CarComponent {
void inject(MainActivity activity);
@Component.Builder
interface Builder {
CarComponent 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);
}
}
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;
}
public void drive() {
Log.d(TAG, "driving..." + this + ", driver is : " + driver);
}
}
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_main3);
CarComponent component = DaggerCarComponent.builder()
.horsePower(20)
.engineCap(100)
.airPressure(500)
.build();
component.inject(this);
car1.drive();
car2.drive();
}
}
driving...com.example.dagger2tutorial.car.Car@8f96006, driver is : com.example.dagger2tutorial.car.Driver@813e6c7, engine is: com.example.dagger2tutorial.car.DieselEngine@5b414f4
driving...com.example.dagger2tutorial.car.Car@4c7741d, driver is : com.example.dagger2tutorial.car.Driver@813e6c7, engine is: com.example.dagger2tutorial.car.DieselEngine@5b414f4
car1, car2의 인스턴스가 다른 반면에 같은 DieselEngine과 Driver 인스턴스를 공유하는 걸 볼수있다.
지금까지 시리즈의 예제에서는 액티비티에서 컴포넌트를 선언해서 사용했다. 이렇게 사용했을때 생길 수 있는 문제 중 하나가 화면이 회전되거나 윈도우 사이즈가 변경되는 등의 이유로 액티비티가 디스트로이 된후 새로 생성되었을때 싱글턴 패턴을 유지하지 못 한다는 문제가 있다.
예를 들어, 위 예제에서 앱의 화면을 회전시켜보자.
D/Car: driving...com.example.dagger2tutorial.car.Car@92013e4, driver is : com.example.dagger2tutorial.car.Driver@f10204d, engine is: com.example.dagger2tutorial.car.DieselEngine@c436702
회전 시키기 전의 Driver 인스턴스 (Driver@813e6c7)와 다른 인스턴스임을 볼수 있다.
이 문제를 해결하려면 컴포넌트가 애플리케이션 사용 내내 살아있을 수 있도록 해야한다. 이를 위해 Application을 상속하는 Custom Application 클래스를 만든 뒤, 그 안에서 컴포넌트를 선언해서 사용해야 한다.
public class MyApplication extends Application {
private CarComponent component;
@Override
public void onCreate() {
super.onCreate();
component = DaggerCarComponent.builder()
.airPressure(100)
.engineCap(200)
.horsePower(300)
.build();
}
public CarComponent getComponent() {
return component;
}
}
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_main3);
CarComponent component = ((MyApplication) getApplication()).getComponent();
component.inject(this);
car1.drive();
car2.drive();
}
}
추가로 MyApplication을 사용하려면 Manifest 파일의 Application scope에 android:name=".MyApplication"를 추가해줘야 한다.
회전하기 전
D/Car: driving...com.example.dagger2tutorial.car.Car@8f96006, driver is : com.example.dagger2tutorial.car.Driver@813e6c7, engine is: com.example.dagger2tutorial.car.DieselEngine@5b414f4
회전 후
D/Car: driving...com.example.dagger2tutorial.car.Car@136c2ec, driver is : com.example.dagger2tutorial.car.Driver@813e6c7, engine is: com.example.dagger2tutorial.car.DieselEngine@5b414f4
싱글턴 패턴을 적용한 Driver와 DieselEngine의 인스턴스가 회전 후에도 같은 인스턴스 임을 확인할 수 있다.
https://www.youtube.com/playlist?list=PLrnPJCHvNZuA2ioi4soDZKz8euUQnJW65