Android Using single context for multiple Dagger2 modules - android

I'm newbie to use Dagger2 on android. i create some class as Dagger module which they are using context, i can't merge,combine or using single context for other modules which they need that. and i get this error now:
android.content.Context is bound multiple times
SpModules:
#Module
public class SpModules {
private Context context;
public SpModules(Context context) {
this.context = context;
}
#Provides // this can be non-scoped because anyway the same instance is always returned
Context provideContext() {
return this.context;
}
#Provides
#Singleton
SP provideSharePreferences(Context context) {
return new SP(context); // use method-local Context
}
}
RealmModule:
#Module
public class RealmModule {
private Context context;
#Provides
Context provideApplicationContext() {
return AlachiqApplication.getInstance();
}
#Provides
#Singleton
RealmConfiguration provideRealmConfiguration() {
final RealmConfiguration.Builder builder = new RealmConfiguration.Builder()
.schemaVersion(Migration.SCHEMA_VERSION)
.deleteRealmIfMigrationNeeded()
.migration(new Migration());
return builder.build();
}
#Provides
Realm provideDefaultRealm(RealmConfiguration config) {
return Realm.getInstance(config);
}
#Provides
Context provideContext() {
return this.context;
}
}
Component:
#Component(modules = {RealmModule.class, SpModules.class})
#Singleton
public interface ApplicationComponent {
void inject(ActivityRegister target);
void inject(ActivityMain target);
void inject(ActivityBase target);
void inject(FragmentAlachiqChannels target);
void inject(SocketServiceProvider target);
}
and then Application class to make Dagger2:
component = DaggerApplicationComponent.builder()
.appModules(new SpModules(this))
.build();
how can i resolve this problem?

That's simply dagger telling you that you're providing several times the same class to your component. In fact you do it here as well as across different modules for the same component:
#Provides
Context provideApplicationContext() {
return AlachiqApplication.getInstance();
}
#Provides
Context provideContext() {
return this.context;
}
These methods are different, but they're providing the same dependency and without extra info dagger is not able to determine which Context to use.
You got a couple of options. If a single context would be enough to serve all your classes, then you could simply remove the extra provide methods.
However, let's say you need both application and some other context. You can use the annotation Named. Here's how it works, you annotate your provide methods with this annotation which basically will give a name to the dependency. Like so:
#Module
public class SpModules {
// ...
#Provides
#Named("context")
Context provideContext() {
return this.context;
}
//...
}
#Module
public class RealmModule {
//...
#Provides
#Named("application.context")
Context provideApplicationContext() {
return AlachiqApplication.getInstance();
}
//...
}
Now you need to also annotated when you use these dependencies, for example say you have an object that depends on the application context:
public class Something {
public Something(#Named("application.context") Context context) {
//...
}
}
Or as a field:
public class Something {
#Named("application.context") Context context;
// ...
}
Or even in your module:
#Provides
#Singleton
SP provideSharePreferences(#Named("context") Context context) {
return new SP(context);
}
The name can be anything you want as long as they're consistent.
An alternative is to use Qualifiers. They work similar to the Named annotation, but are different. You'll be qualifying the dependency. So say you create 2 qualifiers:
#java.lang.annotation.Documented
#java.lang.annotation.Retention(RUNTIME)
#javax.inject.Qualifier
public #interface InstanceContext {
}
#java.lang.annotation.Documented
#java.lang.annotation.Retention(RUNTIME)
#javax.inject.Qualifier
public #interface ApplicationContext {
}
You can then use these to annotate the provide methods:
#Module
public class SpModules {
// ...
#Provides
#InstanceContext
Context provideContext() {
return this.context;
}
//...
}
#Module
public class RealmModule {
//...
#Provides
#ApplicationContext
Context provideApplicationContext() {
return AlachiqApplication.getInstance();
}
//...
}
You then use it the same way as with name. Here are the examples:
public class Something {
public Something(#ApplicationContext Context context) {
//...
}
}
public class Something {
#ApplicationContext Context context;
// ...
}
#Provides
#Singleton
SP provideSharePreferences(#InstanceContext Context context) {
return new SP(context);
}
Again, these names can be anything. I personally didn't put a lot of effort in thinking about them. Hope this helps.

Related

