0

我正在尝试按照此模板进行 MVP/Dagger2/RxJava 项目。

我无法将 Activity 上下文注入到我的演示者中,因为我知道子组件可以开放访问所有父提供的逻辑,所以其他所有注入都会通过。

应用程序组件构建在 Application 类中,然后在基础 Presenter 活动中访问,然后注入 Presenter 和其余部分的相关依赖项。配置持久化组件主要用于保存演示者状态。

如果我只是手动将上下文从活动传递给演示者,则违背了 DI 的目的。

我试图为所有组件和模块添加范围,以确保可以从图中正确访问依赖项,但这没有奏效。

我正在尝试使用上下文的构造函数注入,我实际上在演示者与之通信的活动中接收到上下文,但演示者没有,会引发错误。所以我想知道为什么活动可以访问活动上下文但演示者没有。

任何指导将不胜感激。

错误

Error:(13, 8) error: [<packageName>.injection.component.ActivityComponent.inject(<packageName>.login.LoginActivity)] android.app.Activity cannot be provided without an @Inject constructor or from an @Provides-annotated method.
android.app.Activity is injected at
<packageName>.login.presenter.LoginActivityPresenter.<init>(activity, …)
<packageName>.login.presenter.LoginActivityPresenter is injected at
<packageName>.login.LoginActivity.presenter
<packageName>.login.LoginActivity is injected at
<packageName>.injection.component.ActivityComponent.inject(loginActivity)
A binding with matching key exists in component: <packageName>.injection.component.ActivityComponent

我的组件/模块如下所示:

应用程序组件

@Singleton
@Component(modules = {ApplicationModule.class, BusModule.class, PrefsModule.class, NetModule.class})
public interface ApplicationComponent {
    @ApplicationContext Context context();
    Application application();
    EventBus bus();
    SharedPreferences prefs();
    Gson gson();
}

应用模块

@Module
public class ApplicationModule {
    protected final Application app;

    public ApplicationModule(Application app) {
        this.app = app;
    }

    @Provides
    Application providesApplication() {
        return app;
    }

    @Provides
    @ApplicationContext
    Context providesContext(){
        return app;
    }
}

配置持久化组件

@ConfigPersistent
@Component(dependencies = ApplicationComponent.class)
public interface ConfigPersistentComponent {

  ActivityComponent plus(ActivityModule activityModule);

}

活动组件

@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {

  void inject(LoginActivity loginActivity);

}

活动模块

@Module
public class ActivityModule {

  private Activity activity;

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

  @Provides
  @PerActivity
  Activity providesActivity() {
    return activity;
  }

}

应用类

public class App extends Application {

  private ApplicationComponent applicationComponent;

  @Override public void onCreate() {
    super.onCreate();
    Timber.plant(new Timber.DebugTree());
  }

  public static App get(Context context) {
    return (App) context.getApplicationContext();
  }

  public ApplicationComponent getComponent() {
    if (applicationComponent == null) {
      applicationComponent = DaggerApplicationComponent.builder()
              .applicationModule(new ApplicationModule(this))
              .busModule(new BusModule())
              .netModule(new NetModule())
              .prefsModule(new PrefsModule())
              .build();
    }
    return applicationComponent;
  }

}

主讲者

@ConfigPersistent
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {

  Context context;
  Gson gson;

  @Inject
  public LoginActivityPresenter(Activity activity, Gson gson) {
    this.context = activity;
    this.gson = gson;
  }

  @Override public void attachView(LoginContract.View view) {
    super.attachView(view);
    Timber.d("onAttach");
  }

  @Override public void detachView() {
    super.detachView();
    Timber.d("onDettach");
    disposableSubscriber.dispose();
  }
}

基本演示者活动

public abstract class BasePresenterActivity extends AppCompatActivity {

    private static final String KEY_ACTIVITY_ID = "KEY_ACTIVITY_ID";
    private static final AtomicLong NEXT_ID = new AtomicLong(0);
    private static final Map<Long, ConfigPersistentComponent> sComponentsMap = new HashMap<>();

    private ActivityComponent activityComponent;
    private long activityId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create the ActivityComponent and reuses cached ConfigPersistentComponent if this is
        // being called after a configuration change.
        activityId = savedInstanceState != null ? savedInstanceState.getLong(KEY_ACTIVITY_ID) : NEXT_ID.getAndIncrement();
        ConfigPersistentComponent configPersistentComponent;
        if (!sComponentsMap.containsKey(activityId)) {
            Timber.i("Creating new ConfigPersistentComponent id=%d", activityId);
            configPersistentComponent = DaggerConfigPersistentComponent.builder()
                    .applicationComponent(App.get(this).getComponent())
                    .build();
            sComponentsMap.put(activityId, configPersistentComponent);
        } else {
            Timber.i("Reusing ConfigPersistentComponent id=%d", activityId);
            configPersistentComponent = sComponentsMap.get(activityId);
        }
        activityComponent = configPersistentComponent.plus(new ActivityModule(this));
    }

