Dagger 2 Scoped Activity 上下文问题
Dagger 2 Scoped Activity Context Issue
我正在尝试关注 this template 的 MVP/Dagger2/RxJava 项目。
我无法将 Activity 上下文注入到我的演示者中,每次其他注入都会通过,因为我知道子组件可以开放访问所有父级提供的逻辑。
application 组件在Application class 中构建,然后在base presenter activity 中访问,然后注入presenter 和其余部分的相关依赖项。配置持久化组件主要用于保存演示者状态。
如果我只是手动将上下文从 activity 传递给演示者,那么 DI 的目的就会失败。
我已尝试为所有组件和模块添加作用域,以确保可以从图中正确访问依赖项,但这没有奏效。
我正在尝试使用上下文的构造函数注入,我实际上在 activity 中接收到演示者与之通信但演示者没有的上下文,抛出错误。所以我想知道为什么 activity 可以访问 activity 上下文但演示者不能。
如有任何指导,我们将不胜感激。
Error
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
我的components/modules如下图:
Application Component
@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();
}
Application Module
@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;
}
}
Config Persistent Component
@ConfigPersistent
@Component(dependencies = ApplicationComponent.class)
public interface ConfigPersistentComponent {
ActivityComponent plus(ActivityModule activityModule);
}
Activity Component
@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(LoginActivity loginActivity);
}
Activity Module
@Module
public class ActivityModule {
private Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
@Provides
@PerActivity
Activity providesActivity() {
return activity;
}
}
Application class
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;
}
}
MainPresenter
@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();
}
}
Base Presenter Activity
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));
}
Main Activity
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();
}
}
/********** 编辑 **********/
为了帮助保存演示者的状态并仍然允许传入 activity 上下文,我的解决方案发布在下面。欢迎任何反馈。
@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)
登录Activity
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);
}
登录Activity主持人
@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();
}
Activity 组件
@ActivityContext
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(LoginActivity loginActivity);
}
ConfigPersistentComponent
@ConfigPersistent
@Component(dependencies = ApplicationComponent.class)
public interface ConfigPersistentComponent {
ActivityComponent plus(ActivityModule activityModule);
}
Activity 模块
@Module
public class ActivityModule {
private Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
@Provides
@ActivityContext
Activity providesActivity() {
return activity;
}
}
编辑
我错误地使用了与 activity 模块相同的 activity 上下文。因此,我无法将 activity 注入演示者。将 activity 模块更改为原始 @peractivity 范围并遵循以下答案将使 activity 上下文可注入。
@ConfigPersistent
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {
通过使用@ConfigPersistent 范围标记 LoginActivityPresenter,您告诉 Dagger "manage this class's instance in ConfigPersistentComponent"(即始终 return 来自给定 ConfigPersistentComponent 实例的相同实例),这意味着它不应该从像@ActivityScope 这样的狭窄范围访问任何东西。毕竟,ConfigPersistentComponent 的寿命比 ActivityComponent 长,所以用 Activity 注入 LoginPresenter 没有意义:按照你现在的方式,你会得到相同的 LoginPresenter 实例,但 Activity.
消息 "android.app.Activity cannot be provided without an @Inject constructor" 来自 ConfigPersistentComponent 的生成,它没有 Activity 绑定。当然,您的 ActivityComponent 可以,但它不是尝试存储 LoginPresenter 及其当前注释的地方。
将该声明切换到@ActivityScope,一切都会好起来的:对于您创建的每个 Activity,您将获得不同的 LoginActivityPresenter,并且您将还可以访问@Singleton 范围 (ApplicationComponent) 和@ConfigPersistent 范围 (ConfigPersistentComponent) 中的所有内容。
@ActivityScope
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {
我正在尝试关注 this template 的 MVP/Dagger2/RxJava 项目。
我无法将 Activity 上下文注入到我的演示者中,每次其他注入都会通过,因为我知道子组件可以开放访问所有父级提供的逻辑。
application 组件在Application class 中构建,然后在base presenter activity 中访问,然后注入presenter 和其余部分的相关依赖项。配置持久化组件主要用于保存演示者状态。
如果我只是手动将上下文从 activity 传递给演示者,那么 DI 的目的就会失败。
我已尝试为所有组件和模块添加作用域,以确保可以从图中正确访问依赖项,但这没有奏效。
我正在尝试使用上下文的构造函数注入,我实际上在 activity 中接收到演示者与之通信但演示者没有的上下文,抛出错误。所以我想知道为什么 activity 可以访问 activity 上下文但演示者不能。
如有任何指导,我们将不胜感激。
Error
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
我的components/modules如下图:
Application Component
@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();
}
Application Module
@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;
}
}
Config Persistent Component
@ConfigPersistent
@Component(dependencies = ApplicationComponent.class)
public interface ConfigPersistentComponent {
ActivityComponent plus(ActivityModule activityModule);
}
Activity Component
@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(LoginActivity loginActivity);
}
Activity Module
@Module
public class ActivityModule {
private Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
@Provides
@PerActivity
Activity providesActivity() {
return activity;
}
}
Application class
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;
}
}
MainPresenter
@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();
}
}
Base Presenter Activity
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));
}
Main Activity
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();
}
}
/********** 编辑 **********/
为了帮助保存演示者的状态并仍然允许传入 activity 上下文,我的解决方案发布在下面。欢迎任何反馈。
@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)
登录Activity
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);
}
登录Activity主持人
@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();
}
Activity 组件
@ActivityContext
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(LoginActivity loginActivity);
}
ConfigPersistentComponent
@ConfigPersistent
@Component(dependencies = ApplicationComponent.class)
public interface ConfigPersistentComponent {
ActivityComponent plus(ActivityModule activityModule);
}
Activity 模块
@Module
public class ActivityModule {
private Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
@Provides
@ActivityContext
Activity providesActivity() {
return activity;
}
}
编辑
我错误地使用了与 activity 模块相同的 activity 上下文。因此,我无法将 activity 注入演示者。将 activity 模块更改为原始 @peractivity 范围并遵循以下答案将使 activity 上下文可注入。
@ConfigPersistent
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {
通过使用@ConfigPersistent 范围标记 LoginActivityPresenter,您告诉 Dagger "manage this class's instance in ConfigPersistentComponent"(即始终 return 来自给定 ConfigPersistentComponent 实例的相同实例),这意味着它不应该从像@ActivityScope 这样的狭窄范围访问任何东西。毕竟,ConfigPersistentComponent 的寿命比 ActivityComponent 长,所以用 Activity 注入 LoginPresenter 没有意义:按照你现在的方式,你会得到相同的 LoginPresenter 实例,但 Activity.
消息 "android.app.Activity cannot be provided without an @Inject constructor" 来自 ConfigPersistentComponent 的生成,它没有 Activity 绑定。当然,您的 ActivityComponent 可以,但它不是尝试存储 LoginPresenter 及其当前注释的地方。
将该声明切换到@ActivityScope,一切都会好起来的:对于您创建的每个 Activity,您将获得不同的 LoginActivityPresenter,并且您将还可以访问@Singleton 范围 (ApplicationComponent) 和@ConfigPersistent 范围 (ConfigPersistentComponent) 中的所有内容。
@ActivityScope
public class LoginActivityPresenter extends BasePresenter<LoginContract.View> {