본문

170609(금) - DI with Dagger2

DI with Dagger2


http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/



Scope

- Singleton은 API client, database helpers, analytics managers 등에 쓰인다.

- 그러나 DI를 사용한다면 이런식으로 객체를 직접 가져오면 안됨.

- @Inject가 알아서 객체를 잘 가져와 준다.


- Dagger2는 가능한 scope까지 instance를 유지시켜 준다.

ex)

- @ApplicationScope

Application object만큼 오래 살아 남는다.


- @ActivityScope

Activity가 존재하는 한 reference 유지


- CustomScope

그러나 기본적으로 Dagger2에 위와같은 annotation은 없음.

@Singleton 이 기본적으로 제공



Scope ex

- @Singleton

application scope

실제로는 Application 만큼 오래 live한다.


- @UserScope

logging 된 user 와 연관된 class instance scope


- @ActivityScope

Activity 동안 live 하는 instance scope (ex : presenter)


Dagger scopes



Scopes lifecycle





Implementation

- Implement proper configuration of component.

- 2 ways

1. @Subcomponent 사용

- object graph 전체에 access 가능


2. Component dependencies

- component interface에 노출된 object에만 access 가능



- AppComponent의 Subcomponent인 Usercomponent와 SplashActivityComponent는 AppModule, GithubApiModule에서 생성된 instance에 access 가능

@Singleton
@Component(
        modules = {
                AppModule.class,
                GithubApiModule.class
        }
)
public interface AppComponent {

    UserComponent plus(UserModule userModule);

    SplashActivityComponent plus(SplashActivityModule splashActivityModule);

}



- UserComponent는 plus()의 parameter로 전달되는 다른 module을 필요로 한다.

- AppComponent의 graph는 extending 된다.


- AppComponent에서 extend 된 UserComponent에서 가져온 instance는 Singleton 범위이지만, 

- UserComponent의 일부인 UserModule에서 생성되는 component는 UserComponent instance 만큼 지속되는 local singleton.

- UserComponent appComponent = appComponent.plus(new UserModule(user)) (UserComponent instance 생성) 마다 UserModule에서 가져오는 object는 다른 instance 이다.

@UserScope
@Subcomponent(
        modules = {
                UserModule.class
        }
)
public interface UserComponent {
    RepositoriesListActivityComponent plus(RepositoriesListActivityModule repositoriesListActivityModule);

    RepositoryDetailsActivityComponent plus(RepositoryDetailsActivityModule repositoryDetailsActivityModule);
}


- Annotation create

@Scope @Retention(RetentionPolicy.RUNTIME)        // ??? public @interface UserScope { }


- UserComponent의 lifecycle을 담당해야만 한다.

- initialization과 release를 신경써야 한다.

public class GithubClientApplication extends Application {

    private AppComponent appComponent;
    private UserComponent userComponent;

    //...

    public UserComponent createUserComponent(User user) {
        userComponent = appComponent.plus(new UserModule(user));
        return userComponent;
    }

    public void releaseUserComponent() {
        userComponent = null;
    }

    //...
}



Under the hood


@Generated("dagger.internal.codegen.ComponentProcessor")
public final class UserModule_ProvideRepositoriesManagerFactory implements Factory<RepositoriesManager> {

  //...
  
  @Override
  public RepositoriesManager get() {  
    RepositoriesManager provided = module.provideRepositoriesManager(userProvider.get(), githubApiServiceProvider.get());
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<RepositoriesManager> create(UserModule module, Provider<User> userProvider, Provider<GithubApiService> githubApiServiceProvider) {  
    return new UserModule_ProvideRepositoriesManagerFactory(module, userProvider, githubApiServiceProvider);
  }
}


private final class UserComponentImpl implements UserComponent {

    //...

    private UserComponentImpl(UserModule userModule) {
      if (userModule == null) {
        throw new NullPointerException();
      }
      this.userModule = userModule;
      initialize();
    }

    private void initialize() {
      this.provideUserProvider = ScopedProvider.create(UserModule_ProvideUserFactory.create(userModule));
      this.provideRepositoriesManagerProvider = ScopedProvider.create(UserModule_ProvideRepositoriesManagerFactory.create(userModule, provideUserProvider, DaggerAppComponent.this.provideGithubApiServiceProvider));
    }

    //...
    
}


- factory에서 instance를 가져와서 singleton 처럼 저장

- 따라서 single instance를 반환 가능

public final class ScopedProvider<T> implements Provider<T> {
  
  //...

  private ScopedProvider(Factory<T> factory) {
    assert factory != null;
    this.factory = factory;
  }

  @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
  @Override
  public T get() {
    // double-check idiom from EJ2: Item 71
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = factory.get();
        }
      }
    }
    return (T) result;
  }

  //...

}


'Mobile > DI' 카테고리의 다른 글

170712(수) - Dependency Injection with Dagger2  (0) 2017.07.12
170707(금) - android-architecture-todo-mvp-dagger  (0) 2017.07.07
170609(금) - DI with Dagger2  (0) 2017.06.09
170609 (금) - DI with Dagger2  (0) 2017.06.09
170608(목) - DI with Dagger2  (0) 2017.06.08

공유

댓글