Dagger Dagger-MissingBinding]

Posted

技术标签:

【中文标题】Dagger Dagger-MissingBinding]【英文标题】: 【发布时间】:2021-12-13 12:11:36 【问题描述】:

这段代码有问题,我找不到。

错误

/home/dashwood/androidStudioProjects/DaggerTest2/app/src/main/java/com/dashwood/daggertest/extra/ApplicationComponent.java:16: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends android.app.Activity>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.app.Activity>>> cannot be provided without an @Provides-annotated method.
public interface ApplicationComponent 
       ^
      java.util.Map<java.lang.Class<? extends android.app.Activity>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.app.Activity>>> is injected at
          com.dashwood.daggertest.di.ActivityInjector(activityInjectors)
      com.dashwood.daggertest.di.ActivityInjector is injected at
          com.dashwood.daggertest.extra.A.activityInjector
      com.dashwood.daggertest.extra.A is injected at
          com.dashwood.daggertest.extra.ApplicationComponent.inject(com.dashwood.daggertest.extra.A)

ActivityInjector.java

public class ActivityInjector 
    private final Map<String, AndroidInjector<? extends Activity>> cache = new HashMap<>();
    private Map<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>> activityInjectors;

    @Inject
    ActivityInjector(Map<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>> activityInjectors) 
        this.activityInjectors = activityInjectors;
    

    void inject(Activity activity) 
        if (!(activity instanceof BaseActivity)) 
            throw new IllegalArgumentException("Activity must extends BaseActivity");
        
        String instanceId = ((BaseActivity) activity).getInstanceId();
        if (cache.containsKey(instanceId)) 
            //noinspection unchecked
            ((AndroidInjector<Activity>) cache.get(instanceId)).inject(activity);
            return;
        
        //noinspection unchecked
        AndroidInjector.Factory<Activity> injectorFactory =
                (AndroidInjector.Factory<Activity>) activityInjectors.get(activity.getClass()).get();
        AndroidInjector<Activity> injector = injectorFactory.create(activity);
        cache.put(instanceId, injector);
        injector.inject(activity);
    

    void clear(Activity activity) 
        if (!(activity instanceof BaseActivity)) 
            throw new IllegalArgumentException("Activity must extends BaseActivity");
        
        cache.remove(((BaseActivity) activity).getInstanceId());
    

    static ActivityInjector get(Context context) 
        return ((A) (context.getApplicationContext())).getActivityInjector();
    

ActivityScope.java

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope 


ControllerKey.java

@MapKey
@Target(ElementType.METHOD)
public @interface ControllerKey 
    Class<? extends Controller> value();

Injector.java

public class Injector 
    private Injector() 

    

    public static void inject(Activity activity) 
        ActivityInjector.get(activity).inject(activity);
    

    public static void clearComponent(Activity activity) 
        ActivityInjector.get(activity).clear(activity);
    

    public static void inject(Controller controller) 
        ScreenInjector.get(controller.getActivity()).inject(controller);
    

    public static void clearComponent(Controller controller) 
        ScreenInjector.get(controller.getActivity()).clear(controller);
    

MainScreenBindingModule.java

@Module(subcomponents = 
        TrendingReposComponent.class,
)
public abstract class MainScreenBindingModule 
    @Binds
    @IntoMap
    @ControllerKey(TrendingReposController.class)
    abstract AndroidInjector.Factory<? extends Controller> bindingTrendingReposInjector(TrendingReposComponent.builder builder);


ScreenInjector.java


