I'm having a heck of a time figuring out how to set up Dagger dependencies to satisfy what is essentially this snippet.
I have a controller, WelcomeScreen, (which extends Path - a Mortar/Flow thing), which states that it injects a ThingView. I then inject a provider of ThingView and my ThingView constructor has an #Inject annotation with an Activity, which I supply elsewhere.
I still end up getting this error: No inject registered for members/com.....view.ThingView. You must explicitly add it to the 'injects' option in one of your modules.
Thoughts on what I'm missing?
public class WelcomeScreen extends Path {
#dagger.Module(
injects = {
ThingView.class,
},
addsTo = ActivityModule.class)
public class Module {
}
#Singleton
public static class Presenter extends ViewPresenter<WelcomeView> {
#Inject
public Presenter(
Provider<ThingView> thingViewProvider,) {
// This causes an error: No inject registered for members/com.....view.ThingView.
// You must explicitly add it to the 'injects' option in one of your modules.
thingViewProvider.get()
}
}
}
public class ThingView extends LinearLayout {
#Inject
public ThingView(Activity activity) {
super(activity);
init(activity);
}
// Note - this might not be used anywhere.
public ThingView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
ObjectGraphService.inject(context, this);
View view = View.inflate(context, R.layout.view_thread_pack, this);
ButterKnife.inject(view);
}
}
Update: I've also tried adding the below which has had no effect on the error message:
#Module(addsTo = ApplicationModule.class, library = true)
public class ActivityModule {
#Provides
public ThreadPackView providesThreadPackView() {
return new ThreadPackView(activity);
}
}
Thats not quite how Mortar works. You cannot inject the view into the presenter. Instead you inject your presenter to your view. Here's aa few samples I found that shows a similar setup to what you are doing https://github.com/Zhuinden/MortarFlowInitialDemo https://github.com/matthew-compton/NerdRoll
Related
i'm new to dagger2.
I'm trying to create an 'ActivityComponent' which will retrieves all information from my activity (ex : context ...), and an another component in which i'm trying to inject the 'Activity component' (in the code below, it's the CheckErrorsModel class).
#Singleton
public class CheckErrorsModel {
private Context context;
#Inject
public CheckErrorsModel(MainActivityComponent mainActivityComponent) {
this.context = mainActivityComponent.getContext();
}
public void test() {
Log.d("test", "test lancé ");
}
}
The Interface component class :
#Singleton
#Component()
public interface CheckErrorsModelDi {
CheckErrorsModel getCheckErrorsModel();
MainActivityComponent getApplicationComponent();
}
And everything related to the Application context class :
#Component(modules = {MainActivityModule.class})
#Singleton
public interface MainActivityComponent {
Context getContext();
void inject(MainActivity mainActivity);
}
the module class :
#Module
public class MainActivityModule {
private final Context context;
public MainActivityModule(Context context) {
this.context = context;
}
#Provides
#Singleton
Context provideContext(){
return context;
}
}
But once i try to build the app :
i got some errors :
.MainActivityComponent cannot be provided without an #Provides-annotated method MainActivityComponent getApplicationComponent();
MainActivityComponent cannot be provided without an #Provides-annotated method.
CheckErrorsModel getCheckErrorsModel()
I think this is because your CheckErrorsModelDi don't have any module.
And Scope (like #Singleton) should always be on Module method and the component
And this should be a subComponent of MainActivityComponent
Alors you inject Method seems à bit strange.
You should use à builder instead see bindings instance
I have two kind of fragment. One of them is ListFragment and the other one is ListWithFilterFragment that ListWithFilterFragment extend ListFragment. Also I have two presenter for these fragments that ListWithFilterPresenter extend ListPresenter. So I define separate module for presenter and separate component in Dagger. I want to inject correct presenter to these fragment but I got error
this is ListFragment:
public class ListFragment<V extends ListView> extends BaseFragment<V, ListPresenter<V>> implements ListView {
#Inject
ListPresenter presenter;
#NonNull
#Override
public ListPresenter createPresenter() {
DaggerListComponent.builder().build().inject((ListFragment<ListView>) this);
return presenter;
}
this is ListWithFilterFragment:
public class ListWithFilterFragment extends ListFragment<ListWithFilterView> implements ListWithFilterView {
#Inject
ListWithFilterPresenter presenter;
#NonNull
#Override
public ListWithFilterPresenter createPresenter() {
DaggerListWithFilterComponent.builder().build().inject(this);
return presenter;
}
}
this is ListComponent:
#Singleton
#Component(modules = {ListPresenterModule.class,
ListInteractorModule.class})
public interface ListComponent {
void inject(ListFragment<ListView> fragment);
}
this is ListWithFilterComponent:
#Singleton
#Component(modules = {ListWithFilterPresenterModule.class,
ListInteractorModule.class})
public interface ListWithFilterComponent {
void inject(ListWithFilterFragment fragment);
}
and my presenters modules
#Module
public class ListWithFilterPresenterModule {
#Provides #NonNull #Singleton
public ListWithFilterPresenter providePresenter(ListInteractor interactor) {
return new ListWithFilterPresenter(interactor);
}
}
#Module
public class ListPresenterModule {
#Provides #NonNull #Singleton
public ListPresenter providePresenter(ListInteractor interactor) {
return new ListPresenter(interactor);
}
}
Error:(16, 10) error: com.sober.appModules.List.presenter.ListPresenter cannot be provided without an #Provides- or #Produces-annotated.method.com.sober.appModules.List.presenter.ListPresenter is injected at
com.sober.appModules.List.ui.ListFragment.presenter
com.sober.appModules.List.ui.ListWithFilterFragment is injected at com.sober.appModules.List.injection.ListWithFilterComponent.inject(fragment)
Dagger does not do partial injections. Either it can provide all the objects, or it will fail with cannot be provided.
In your case ListWithFilterComponent cannot provide ListPresenter to ListWithFilterFragment—or rather its ListFragment parent.
see comments at the end of the lines
error: List.presenter.ListPresenter cannot be provided without an #Provides- or #Produces-annotated.method.
List.presenter.ListPresenter is injected at <- missing object
List.ui.ListFragment.presenter <- field in parent that is getting injected
List.ui.ListWithFilterFragment is injected at <- object it tries to inject
List.injection.ListWithFilterComponent.inject(fragment) <- component that's missing something
If you want to keep your current setup you will need to provide ListPresenter from your ListWithFilterComponent as well, or otherwise you will need to refactor your fragments so that they don't extend one another.
I'm following this tutorial to add Dagger 2 to my Android project.
After doing setup and creating the modules and components I can add the dependencies in an Activity like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_account);
ButterKnife.bind(this);
((AppController) getApplication()).getNetComponent().inject(this);
}
I am struggling in how to inject dependencies in a Fragment and IntentService?
public class FragmentBrandList extends ListFragment {
}
In this class which #Override method should I request injection in and what will be the code for this?
In this class which #Override method i should use and what will be the code to add dependency in fragment?
The correct place to call injection inside a Fragment is onAttach(Context context). This is stated in the where to inject section of the Dagger 2 user guide here
#Override
public void onAttach(Context context) {
((AppController) context.getApplicationContext()).getNetComponent().inject(this);
super.onAttach(context);
}
The correct place to call injection inside a Service is onCreate()
#Override
public void onCreate() {
((AppController) getApplication()).getNetComponent().inject(this);
super.onCreate();
}
Note that in both cases the request for injection comes before the call to super.onCreate(). The Dagger user guide explains it like this:
It is crucial to call AndroidInjection.inject() before super.onCreate() in an Activity, since the call to super attaches Fragments from the previous activity instance during configuration change, which in turn injects the Fragments. In order for the Fragment injection to succeed, the Activity must already be injected. For users of ErrorProne, it is a compiler error to call AndroidInjection.inject() after super.onCreate().
In other words:
The Activity super.onCreate() call re-attaches Fragments from a previous instance
This super call in cause Fragments to be re-injected (since Fragments are injected in onAttach)
Fragments should be injected after their Activity is injected, therefore request injection in your Activity before calling super.onCreate().
You can always check where to inject by looking at the relevant source code for the com.google.dagger:dagger-android classes like DaggerFragment and DaggerService. See the GitHub repo here
For your specific example, please make sure you have added the new injection sites to the NetComponent:
void inject(FragmentBrandList frag);
void inject(BrandListService service);
Step 1: Create your ApplicationModule
#Module
public class ApplicationModule {
private final DIApplication application;
public ApplicationModule(DIApplication application) {
this.application = application;
}
#Provides #Singleton
public DIApplication provideApplication() {
return application;
}
#Provides #Singleton
public DogModel provideDogModel() {
return new DogModelImpl("Scooby-doo");
}
}
Step 2: Create your ApplicationComponent:
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(DIApplication application);
void inject(BaseActivity activity);
void inject(BaseFragment fragment);
void inject(DogSyncService service);
}
Step 3: Create a DI Class:
public class DependencyInjector {
private static ApplicationComponent applicationComponent;
public static void initialize(DIApplication diApplication) {
applicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(diApplication))
.build();
}
public static ApplicationComponent applicationComponent() {
return applicationComponent;
}
private DependencyInjector(){}
}
Final Step: Inject anywhere using:
DependencyInjector.applicationComponent()
Your question inspired me to create a Demo project that shows Activity, Fragment and Service injection using Dagger2. Here is the git: https://github.com/write2sv/AndroidDIDagger2/tree/master/app/src/main/java/work/shaggy/didemo
I did it using
((AppController) getActivity().getApplication()).getNetComponent().inject(this);
in Fragment.onCreate() method.
((MyApp) context.getApplicationContext()).getApplicationComponent().inject(MyFragment.this);
I added this in onAttach(Context context) method of the fragment.
You really just need to include the injector method for whatever you want to inject.
#Singleton
#Component
public interface AppComponent {
void inject(MainActivity activity);
void inject(FragmentBrandList fragmentBrandList);
}
You can use autodagger2 to avoid having to write all that boilerplate. I use this architecture quite often:
build.gradle
apt 'com.github.lukaspili.autodagger2:autodagger2-compiler:1.1'
compile 'com.github.lukaspili.autodagger2:autodagger2:1.1'
YourApp.java
#AutoComponent( modules = YourApp.YourAppModule.class )
public class YourApp extends Application {
private static YourApp instance;
private YourAppComponent component;
public YourAppComponent getComponent() {
return this.component;
}
#Override
public void onCreate() {
super.onCreate();
setupComponent();
}
private void setupComponent() {
component = DaggerYourAppComponent.builder()
.yourAppModule(new YourAppModule(instance))
.build();
}
#dagger.Module
public static class YourAppModule {
private YourApp app;
YourAppModule(YourAppApp application) {
this.app = application;
}
#Provides #AutoExpose(YourApp.class)
Application provideApplication() {
return app;
}
#Provides #AutoExpose(PoswalaApp.class)
Context provideContext() {
return app;
}
#Provides #AutoExpose(YourApp.class)
Retrofit provideApiAdapter() {
return ApiService.getServiceInstance();
}
}
}
YourActivity.java
#AutoComponent(
dependencies = YourApp.class,
modules = YourActivity.YourActivityModule.class
)
public class YourActivity extends BaseActivity implements YourActivityView {
private YourActivityComponent component;
#Inject MyPresenter presenter
// This is an abstract method from BaseActivity
#Override
protected void setupComponent(YourAppComponent appComponent) {
component = DaggerYourActivityComponent.builder()
.yourAppComponent(((YourApp) getApplication()).getComponent())
.yourActivityModule(new YourctivityModule(this))
.build();
}
#Override
protected MyPresenter getPresenter() {
return presenter;
}
#dagger.Module
public static class YourActivityModule {
private YourActivityView view;
YourActivityModule(YourActivityView view) {
this.view = view;
}
#Provides #AutoExpose(YourActivity.class)
YourActivityView provideView() {
return view;
}
// Your other dependencies
}
}
Quick explanation:
Your app's module will have to a "universal" dependency, but this way you can achieve using several modules for a class. You just need to customize the
#AutoComponent(
dependencies = YourApp.class,
modules = { YourActivity.YourActivityModule.class, YourFragment.YourFragmentModule.class }
)
block. You can add as many modules as you like using that syntax.
Hope this helps you
I have been using using dagger 2 in my project lately,
the problem is when I try to build my project, the presenter in my login
activity which injected like below is null,
and when I try to build the project
presenter cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method...
I don't understand what have I done wrong??, please someone help me with this,
thanks in advance.
Here is my Login Activity, the presenter here is null, which shows that, I've not injected it properly
#Inject
LoginPresenter presenter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
InjectHelper.getRootComponent().injectPresenter(this);
presenter.setProgressBarVisiblity(View.INVISIBLE);
}
this is my presenter module
#Module
public class PresenterModule {
private final LoginActivity activity;
public PresenterModule(LoginActivity activity) {
this.activity = activity;
}
#Provides
#Singleton
public LoginActivity providesView() {
return activity;
}
}
#Provides
#Singleton
public LoginPresenter providesPresenter()
{
return new LoginPresenter();
}
}
this inject helper class
public class InjectHelper {
private static RootComponent sRootComponent;
static {
initModules();
}
private static void initModules() {
sRootComponent = getRootComponentBuilder().build();
}
public static DaggerRootComponent.Builder getRootComponentBuilder() {
return DaggerRootComponent.builder();
}
public static RootComponent getRootComponent() {
if (sRootComponent == null) {
initModules();
}
return sRootComponent;
}
}
this is root component class
#Singleton
#Component(modules = {
PresenterModule.class
})
public interface RootComponent {
void injectLoginView(LoginPresenter loginPresenter);
}
you need to inform the dagger which views want to use injection, in your component.
you must change the inject function code in your component to below:
void inject(LoginActivity activity);
for showing dagger what you want, you need to use #dagger annotation NOT by sending it as inject function in component file. as you did properly:
#Inject
LoginPresenter presenter;
Dagger will search for a variable of type LoginPresenter in your module and finds the proper provider method using the type.
what you put in your component as a argument for "inject" function tells the Dagger what view you are going to do injection in (NOT what you want to inject)
#Singleton
#Component(modules = {PresenterModule.class})
public interface RootComponent {
void inject(LoginActivity activity);
}
I am trying to convert my singleton CookieUtil to be injected with Dagger into LoginActivity. CookieUtil takes an application context therefor I have set up the following structure :
AndroidModule
#Module(
injects = {
CookieUtil.class,
LoginActivity.class
},
library = true
)
public class AndroidModule {
private final App application;
public AndroidModule(App application) {
this.application = application;
}
/**
* Allow the application context to be injected but require that it be annotated with
* {#link ForApplication #Annotation} to explicitly differentiate it from an activity context.
*/
#Provides
#Singleton
#ForApplication
Context provideApplicationContext() {
return application;
}
#Provides
#Singleton
CookieUtil provideCookieUtil() {
return new CookieUtil();
}
}
CookieUtil (What I want to inject into LoginActivity)
#Singleton
public class CookieUtil {
#Inject Context mContext; // gets injected with a app context. is this right ?
private PersistentCookieStore persistentCookieStore;
private CookieUtil() {
// use injected mContext
persistentCookieStore = new PersistentCookieStore(mContext);
// ...
}
}
LoginActivity (Where I want to inject CookieUtil)
public class LoginActivity extends BaseActivity {
#Inject CookieUtil cookieUtil;
#Override
protected void onCreate(Bundle savedInstanceState) {
// use CookieUtil ...
}
}
I have also set up all the bootstraping stuff from Dagger examples to enable everything to work
BaseActivity
public class BaseActivity extends ActionBarActivity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Perform injection so that when this call returns all dependencies will be available for use.
((App) getApplication()).inject(this);
}
}
App
public class App extends Application {
private ObjectGraph graph;
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(getModules().toArray());
// ...
}
protected List<Object> getModules() {
return Arrays.asList(
new AndroidModule(this),
new CupsModule() // Another module I haven't listed
);
}
public void inject(Object object) {
graph.inject(object);
}
}
ForApplication
#Qualifier
#Retention(RUNTIME)
public #interface ForApplication {
}
When I run the code, I get
No injectable members on android.content.Context. Do you want to add
an injectable constructor? required by CookieUtil for AndroidModule
What am I doing wrong ?
Everything still looks like magic to me as I don't fully aware of how to wire everything up, so detailed explanation would be very much appreciated.
Also, would be helpful if someone could point me to an explanation on dagger modules, when does it make sense to separate into two different modules ? What logical pieces do they usually bind ?
Thanks
EDIT
Changed suggested by Christian Gruber
#Singleton
public class CookieUtil {
private Context mContext;
private PersistentCookieStore persistentCookieStore;
#Inject
public CookieUtil(Context context) {
// use injected context
mContext = context
persistentCookieStore = new PersistentCookieStore(mContext);
// ...
}
}
Cookie cannot have a private constructor and still be created (provisioned) by Dagger. You can have a package-friendly constructor, and then it should work without #Provides CookiUtil .... Having the provides method for a class you control and could make injectable seems wasteful.
Generally speaking, Dagger considers a "binding" according to a "key" which is its type (with concrete type parameters, such as Foo<Bar>) along with any #Qualifier. So the type Foo<Bar> is different from #ForApplication Foo<Bar>. A binding is requested wherever #Inject occurs, and is supplied wherever #Provides occurs (or for unqualified bindings, if a class has an #Inject-marked constructor or fields, then an implicit binding is supplied. For every #Inject field or constructor parameter there must be a present binding for that key. Dagger needs to be able to see the methods it uses to create things, so generally private fields, methods, and constructors are not possible.
Also, please never simply inject Context without a qualifier. Or better, inject Application or Activity if that's the Context subtype you mean. Your graph WILL be impossible to manage if you don't distinguish between the 30,000 things that Android calls Context subtypes. (snark)