Dagger2 Custom Scopes: Как работают пользовательские области (@ActivityScope)?

Я читаю исходный код Dagger2 Component Scopes Test на GitHub, и я видел "настраиваемую область", определенную для действий с именем @ActivityScope, но Я видел это в других проектах, включая 4-модуль CleanArchitecture, который имеет область @PerActivity.

Но буквально код для аннотации @ActivityScope следующий:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.inject.Scope;

/**
 * Created by joesteele on 2/15/15.
 */
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

И это "волшебное" использование в модулях:

@Module
public class ActivityModule {
  @Provides @ActivityScope Picasso providePicasso(ComponentTest app, OkHttpClient client) {
    return new Picasso.Builder(app)
        .downloader(new OkHttpDownloader(client))
        .listener(new Picasso.Listener() {
          @Override public void onImageLoadFailed(Picasso picasso, Uri uri, Exception e) {
            Log.e("Picasso", "Failed to load image: " + uri.toString(), e);
          }
        })
        .build();
  }
}

Или пример CleanArchitecture:

@Scope
@Retention(RUNTIME)
public @interface PerActivity {}

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
  //Exposed to sub-graphs.
  Activity activity();
}

@Module
public class ActivityModule {
  private final Activity activity;

  public ActivityModule(Activity activity) {
    this.activity = activity;
  }

  /**
  * Expose the activity to dependents in the graph.
  */
  @Provides @PerActivity Activity activity() {
    return this.activity;
  }
}

Я ясно вижу, что это связано с пользовательскими областями JSR-330, но я действительно не понимаю, что именно происходит здесь, чтобы сделать это так, чтобы этот код позволял данному модулю и/или то, что предоставляется данным модулем, чтобы зависеть от фактического жизненного цикла Activity, и для того, чтобы существовать только один экземпляр, но только если эта активная активность активна.

Документы говорят следующее:

Scope

Dagger 1 only supported a single scope: @Singleton. 
Dagger 2 allows users to any well-formed scope annotation. 
The Component docs describe the details of 
    how to properly apply scope to a component.

Он говорит, чтобы посмотреть страницу Компонентные документы, но это дает мне 404. Я также видел this, но...

Могу ли я попросить некоторую помощь в разъяснении, почему магическое создание этой настраиваемой области делает работу Activity-level scopes без проблем?

(Ответ заключается в том, что подскоп может получать зависимости от своего суперскопа, а подскоп существует до тех пор, пока компонент выполняет. И вам нужно указать области действия в своих модулях, и вам нужно указать зависимости компонентов для подкапа один суперскоп.)

Ответ 1

Стоит отметить, что, по-видимому, Dagger2 создает один экземпляр для каждого поставщика в модуле для каждого компонента.

Итак, чтобы получить провайдера с ограниченным охватом в модуле, вам нужно указать область действия для вашего провайдера модулей.

@Module
public class YourModule {
    @Provides
    @YourScope //one per component
    public Something something() { return new SomethingImpl(); }

    @Provides //new instance per injection
    public Otherthing otherthing() { return new OtherthingImpl(); }
}

@Component
@YourScope
public interface YourComponent {
    Something something();
    Otherthing otherthing();

    void inject(YourThing yourThing);
}

Затем обратитесь к Кириллскому ответу; по сути, "область" сама по себе определяет, что она отличается от другой. Использование зависимостей компонентов (или подкомпонентов) создает подкласс.

@Module
public class SubModule {
    @Provides
    @SubScope
    public ThatThing thatThing() { return new ThatThingImpl(); }
}

@Component(dependencies={YourComponent.class}, modules={SubModule.class})
@SubScope
public interface SubComponent extends YourComponent {
    ThatThing thatThing();

    void inject(SubThing subThing);
}

Компонент может зависеть только от одного другого компонента с областью.

Ответ 2

На самом деле нет волшебства. Аннотации пользовательской области - это всего лишь аннотации. Они могут иметь любое имя. Аннотации Scope служат инструментом для статического анализа зависимостей, которые предоставляют модули и компоненты. Поэтому использование @ActivityScope зависимостей в компоненте @ActivityScope приведет к ошибке компиляции.

Как определить реальный объем - ваша прерогатива. Определите жизненный цикл вашего компонента области видимости, время его создания и время его уничтожения - это ваш объем. Например. @ActivityScope привязан к жизненному циклу активности и определяется следующим образом:

private ActivityComponent component;

@Override
protected void onCreate(Bundle savedInstanceState) {
    component = DaggerActivityComponent.builder().build();
    component.inject(this);
}

@Override
protected void onDestroy() {
    component = null;
    super.onDestroy();
}

Итак, нет никакой магии. Определите свои области применения семантикой их использования. Вы также можете найти этот ответ и эти примеры.