@ActivityScope
public class ScreenInjector 

    private final Map<Class<? extends Controller>, Provider<AndroidInjector.Factory<? extends Controller>>> screenInjector;
    private final Map<String, AndroidInjector<Controller>> cache = new HashMap<>();

    @Inject
    ScreenInjector(Map<Class<? extends Controller>, Provider<AndroidInjector.Factory<? extends Controller>>> screenInjector) 
        this.screenInjector = screenInjector;
    

    void inject(Controller controller) 
        if (!(controller instanceof BaseController)) 
            throw new IllegalArgumentException("Controller must extends BaseController");
        
        String instanceId = controller.getInstanceId();
        if (cache.containsKey(instanceId)) 
            Objects.requireNonNull(cache.get(instanceId)).inject(controller);
            return;
        
        //noinspection unchecked
        AndroidInjector.Factory<Controller> injectFactory =
                (AndroidInjector.Factory<Controller>) Objects.requireNonNull(screenInjector.get(controller.getClass())).get();
        AndroidInjector<Controller> injector = injectFactory.create(controller);
        cache.put(instanceId, injector);
        injector.inject(controller);
    

    void clear(Controller controller) 
        if (!(controller instanceof BaseController)) 
            throw new IllegalArgumentException("Controller must extends BaseController");
        
        cache.remove(controller.getInstanceId());
    

    static ScreenInjector get(Activity activity) 
        if (!(activity instanceof BaseActivity)) 
            throw new IllegalArgumentException("Controller must be hosted BaseActivity");
        

        return ((BaseActivity) activity).getScreenInjector();
    

ScreenScope.java

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ScreenScope 


A.java

public class A extends Application 

    @Inject
    ActivityInjector activityInjector;
    private ApplicationComponent component;

    @Override
    public void onCreate() 
        super.onCreate();
        component = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build();
        component.inject(this);
    

    public ActivityInjector getActivityInjector() 
        return activityInjector;
    

ActivityScreenBinding.java

@Module(subcomponents = 
        MainActivityComponent.class,
)
public abstract class ActivityBindingModule 
    @Binds
    @IntoMap
    @ClassKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> provideMainActivtyInjector(MainActivityComponent.Builder builder);

ApplicationComponent.java

@Singleton
@Component(modules = 
        ApplicationModule.class,
        ActivityBindingModule.class,
)
public interface ApplicationComponent 
    void inject(A myApplication);

ApplicationModule.java

@Module
public class ApplicationModule 
    private final Application application;

    public ApplicationModule(Application application) 
        this.application = application;
    

    @Provides
    Context provideApplicationContext() 
        return application;
    

BaseActivity.java

public abstract class BaseActivity extends AppCompatActivity 
    @Inject
    ScreenInjector screenInjector;
    @Inject
    ScreenNavigator screenNavigator;
    private static final String INSTANCE_ID_KEY = "instance_id";
    private String instanceId;
    private Router router;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        if (savedInstanceState != null) 
            instanceId = savedInstanceState.getString(INSTANCE_ID_KEY);
         else 
            instanceId = UUID.randomUUID().toString();
        
        Injector.inject(this);
        ViewGroup screenContainer = findViewById(R.id.screen_container);
        if (screenContainer == null) 
            throw new IllegalArgumentException("Activity must have a view name 'screen_container'");
        
        router = Conductor.attachRouter(this, screenContainer, savedInstanceState);
        screenNavigator.initWithRouter(router, initialScreen());
        setContentView(layoutRes());
        monitorBackStack();
        super.onCreate(savedInstanceState);
        super.onBackPressed();
    

    @LayoutRes
    protected abstract int layoutRes();

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) 
        super.onSaveInstanceState(outState);
        outState.putString(INSTANCE_ID_KEY, instanceId);
    

    public String getInstanceId() 
        return instanceId;
    

    protected abstract Controller initialScreen();

    @Override
    public void onBackPressed() 
        if (!screenNavigator.pop()) super.onBackPressed();

    

    @Override
    protected void onDestroy() 
        super.onDestroy();
        screenNavigator.clear();
        if (isFinishing()) 
            Injector.clearComponent(this);
        
    

    public ScreenInjector getScreenInjector() 
        return screenInjector;
    

    private void monitorBackStack() 
        router.addChangeListener(new ControllerChangeHandler.ControllerChangeListener() 
            @Override
            public void onChangeStarted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler) 

            

            @Override
            public void onChangeCompleted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler) 
                if (!isPush && from != null) 
                    Injector.clearComponent(from);
                
            
        );
    

