首页 > 解决方案 > Dagger 2 - 无法将成员注入原始类型

问题描述

我需要三级继承并用dagger2注入

1.MainActivity

--1.1 MainSubActivity

----1.1.1 MainSubActivityOne

----1.1.2 MainSubActivityTwo(与MainSubActivityOne结构相同)

主要活动

public abstract class MainActivity<T extends MainPresenter> extends BaseActivity implements MainView{

     @Inject
     protected T mPresenter;

}

主讲者

public abstract class MainPresenter<T extends MainView> extends BasePresenter<T> { ... }

主视图

public interface MainView extends BaseView{ ... }

-- 主子活动

public abstract class MainSubActivity extends MainActivity<MainSubPresenter> implements MainSubView { ... }

-- MainSubPresenter

public abstract class MainSubPresenter<T extends MainSubView> extends MainPresenter<T> { ... }

-- 主子视图

public interface MainSubView extends MainView { ... }

---- MainSubActivityOne(与 MainSubActivityTwo 相同):

public class MainSubActivityOne extends MainSubActivity implements MainSubViewOne{

    @Override
    protected void onCreatePresenter(){
        mPresenter.setView(this);
        mPresenter.onCreate();
    }

    @Override
    protected void initializeDagger() {
        getActivityComponent().inject(this);
    }

}

---- MainSubPresenterOne(与 MainPresenterTwo 相同):

public class MainSubPresenterOne extends MainSubPresenter<MainSubViewOne> { ... }

---- MainSubViewOne(与 MainSubViewTwo 相同):

public interface MainSubViewOne extends MainSubView { ... }

活动组件

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = 
{ActivityModule.class})
public interface ActivityComponent { 

    void inject(MainActivity mainActivity);

}

活动模块

@Provides
@PerActivity
MainPresenter provideMainPresenter() {
    return new MainSubPresenterOne();
}

当我只有两个级别时,一切正常,但现在我收到此错误:

...components/ActivityComponent.java:90: error: [Dagger/MembersInjection] Cannot inject members into raw type com.example.main.MainActivity
void inject(MainActivity mainActivity);
     ^
  com.example.main.MainActivity is injected at
      ...components.ActivityComponent.inject(com.example.main.MainActivity)

如果我将活动组件更改为:

void inject(MainSubActivityOne activity);
void inject(MainSubActivityTwo activity);

我得到了下一个错误:

.../components/ActivityComponent.java:92: error: [Dagger/MissingBinding] com.example.main.MainSubPresenterOne cannot be provided without an @Provides-annotated method.
void inject(MainSubActivityOne mainActivity);
     ^
  com.example.main.MainSubPresenter is injected at
      com.example.main.MainActivity.mPresenter
  com.example.main.MainSubPresenterOne is injected at
      ...components.ActivityComponent.inject(com.example.main.MainSubActivityOne)

标签: androidinheritanceabstract-classdagger-2

解决方案


This line is your problem:

void inject(MainActivity mainActivity);

MainActivity<T> needs a generic type argument, but that's irrelevant. It's an abstract class. You're not injecting this common parent class. You're injecting the instances of its concrete children. Here's what you should do instead:

void inject(MainSubActivityOne activity);
void inject(MainSubActivityTwo activity);

[Dagger/MissingBinding] com.example.main.MainSubPresenterOne cannot be provided without an @Provides-annotated method.

This is all true. Your MainSubActivityOne expects a MainSubPresenterOne here:

 @Inject
 protected T mPresenter;

Yet you only created a binding for MainPresenter:

@Provides
@PerActivity
MainPresenter provideMainPresenter() {
    return new MainSubPresenterOne();
}

This means that Dagger knows only how to inject a MainPresenter, it doesn't care that the MainPresenter is actually a MainSubPresenterOne.

Instead, what I would do is to scope the concrete presenters and let them have an @Inject constructor:

@PerActivity
public class MainSubPresenterOne extends MainSubPresenter<MainSubViewOne> { 

    @Inject
    public MainSubPresenterOne() {
        // ...
    }

    // ...
}

Now Dagger knows how to inject MainSubPresenterOne. Remove the @Provides method.

I recommend the official documentation, which – among other things – explains that @Provides is a kind of last resort and you should prefer @Inject on the types under your control.


Alternatively you would

 @Inject
 protected MainPresenter mPresenter;

and create a separate subcomponent for each of your activities with a module providing the actual presenter:

@Module
abstract class MainSubActivityOneModule {

    @Binds
    abstract MainSubPresenter<MainSubViewOne> bindMainPresenter(MainSubPresenterOne impl);
}

This assumes that the activity doesn't care about the concrete implementation of its presenter, which may or may not be what you want.


推荐阅读