Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Field not injected in Android Dagger project

I am playing with Dagger on Android. I created a model UserPreference, a module called PreferenceModule and another class UserPreferenceTest which is a test of the PreferenceModule. I have below 3 java files

UserPreference.java

package com.sigicn.preference;

import javax.inject.Inject;

import com.sigicn.commonmodels.Application;

public class UserPreference {
    public String name, weiboAccount;

    @Inject
    public Application[] frequentlyUsedApps;
}

Then PreferenceModule.java

package com.sigicn.preference;

import javax.inject.Singleton;

import com.sigicn.commonmodels.Application;
import com.sigicn.utils.MiscUtils;

import dagger.Module;
import dagger.Provides;

@Module(library = true, complete = true)
public class PreferenceModule {

    @Provides @Singleton UserPreference provideUserPreference() {
        UserPreference userPreference = new UserPreference();
        userPreference.frequentlyUsedApps = provideApplications();
        return userPreference;
    }

    @Provides @Singleton Application[] provideApplications() {
        return new Application[]{
                new Application(
                        MiscUtils.generateUUID(), "Youtube"),
                new Application(
                        MiscUtils.generateUUID(), "Pixi")
            };
    }

}

Then UserPreferenceTest.java

package com.sigicn.test.preference;

import javax.inject.Inject;

import com.sigicn.preference.PreferenceModule;
import com.sigicn.preference.UserPreference;

import dagger.Module;
import dagger.ObjectGraph;
import android.test.AndroidTestCase;

public class UserPreferenceTest extends AndroidTestCase {
    @Module(injects = {UserPreference.class, UserPreferenceTest.class}, 
            includes = PreferenceModule.class)
    static class TestModule {
    }

    ObjectGraph objectGraph; 

    @Inject
    UserPreference userPreference;

    @Override
    protected void setUp() throws Exception {
        if (objectGraph == null) {
            objectGraph = ObjectGraph.create(new TestModule());
        }
        super.setUp();
    }

    public void testFrequentlyUsedApps()
    { 
        UserPreference localUserPreference = objectGraph.get(UserPreference.class);
        assertNotNull(localUserPreference);
        assertEquals(localUserPreference.frequentlyUsedApps.length, 2);

        objectGraph.inject(this);
        assertNotNull(userPreference);
        assertEquals(userPreference.frequentlyUsedApps.length, 2);
        assertSame(localUserPreference, userPreference);
        assertSame(localUserPreference.frequentlyUsedApps, userPreference.frequentlyUsedApps);
    }
}

But don't know why, that the frequentlyUsedApps of UserPreference is not injected as expected. Any idea why?

Update:

I think I have figured out the reason. It's because that I manually create UserPreference and use it in the provider. If I remove the Provider for UserPreference, and let Dagger to wire it automatically, then the field frequentlyUsedApps does get injected. So it is my fault of not understanding Dagger well.

like image 364
imgen Avatar asked Aug 18 '14 10:08

imgen


People also ask

How do you inject an activity Dagger?

To inject an object in the activity, you'd use the appComponent defined in your Application class and call the inject() method, passing in an instance of the activity that requests injection. When using activities, inject Dagger in the activity's onCreate() method before calling super.

What is inject in Dagger?

With the @Inject annotation on the constructor, we instruct Dagger that an object of this class can be injected into other objects. Dagger automatically calls this constructor, if an instance of this class is requested.

What is Dagger dependency injection Android?

Dagger 2 is a compile-time android dependency injection framework that uses Java Specification Request 330 and Annotations. Some of the basic annotations that are used in dagger 2 are: @Module This annotation is used over the class which is used to construct objects and provide the dependencies.


1 Answers

I think you need to add some ObjectGraph#inject calls.

In each class where you have an @Inject annotation, you will also need a call to the inject method of the ObjectGraph you created.

I have had been struggling with this for a while also. I think the basic pattern is:

  1. Annotate your fields to indicate you want to inject them
  2. Create a module to "provide" the instances for those @Injects
  3. Create the graph somewhere (seems like most people are doing that in the Application class)
  4. In the classes you want to inject stuff from your module, get an instance of the graph and call inject(this).

I started using a singleton rather than the Application class, because at least for now I have some places were I want to inject the app itself.

So here is what I am currently doing, which seems to work pretty weill

public class Injector {

    private static Injector mInjector;
    private ObjectGraph mObjectGraph;
    private MyApp mApp;

    private Injector() {

    }

    public static Injector getInstance() {
        if (mInjector == null) {
            mInjector = new Injector();
        }
        return mInjector;
    }

    protected List<Object> getModules() {
        return Arrays.asList(
                                new ApplicationModule(mApp),
                                new AndroidModule(mApp)
                             );
    }

    public void inject(Object object) {
        getObjectGraph().inject(object);
    }

    public ObjectGraph getObjectGraph() {
        return mObjectGraph;
    }

    public void initialize(MyApp app) {
        mApp = app;
        mObjectGraph = ObjectGraph.create(getModules().toArray());  
        System.out.println(String.format("init object graph = %s",mObjectGraph.toString()));

    }

}

Then in my application class I have a constructor like this:

public MyApp() {
    System.out.println("myapp construtor");  
    Injector.getInstance().initialize(this);
    Injector.getInstance().inject(this);

}

Then when I want to inject something I do this

@Inject Bus mBus;

public GcmBroadcastReceiver() {
    Injector.getInstance().inject(this);

}

I have two modules , one for production and one for test

The production one has this

@Provides @Singleton
public Bus provideBus () {
    return BusProvider.getInstance();
}

and the test one has this

@Provides @Singleton
public Bus provideBus () {
    return mock(Bus.class);
}
like image 193
nPn Avatar answered Oct 06 '22 05:10

nPn