BaseController.java

public abstract class BaseController extends Controller 

    private boolean isInjected = false;

    @Override
    protected void onContextAvailable(@NonNull Context context) 
        if (!isInjected) 
            Injector.inject(this);
            isInjected = true;
        
        super.onContextAvailable(context);

    

MainActivityComponent.java

@ActivityScope
@Subcomponent(modules = 
        MainScreenBindingModule.class,
        NavigationModule.class,
)
public interface MainActivityComponent extends AndroidInjector<MainActivity> 
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity>
        @Override
        public void seedInstance(MainActivity instance) 

        
    

TrendingReposComponent.java

@ScreenScope
@Subcomponent
public interface TrendingReposComponent extends AndroidInjector<TrendingReposController> 
    @Subcomponent.Builder
    abstract class builder extends AndroidInjector.Builder<TrendingReposController>

    

TrendingReposController.java

public class TrendingReposController extends BaseController 

    @NonNull
    @Override
    protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container, @Nullable Bundle savedViewState) 
        return null;
    

DefaultScreenNavigator.java

@ActivityScope
public class DefaultScreenNavigator implements ScreenNavigator 

    private Router router;

    @Inject
    DefaultScreenNavigator() 

    

    @Override
    public void initWithRouter(Router router, Controller rootScreen) 
        this.router = router;
        if (router.hasRootController()) 
            return;
        
        router.setRoot(RouterTransaction.with(rootScreen));
    

    @Override
    public boolean pop() 
        return router!=null && router.handleBack();
    

    @Override
    public void clear() 
        router = null;
    

NavigationModule.java

@Module
public abstract class NavigationModule 
    @Binds
    abstract ScreenNavigator provideScreenNavigator(DefaultScreenNavigator screenNavigator);

ScreenNavigator.java

public interface ScreenNavigator 
    void initWithRouter(Router router, Controller rootScreen);

    boolean pop();

    void clear();

MainActivity.java

public class MainActivity extends BaseActivity 

    @Override
    protected int layoutRes() 
        return R.layout.activity_main;
    

    @Override
    protected Controller initialScreen() 
        return new TrendingReposController();
    

Gradle

dependencies 

    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'



    implementation 'com.google.dagger:dagger:2.39.1'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.39.1'
    implementation 'com.google.dagger:dagger-android:2.39.1'
    implementation 'com.google.dagger:dagger-android-support:2.39.1'
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.39.1'

    implementation "com.squareup.retrofit2:retrofit:2.7.1"
    implementation "com.squareup.retrofit2:converter-moshi:2.7.1"

    implementation "com.squareup.moshi:moshi:1.12.0"
    annotationProcessor("com.ryanharter.auto.value:auto-value-moshi-extension:1.1.0")
    implementation("com.ryanharter.auto.value:auto-value-moshi-runtime:1.1.0")
    annotationProcessor("com.ryanharter.auto.value:auto-value-moshi-factory:1.1.0")



    annotationProcessor "com.google.auto.value:auto-value:1.8.2"

    implementation 'io.reactivex.rxjava3:rxjava:3.0.3'
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation 'com.jakewharton.rxrelay3:rxrelay:3.0.1'

    implementation 'com.bluelinelabs:conductor:3.1.1'



【问题讨论】:

【参考方案1】:

我修复了这个错误,因为 AndroidInjector.factory 不能扩展任何类。之后我看到了许多错误并修复了所有错误,您可以从我的 GitHub 看到修复此代码

https://github.com/dashwood01/Dagger-Test

【讨论】:

以上是关于Dagger Dagger-MissingBinding]的主要内容,如果未能解决你的问题,请参考以下文章

当 Dagger2 应用在 MVP 框架中

Dagger Dagger-MissingBinding]

Dagger2使用解析

找不到 com.google.dagger:dagger-compiler:3.0.0

Dagger2依赖注入框架

Dagger2