How to Inject Retrofit using Dagger2

I'm trying to implement MVP pattern using Dagger2. While I successfully did di for application, activities and for fragments (I'm not sure I did well with fragments). Actually after reading guides I still don't understand how it works.
I created RetrofitModiule:
#Module
public class RetrofitModule {
String mBaseUrl;
...
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
}
Then I declare module in MyApplicationComponent:
#Singleton
#Component(
modules = {
MyApplicationModule.class,
RetrofitModule.class
}
)
public interface MyApplicationComponent {
void inject(MyApplication myApplication);
Retrofit provideRetrofit();
}
Actually I don't understand why I have to use Inject here; Because there nothing to actually inject into MyApplication:
public class MyApplication extends Application {
private MyApplicationComponent mMyApplicationComponent;
...
#Override
public void onCreate() {
super.onCreate();
mMyApplicationComponent = DaggerMyApplicationComponent.builder()
.retrofitModule(new RetrofitModule("https://androidtutorialpoint.com"))
.build();
mMyApplicationComponent.inject(this);
}
}
I use Retrofit only in LoaderActivityPresenterImpl which injected to LoaderActivity;
#ActivityScoped
public class LoaderActivityPresenterImpl implements LoaderActivityPresenter {
private LoaderActivityView mView;
private #ActivityContext Context mContext;
private Retrofit mRetrofit;
#Inject
public LoaderActivityPresenterImpl(LoaderActivityView view, #ActivityContext Context context, Retrofit retrofit) {
mView = view;
mContext = context;
mRetrofit = retrofit;
}
}
LoaderActivity:
public class LoaderActivity extends BaseActivity implements LoaderActivityView{
#Inject LoaderActivityPresenter mPresenter;
private LoaderActivityComponent mLoaderActivityComponent;
#Override
protected void setupActivityComponent(MyApplicationComponent myApplicationComponent) {
mLoaderActivityComponent = DaggerLoaderActivityComponent.builder()
.myApplicationComponent(myApplicationComponent)
.loaderActivityModule(new LoaderActivityModule(this, this, myApplicationComponent.provideRetrofit()))
.build();
mLoaderActivityComponent.inject(this);
}
LoaderComponent:
#ActivityScoped
#Component(
modules = LoaderActivityModule.class,
dependencies = MyApplicationComponent.class
)
public interface LoaderActivityComponent {
void inject(LoaderActivity loaderActivity);
}
LoaderActivityModule:
#Module
public class LoaderActivityModule {
private Retrofit mRetrofit;
private LoaderActivityView mLoaderActivityView;
private #ActivityContext Context mContext;
public LoaderActivityModule(LoaderActivityView loaderActivityView, #ActivityContext Context context, Retrofit retrofit) {
mLoaderActivityView = loaderActivityView;
mContext = context;
mRetrofit = retrofit;
}
#Provides
LoaderActivityView provideLoaderActivityView() {
return mLoaderActivityView;
}
#Provides
public #ActivityContext Context provideActivityContext() {
return mContext;
}
#Provides
public LoaderActivityPresenter LoaderActivityPresenterImpl() {
return new LoaderActivityPresenterImpl(mLoaderActivityView, mContext, mRetrofit);
}
}
LoaderActivityComponent:
#ActivityScoped
#Component(
modules = LoaderActivityModule.class,
dependencies = MyApplicationComponent.class
)
public interface LoaderActivityComponent {
void inject(LoaderActivity loaderActivity);
}
I get this error:
java.lang.RuntimeException: Unable to create application com.xxxxx.application.MyApplication: java.lang.IllegalStateException: com.xxxxx.di.modules.MyApplicationModule must be set;
I can probably forget to show some classes, so feel free to ask me.
As the error says, you forget to add your ApplicationModule to your component.
By the way, I highly suggest you take a look at AndroidInjector, to avoid creating this Android component hierarchy manually.
public class MyApplication extends Application {
private MyApplicationComponent mMyApplicationComponent;
...
#Override
public void onCreate() {
super.onCreate();
mMyApplicationComponent = DaggerMyApplicationComponent.builder()
.myApplicationModule()
.retrofitModule(new RetrofitModule("https://androidtutorialpoint.com"))
.build();
mMyApplicationComponent.inject(this);

Dagger2 Optimal way to inject dependencies from two different classes

After searching and trying too many things i'm stuck in some what seems like an easy problem.
Below is my module which is reponsible for injecting retrofit.
#Module
public class NetworkingModule
{
#Provides
public Retrofit providesRetrofit()
{
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
return retrofit;
}
}
My NetworkComponent
#Component( modules = NetworkingModule.class)
public interface NetworkingComponent {
void inject(DashboardPresenterImpl target);
void inject(PicksPresenterImpl picksPresenter);
void inject(LoadsPresenterImpl loadsPresenter);
void inject(ShippingPresenterImpl shippingPresenter);
void inject(GeneralFilePresenterImpl generalFilePresenter);
}
A utility class with constructor injection. Please note this class also has injection of AppPreferences.
public class AppUtils {
private Context context;
#Inject
AppPreferences preferences;
#Inject
public AppUtils(#ActivityContext Context context)
{
this.context = context;
/*ActivityComponent component = DaggerActivityComponent.builder()
.activityModule(new ActivityModule((Activity) context))
.build();
component.inject(this);*/
}
}
Now in my Code i want to achieve this
Class MyPresenterImpl{
#Inject
Retrofit retrofit;
#Inject
AppUtils appUtils;
}
Please suggest an optimize and good way to achieve the above.
EDIT
Added AppPreference.java
public class AppPreferences {
#Inject
SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
#Inject
public AppPreferences(#ActivityContext Context context)
{
ApplicationComponent component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule((Application) context.getApplicationContext()))
.build();
component.inject(this);
editor = sharedPreferences.edit();
}
public void putString(String key, String value)
{
editor.putString(key, value).commit();
}
public String getString(String key)
{
return sharedPreferences.getString(key, null);
}
}
Please decide on whether you want to use field injection or constructor injection, and hopefully choose constructor injection.
public class AppUtils {
private Context context;
#Inject // field injection?
AppPreferences preferences;
#Inject // constructor injection?
public AppUtils(#ActivityContext Context context)
{
// ...
}
}
Dagger won't inject your fields if you use constructor injection and you should not call it yourself afterwards either. Your component should really not contain all those methods to inject your presenter etc.
If you need something, put it in the constructor.
public class AppUtils {
private Context context;
private AppPreferences preferences;
#Inject // constructor injection!
public AppUtils(#ActivityContext Context context, AppPreferences preferences)
{
// ...
}
}
The same applies for MyPresenterImpl. If you depend on something, put it in the constructor, mark the constructor with #Inject, and Dagger will create the object for you with all the dependencies provided.
Your components should only contain .inject(..) method for Android framework types (Activities, Fragments, ...) and nothing else.
I also wrote an article recently with some general concepts about the use of Dagger.
my suggestion:
create context module:
#Module
public class ContextModule
{
Context context;
public ContextModule(Context context) {
this.context = context;
}
#Provides
#AppScope
Context context() {
return context;
}
}
create SharedPreferences module
#Module
public class SharedPreferencesModule
{
#Provides
#AppScope
AppSharedPreferences provideAppSharedPreferences(Context context) {
return new AppSharedPreferences(context.getSharedPreferences("App",Context.MODE_PRIVATE));
}
}
same way You can create more modules if you need to (for example for AppUtils)
instead of creating separate component for network, create one for your app
#Component( modules = {ContextModule.class, NetworkingModule.class, SharedPreferencesModule})
public interface AppComponent {...

Repository module implementation with Context

I would like to implement Repository module to handle data operations. I have JSON file in row directory and want create concrete Repository implementation to get data from file. I'm not sure if I can use Context as attribute in the constructor or method of Repository.
e.g.
public class UserRepository {
UserRepository() {}
public List<User> loadUserFromFile(Context contex) {
return parseResource(context, R.raw.users);
}
}
IMHO, you should use DI (Dependency Injection) like Dagger2, to provide you Context something like,
AppModule.class
#Module
public class AppModule {
private Context context;
public AppModule(#NonNull Context context) {
this.context = context;
}
#Singleton
#Provides
#NonNull
public Context provideContext(){
return context;
}
}
MyApplication.class
public class MyApplication extends Application {
private static AppComponent appComponent;
public static AppComponent getAppComponent() {
return appComponent;
}
#Override
public void onCreate() {
super.onCreate();
appComponent = buildComponent();
}
public AppComponent buildComponent(){
return DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
}
UserRepository.class
#Singleton
public class UserRepository {
UserRepository() {}
#Inject
public List<User> loadUserFromFile(Context contex) {
return parseResource(context, R.raw.users);
}
}
Happy Coding..!!
I don't see any harm in passing the context as an attribute. If you dislike the idea then you can retrieve the context via a convenient method : Static way to get 'Context' on Android?

No injectable members. Do you want to add an injectable constructor?

I have these 2 simple modules modules:
AppModule
#Module(injects =
{
HomeActivity.class,
},
includes = {
GoogleApiModule.class,
DbModule.class,
GcmModule.class,
})
public class AppModule {
private final Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides
#Singleton
Application provideApplication() {
return app;
}
}
ApiModule
#Module(injects =
{
LoginFragment.class,
}, addsTo = AppModule.class)
public class ApiModule {
private final Context context;
public ApiModule(Context context) {
this.context = context;
}
#Provides
#Singleton
Publicapi providePublicApi() {
Publicapi.Builder builder = new
return new Publicapi();
}
}
Beacause of #Inject PublicApi in LoginFragment compiler complains:
No injectable members on myapp.publicapi.Publicapi. Do you want to add an injectable constructor? required by myapp.fragments.LoginFragment for myapp.modules.AppModule
I've been reading several threads I was sure I'm doing it right, been obviously wrong. What is wrong in this setup?
Publicapi must have a constructor method who receive the context, and you have to assign it an context class attribute. I mean:
public Publicapi(Context context) {
this.context = context;
}
Then... in ApiModule you have to instantiate the constructor sending the context as param. Something like that:
// (With "a" in lower case like the class name) !!!
#Provides
#Singleton
Publicapi providesPublicapi(Context context) {
return new Publicapi(context);
}
Regards!

No injectable members-error when using more than one dagger module in project

I would really like to implement Dagger in my app but I have a hard time to understand how it should be configured correctly.
I have two classes that I would like to inject.
public class LoginController {
#Inject Bus bus;
#Inject Context context;
// more code
}
public class LoginFragment {
#Inject Bus bus;
// more code
}
If I put all my code in one module it works fine:
#Module(injects = {LoginController.class, LoginFragment.class})
public class BasicModule {
#Provides
#Singleton
public Bus busProvider() {
return new Bus();
}
#Provides
public Context provideContext() {
return RblMobileApp.getContext();
}
}
But when I put those methods in two different modules (BasicModule and TestModule) I get compiler errors:
#Module(injects = {LoginController.class, LoginFragment.class})
public class BasicModule {
#Provides
#Singleton
public Bus busProvider() {
return new Bus();
}
}
Error:(18, 8) java: No injectable members on android.content.Context. Do you want to add an injectable constructor? required by rblmobile.controllers.LoginController for rblmobile.injection.BasicModule
#Module(injects = {LoginController.class})
public class TestModule {
#Provides
public Context provideContext() {
return RblMobileApp.getContext();
}
}
Error:(13, 8) java: No injectable members on com.squareup.otto.Bus. Do you want to add an injectable constructor? required by rblmobile.controllers.LoginController for rblmobile.injection.TestModule
So basically I'm told that BasicModule doesn't deliever Context and TestModule doesn't deliever Bus. Why does the ObjectGraph not know that the respective Constructor is in the other module?
Thanks
EDIT:
That's the method where the ObjectGraph gets created.
public static void init(final Object... modules) {
Log.i(TAG, "initializing object graph");
if (objectGraph == null) {
objectGraph = ObjectGraph.create(modules);
} else {
objectGraph = objectGraph.plus(modules);
}
}
There is a #Module annotation attribute called complete. Try to set it to false:
#Module(complete = false, injects = {LoginController.class, LoginFragment.class})
public class BasicModule {
#Provides
#Singleton
public Bus busProvider() {
return new Bus();
}
}
#Module(complete = false, injects = {LoginController.class})
public class TestModule {
#Provides
public Context provideContext() {
return RblMobileApp.getContext();
}
}

Categories

Resources