konmik / nucleus

Nucleus is an Android library, which utilizes the Model-View-Presenter pattern to properly connect background tasks with visual parts of an application.
MIT License
1.97k stars 253 forks source link

Help with injecting Presenter when using AndroidInjector #149

Closed fredagsfys closed 6 years ago

fredagsfys commented 6 years ago

Hi @konmik !

I'm trying to figure out how to use AndroidInjector together with Nucleus. Everything works fine in Fragments and Activities except I don't seem to understand how to inject the Presenters. They are always null.

Issue I get from injection in Presenter inside ComponentReflectionInjector : Caused by: java.lang.reflect.InvocationTargetException Caused by: java.lang.ClassCastException: com.company.app.ui.history.HistoryActivityPresenter cannot be cast to com.company.app.App

Looking like this:

AppComponent

@Singleton
@Component(modules = {ApplicationModule.class, ActivityBindingModule.class,
        AndroidSupportInjectionModule.class, NetworkModule.class, DatabaseModule.class,
        RepositoryModule.class, SharedPreferencesModule.class})
   public interface AppComponent extends AndroidInjector<App> {
    // Gives us syntactic sugar. we can then do ApplicationModule.builder().application(this).build().inject(this);
    // never having to instantiate any modules or say which module we are passing the application to.
    // Application will just be provided into our app graph now.
    @Component.Builder
    interface Builder {

        @BindsInstance
        AppComponent.Builder application(Application application);

        AppComponent build();
    }
}

App

public class App extends DaggerApplication implements Injector {
    private ComponentReflectionInjector<AppComponent> injector;

    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        AppComponent component = DaggerAppComponent.builder().application(this).build();

        injector = new ComponentReflectionInjector<>(AppComponent.class, component);

        return component;
    }

    @Override
    public void inject(Object target) {
        injector.inject(target);
    }
}

BaseActivity

public abstract class BaseActivity<P extends Presenter> extends NucleusAppCompatDaggerActivity<P>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        final PresenterFactory<P> superFactory = super.getPresenterFactory();
        setPresenterFactory(superFactory == null ? null : (PresenterFactory<P>) () -> {
            P presenter = superFactory.createPresenter();
            ((Injector) getApplication()).inject(presenter);
            return presenter;
        });
        super.onCreate(savedInstanceState);
    }
}
fredagsfys commented 6 years ago

Doing like this in the AppComponent makes it work but i surely isn't optimal, right?

@Singleton
@Component(modules = {ApplicationModule.class, ActivityBindingModule.class,
        AndroidSupportInjectionModule.class, NetworkModule.class, DatabaseModule.class,
        RepositoryModule.class, SharedPreferencesModule.class})
public interface AppComponent extends AndroidInjector<HAGSInspection> {

    // Gives us syntactic sugar. we can then do ApplicationModule.builder().application(this).build().inject(this);
    // never having to instantiate any modules or say which module we are passing the application to.
    // Application will just be provided into our app graph now.
    @Component.Builder
    interface Builder {

        @BindsInstance
        AppComponent.Builder application(Application application);

        AppComponent build();
    }

    void inject(HistoryActivityPresenter x);

    void inject(InstallationFragmentPresenter x);
    void inject(AnnualFragmentPresenter x);

    void inject(FunctionalFragmentPresenter x);
    void inject(RoutineFragmentPresenter x);

    void inject(FacilityItemFragmentPresenter x);
    void inject(GalleryFragmentPresenter x);

    void inject(MeasureErrorFragmentPresenter x);
    void inject(MeasureResponseFragment x);

    void inject(AssessmentFragmentPresenter x);
    void inject(MediaFragmentPresenter x);

    void inject(TaskAddFragmentPresenter x);
    void inject(TaskListFragmentPresenter x);
    void inject(TaskDetailFragmentPresenter x);

    void inject(SearchFragmentPresenter x);
}
fredagsfys commented 6 years ago

It seems I could use Subcomponents to separate each component with their corresponding activity/fragment/presenter. But how does the ComponentReflectionInjector work with Subcomponents? Right now we use the injector = new ComponentReflectionInjector<>(AppComponent.class, component); to get the graph.

konmik commented 6 years ago

Hi! There is an example for Dagger 2 usage with Nucleus, here it is: https://github.com/konmik/nucleus/tree/master/nucleus-example-real-life

Otherwise, I didn't have experience with AndroidInjector so I cannot help here.