imgen
imgen

Reputation: 3133

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.

Upvotes: 5

Views: 3146

Answers (1)

nPn
nPn

Reputation: 16748

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);
}

Upvotes: 8

Related Questions