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

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!

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 dependecies reference to applicationComponent is not providing object to inject

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
}

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?

Android Using single context for multiple Dagger2 modules

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.

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