I checked all questions but did not find any clue. I stripped my problem to a simplest code:
Situation:
I want to have:
CatComponent catComponent = DaggerCatComponent.builder()
.kameModule(new KameModule(MainActivity.this))
.build();
catComponent.getCatAnalyzer().analyze();
I have created component:
#Component(modules = {KameModule.class, CatAnalyzerModule.class})
public interface CatComponent {
CatAnalyzer getCatAnalyzer();
}
And modules:
#Module
public class KameModule {
private Context context;
public KameModule(Context context) {
this.context = context;
}
#Provides
KameCat provideKameCat() {
return new KameCat(context );
}
}
#Module(includes = KameModule.class)
public class CatAnalyzerModule {
#Inject
KameCat cat;
#Provides
CatAnalyzer provideCatAnalyzer() {
return new CatAnalyzer(cat);
}
}
And classes:
public class KameCat {
Context context;
public KameCat(Context context) {
this.context = context;
}
public void doCatStuff() {
Toast.makeText(context, "Poo and Meow", Toast.LENGTH_LONG).show();
}
}
public class CatAnalyzer {
#Inject
KameCat cat;
#Inject
public CatAnalyzer(KameCat cat) {
this.cat = cat;
}
void analyze() {
cat.doCatStuff();
}
}
When I retrieve my CatAnalyzer object from CatComponent it has cat field nulled.
I have no idea why Dagger won't inject it. Could you guide me somehow?
Proper code:
#Module(includes = KameModule.class)
public class CatAnalyzerModule {
#Inject //remove this
KameCat cat;// remove this
#Provides
// Add cat as a argument and let KameModule provide it..
CatAnalyzer provideCatAnalyzer(KameCat cat) {
return new CatAnalyzer(cat);
}
}
Thanks to:
https://www.future-processing.pl/blog/dependency-injection-with-dagger-2/
Related
I was following some examples to work on dagger2. Here I was using dependencies on HomeFragmentComponent to provide reference of context from another scope but its not working.
ContextModule
#Module
public class ContextModule {
private final Context context;
public ContextModule(Context context) {
this.context = context;
}
#Provides
#ShikshyaScope
public Context context(){
return context;
}
}
Network Module :
#Module(includes = ContextModule.class)
public class NetworkModule {
#Provides
#ShikshyaScope
public File file(Context context){
File cacheFile = new File(context.getCacheDir(),"okhttp_cache");
cacheFile.mkdirs();
return cacheFile;
}
ShikshyaApplicationComponent:
#ShikshyaScope
#Component(modules = {NetworkModule.class, PicassoModule.class, StorageModule.class})
public interface ShikshyaApplicationComponent {
void injectShikshyaApplication(ShikshyaBusApplication shikshyaBusApplication);
}
Home Fragment Module :
#Module
public class HomeFragmentModule {
public final HomeFragment homeFragment;
public HomeFragmentModule(HomeFragment homeFragment) {
this.homeFragment = homeFragment;
}
#Provides
#HomeFragmentScope
public HomeFragment homeFragment(){
return homeFragment;
}
#Provides
#HomeFragmentScope
public HomeFragmentView homeFragmentView(HomeFragment homeFragment){
return (HomeFragmentView)homeFragment;
}
#Provides
#HomeFragmentScope
public HomeFragmentPresenter homeFragmentPresenter(HomeFragmentView homeFragmentView,MetaDatabaseRepo metaDatabaseRepo){
return new HomeFragmentPresenter(homeFragmentView,metaDatabaseRepo);
}
#Provides
#HomeFragmentScope
public DatabaseHelper databaseHelper(Context context){
return OpenHelperManager.getHelper(context,DatabaseHelper.class);
}
}
HomeFragmentComponent :
#HomeFragmentScope
#Component(modules = HomeFragmentModule.class,dependencies =ShikshyaApplicationComponent.class)
public interface HomeFragmentComponent {
void injectHomeFragment(HomeFragment homeFragment);
}
Now I get error as
error: android.content.Context cannot be provided without an #Provides-annotated method.
android.content.Context is injected at com.bihani.shikshyabus.di.module.HomeFragmentModule.databaseHelper(context)
com.bihani.shikshyabus.database.DatabaseHelper is injected at
You should include ContextModule as HomeFragmentModule dependency so Dagger2 be able to provide context to DatabaseHelper
#Module(includes = ContextModule.class)
public class HomeFragmentModule {
// Your stuff here
}
Sorry for the long question / code and my english skills :)
I want to use Dagger-2 in my new android app. I have no experience with Dagger-2 or other dependency libraries. I think i misunderstand something...
Here are the relevant application. If the activity "OverviewActivity" starts it injects the "OverviewPresenter" and so on.. (OverviewActivity->OverviewPresenter->CategoriesPovider->DBOpenHelper->DBDefaults).
At the first start the class DBDefaults should prefill the database with some default values. At the time it should access the resources files i get a NPE. Can anybody tell me why this happens? I tried to use the "Log" tool to check if any needed variable is null but everything looks fine. Gets the DBDefaults class the wrong context?
Finally the method throws a NPE
context.getResources().getStringArray(R.array.category_colors);
But here is my code.
AppComponent.java
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
Context getContext();
Application getApplication();
DBOpenHelper getDBOpenHelper();
}
AppModule.java
#Module
public class AppModule {
private final Manager app;
public AppModule(Manager app) {
this.app = app;
}
#Singleton
#Provides
public Application provideApplication() {
return app;
}
#Singleton
#Provides
public Context provideContext() {
return app.getApplicationContext();
}
#Singleton
#Provides
public DBOpenHelper provideDBOpenHelper() {
return new DBOpenHelper(app);
}
}
OverviewComponent.java
#ActivityScope
#Component(dependencies = AppComponent.class)
public interface OverviewComponent {
void inject(OverviewActivity activity);
OverviewPresenter getOverviewPresenter();
}
OverviewPresenter.java
public class OverviewPresenter extends MvpBasePresenter<OverviewView> {
#Inject
public OverviewPresenter(CategoriesProvider provider) {
Log.d("OverviewPresenter", "presenter..");
}
}
CategoriesProvider.java
public class CategoriesProvider extends BaseProvider {
#Inject
public CategoriesProvider(DBOpenHelper dbOpenHelper) {
super(dbOpenHelper);
// Just testing...
dbOpenHelper.getReadableDatabase();
}
}
DBOpenHelper.java
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String db = "manager";
private static final int version = 1;
private Context context;
#Inject
public DBOpenHelper(Context context) {
super(context, db, null, version);
this.context = context;
Log.d("DBOpenHelper", "database...");
if (context == null) {
Log.d("DBOpenHelper", "wtf...");
}
}
#Override
public void onCreate(SQLiteDatabase db) {
// db structure
db.execSQL(CategoriesTable.getCreateTableQuery());
db.execSQL(UsersTable.getCreateTableQuery());
// default values
DBDefaults defaults = new DBDefaults(db, context);
defaults.insertSystemUser();
defaults.insertCategories();
}
}
DBDefaults.java
public class DBDefaults {
private SQLiteDatabase db;
private Context context;
public DBDefaults(SQLiteDatabase db, Context context) {
this.db = db;
this.context = context;
if (context == null) {
Log.d("DBDefaults", "wtf... in Defaults");
}
}
public void insertCategories() {
String[] test = context.getResources().getStringArray(R.array.category_colors);
for (String item : test) {
Log.d("Categories", item);
}
// NPE ?!?!?!
}
}
EDIT
My Manger class. Here I'm storing my app component and init my AppModule
public class Manager extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
My Activity
public class OverviewActivity extends MvpActivity<OverviewView, OverviewPresenter> {
public OverviewComponent overviewComponent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_navigation);
}
#Override
public void inject() {
Log.d("Activity", "inject");
overviewComponent = DaggerOverviewComponent.builder()
.appComponent(((MoneyManager)getApplication()).getAppComponent())
.build();
overviewComponent.inject(this);
}
#Override
public OverviewPresenter getPresenter() {
Log.d("Activity", "getPresenter");
return overviewComponent.getOverviewPresenter();
}
}
If CategoriesProvider is a ContentProvider, you might get null when calling Application.getApplicationContext(). So the context that you're injecting could be null. You can verify this in AppModule. Try logging the result before returning it. Note that Application.getApplicationContext() normally returns the same instance, so you could just return app; instead of what you currently have.
So I am trying to get my head around Dagger2 and it's dependency injection. Here is my current setup.
SQLiteModelModule
#Module
public class SQLiteModelModule {
private final int versionNumber;
private final Context context;
private final String databaseName;
public SQLiteModelModule( Context context, String databaseName, int versionNumber ) {
this.context = context;
this.databaseName = databaseName;
this.versionNumber = versionNumber;
}
#Provides
#Singleton
public SQLite providesSQLite() {
return SQLite.newInstance(context,databaseName,versionNumber);
}
#Provides
#Singleton
public SQLiteModel providesSQLiteModel( SQLite sqlite) {
return new SQLiteModel( sqlite );
}
}
Because this is a library and the ModelManager may contain different modules other than the SQLiteModel I have created a TestModelManagerModule to provide the manager.
TestModelManagerModule
#Module
public class TestModelManagerModule {
#Provides
#Singleton
public ModelManager providesModelManager( SQLiteModel model ) {
ModelManager manager = new ModelManager();
manager.registerModel(model,"sosmv1",true);
return manager;
}
}
Then to tie them all together for my tests I have a test component.
SQLiteTestComponent
#Singleton
#Component( modules = {TestModelManagerModule.class, SQLiteModelModule.class})
public interface SQLiteTestComponent {
ModelManager provideModelManager();
}
This works when I do the following and the correct type of 'IModel' Object is returned.
private ModelManager modelManager;
private SQLiteTestComponent sqLiteTestComponent;
#Before
public void setup() {
Context context = Mockito.mock(Context.class);
this.sqLiteTestComponent = DaggerSQLiteTestComponent.builder()
.sQLiteModelModule(new SQLiteModelModule(context, "TestDB", 1))
.build();
this.modelManager = sqLiteTestComponent.provideModelManager();
}
#Test
public void testCreation() {
IModel model = modelManager.getDefaultModel();
Assert.assertEquals(SQLiteModel.class, model.getClass());
}
MY QUESTION
I have a class OMKObject that has a dependency on ModelManager. It uses this dependency not only in constructors but also in a field. How do I go about injecting this?
Also is the above all correct? Or have I done something wrong in the setup?
I am trying to provide SettingsPresenter to SettingsActivity (View) and got "dagger class could not be bound with key" error, please help me fix it and figure out reason of the error.
error:
Error:(32, 8) error: presenter.ISettingsPresenter could not be bound with key presenter.ISettingsPresenter required by ui.activity.settings.SettingsActivity for dagger.AppModule
ModelsModule provides securityModel and userModel and it's working fine;
My code is:
SettingsActivity:
SettingsActivity implements ISettingsView {
#Inject ISettingsPresenter presenter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
createScopedGraph(new SettingsModule(this)).inject(this);
presenter.onCreate();
//...
}
public ObjectGraph createScopedGraph(Object... modules) {
return objectGraph.plus(modules);
}
}
AppModule:
#Module(
injects = {
App.class,
},
includes = {
AnalyticsModule.class,
ModelsModule.class
})
public class AppModule {
private App app;
public AppModule(App app) {
this.app = app;
}
SettingsModule:
#Module(
injects = SettingsActivity.class,
addsTo = AppModule.class,
complete = false)
public class SettingsModule {
private final ISettingsView view;
public SettingsModule(ISettingsView settingsView) {
this.view = settingsView;
}
#Provides
public ISettingsView provideView() {
return this.view;
}
#Provides
public ISettingsPresenter providePresenter(ISettingsView view, UserModel userModel, SecurityModel securityModel) {
return new SettingsPresenter(view, userModel, securityModel);
}
}
Let's say I have three Dagger modules:
#Module()
public class MainModule {
private Application application;
public MainModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application provideApplication() {
return application;
}
#Provides
Something provideSomething(Application application) {
return new Something(application);
}
}
#Module()
public class SubModule1 {
private Activity activity;
public SubModule1(Activity activity) {
this.activity = activity;
}
#Provides
#Singleton
Activity provideActivity() {
return activity;
}
#Provides
SomethingElse provideSomethingElse(Activity activity) {
return new SomethingElse(activity);
}
}
#Module()
public class SubModule2 {
private Activity activity;
public SubModule2(Activity activity) {
this.activity = activity;
}
#Provides
#Singleton
Activity provideActivity() {
return activity;
}
#Provides
Anything provideAnything(Activity activity) {
return new Anything(activity);
}
}
Now assuming I want to do something like this:
public class MyApplication extends Application {
...
#Override
public void onCreate() {
super.onCreate();
objectGraph = ObjectGraph.create(new MainModule(this));
objectGraph.inject(this);
}
}
public class MyActivity1 extends Activity {
...
#Override
public void onCreate() {
super.onCreate();
objectGraph = ((MyApplication) getApplication()).getObjectGraph().plus(new SubModule1(this)
objectGraph.inject(this);
}
}
public class MyActivity2 extends Activity {
...
#Override
public void onCreate() {
super.onCreate();
objectGraph = ((MyApplication) getApplication()).getObjectGraph().plus(new SubModule2(this)
objectGraph.inject(this);
}
}
It also might happen that I have such a class:
public class TestClass {
#Inject
SomethingElse somethingElse;
#Inject
Anything anything;
}
What is the right way to implement this? How should I use includes, addsTo, injects, library, complete and plus()?
I usually set Dagger up as follows:
Extend Application like this:
public class InjectingApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
objectGraph = ObjectGraph.create(new MainModule(this));
objectGraph = ObjectGraph.create(new SubModule1(this));
objectGraph = ObjectGraph.create(new ActivityModule());
objectGraph.inject(this);
}
}
Then have a module for adding references to your Activities (called ActivityModule.java in this instance):
#Module(
injects = {
MyActivity1.class,
MyActivity2.class }
) public class ActivityModule { }
Then your Activities will look like this:
public class MyActivity1 extends Activity {
#Inject
Something something;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((InjectingApplication) getApplication()).inject(this);
}
}
Finally, your modules will look like this:
#Module()
public class MainModule {
private Context context;
public MainModule(Context context) {
this.context = context;
}
#Provides
Something provideSomething(Context context) {
return new Something(context);
}
}
#Module()
public class SubModule1 {
private Context context;
public SubModule1(Context context) {
this.context = context;
}
#Provides
SomethingElse provideSomethingElse(Context context) {
return new SomethingElse(context);
}
}