主要活动

public class LoginActivity extends BasePresenterActivity implements LoginContract.View {

  @Inject
  EventBus bus;
  @Inject
  SharedPreferences prefs;
  @Inject
  Activity activity;
  @Inject
  LoginActivityPresenter presenter;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    activityComponent().inject(this);
    setContentView(R.layout.activity_login);
    presenter.attachView(this);
  }

  @Override protected void onDestroy() {
    super.onDestroy();
    presenter.detachView();
  }
}

/********** 编辑 **********/

为了帮助为演示者保存状态并仍然允许传入活动上下文,我的解决方案发布在下面。欢迎任何反馈。

     @ActivityContext
    public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {

      Context context;

      @Inject
      LoginStateHolder loginStateHolder;

      @Inject
      public LoginActivityPresenter(Context context, Gson gson) {
        this.context = activity;
        this.gson = gson;
      }
}

@ConfigPersistent
public class LoginStateHolder {

  String title;

  @Inject
  public LoginStateHolder(Context context) {
    title = "Save me";
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getTitle() {
    return title;
  }
}

/*********编辑 - 21_5_17 *********/

例外:

Error:(13, 8) error: [<packagename>.injection.component.ActivityComponent.inject(<packagename>.login.ui.activity.LoginActivity)] android.app.Activity cannot be provided without an @Provides-annotated method.
android.app.Activity is injected at
<packagename>.login.presenter.LoginActivityPresenter.<init>(activity, …)
<packagename>.login.presenter.LoginActivityPresenter is injected at
<packagename>.login.ui.activity.LoginActivity.presenter
<packagename>.login.ui.activity.LoginActivity is injected at
<packagename>.injection.component.ActivityComponent.inject(loginActivity)

登录活动

public class LoginActivity extends BasePresenterActivity implements LoginContract.View {

  @Inject
  EventBus bus;
  @Inject
  SharedPreferences prefs;
  @Inject
  LoginActivityPresenter presenter;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    activityComponent().inject(this);
    setContentView(R.layout.activity_login);
    ButterKnife.bind(this);
    presenter.attachView(this);
    presenter.setupValidation(username, companyId, password);

}

登录活动演示者

@ActivityContext
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {

  @Inject LoginPresenterStorage loginPresenterStorage;

  Gson gson;

  @Inject
  public LoginActivityPresenter(Activity activity, Gson gson) {
    this.context = activity;
    this.gson = gson;
  }
}

登录演示者存储

@ConfigPersistent
public class LoginPresenterStorage {

  private String test = "";

    @Inject
  public LoginPresenterStorage(Activity activity) {
    test = "I didn't die";
  }

  public String getTest() {
    return test;
  }
}

应用组件

@Singleton
@Component(modules = {ApplicationModule.class, BusModule.class, PrefsModule.class, NetModule.class})
public interface ApplicationComponent {

    @ApplicationContext Context context();
    Application application();
    EventBus bus();
    SharedPreferences prefs();
    Gson gson();

}

活动组件

@ActivityContext
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {

  void inject(LoginActivity loginActivity);

}

配置持久组件

@ConfigPersistent
@Component(dependencies = ApplicationComponent.class)
public interface ConfigPersistentComponent {

  ActivityComponent plus(ActivityModule activityModule);

}

活动模块

@Module
public class ActivityModule {

  private Activity activity;

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

  @Provides
  @ActivityContext
  Activity providesActivity() {
    return activity;
  }

}

编辑

我错误地使用了与活动模块相同的活动上下文。因此,我无法将活动注入演示者。将活动模块更改为原始@peractivity 范围并遵循以下答案将使活动上下文可注入。

4

1 回答 1

2
@ConfigPersistent
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {

通过将 LoginActivityPresenter 标记为 @ConfigPersistent 范围,您是在告诉 Dagger“在 ConfigPersistentComponent 中管理此类的实例”(即始终从给定的 ConfigPersistentComponent 实例返回相同的实例),这意味着它不应该访问更窄范围内的任何内容,例如@ActivityScope。毕竟,ConfigPersistentComponent 会比 ActivityComponent 活得更久,所以用 Activity 注入 LoginPresenter 是没有意义的:按照现在的方式,你会得到相同的 LoginPresenter 实例和不同的 Activity。

消息“android.app.Activity 不能在没有@Inject 构造函数的情况下提供”来自 ConfigPersistentComponent 的生成,它没有 Activity 绑定。当然,您的 ActivityComponent 确实如此,但这不是它试图存储 LoginPresenter 及其当前注释的地方。

将该声明切换到@ActivityScope,一切都会好起来的:您将为您创建的每个 Activity 获得不同的 LoginActivityPresenter,并且您还可以访问 @Singleton 范围 (ApplicationComponent) 和 @ConfigPersistent 范围 (ConfigPersistentComponent) 中的所有内容。

@ActivityScope
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {
于 2017-04-14T17:02:20.153 回答