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 {...
Related
In my application , I am trying to create Dagger 2 components for
Context (To use in different classes) Which is already done
AppUtil (requires context for network check method) Need to get this done!
I have created components and initiated from Application class.
public class MyApp extends Application{
private ContextComponent mContextComponent;
private AppUtilComponent mAppUtilComponent;
private NetworkComponent mNetworkComponent;
#Override
public void onCreate() {
super.onCreate();
mNetworkComponent = createNetworkComponent();
mContextComponent = createContextComponent();
mAppUtilComponent = createAppUtilComponent();
}
private AppUtilComponent createAppUtilComponent() {
return DaggerAppUtilComponent.builder().appUtilModule(new AppUtilModule(this)).build();
}
public AppUtilComponent getAppUtilComponent() {
return mAppUtilComponent;
}
private NetworkComponent createNetworkComponent() {
return DaggerNetworkComponent.builder().networkModule(new NetworkModule()).build();
}
public NetworkComponent getNetworkComponent() {
return mNetworkComponent;
}
private ContextComponent createContextComponent() {
return DaggerContextComponent.builder().contextModule(new ContextModule(this)).build();
}
public ContextComponent getContextComponent(){
return mContextComponent;
}
}
The ContextModule class is as follows
#Module
public class ContextModule {
private Context mContext;
public ContextModule(Context context){
mContext = context;
}
#Provides
#Singleton
Context getContext(){
return mContext;
}
}
Context component will be
#Singleton
#Component (modules = ContextModule.class)
public interface ContextComponent {
void inject(AppUtils appUtils);
}
AppUtilModule is like
#Singleton
#Component (modules = AppUtilModule.class)
public interface AppUtilComponent {
void inject(SplashActivity splashActivity);
}
With this AppUtilModule have modified as
#Module (includes = ContextModule.class)
public class AppUtilModule {
private AppUtils mAppUtils;
private Context context;
#Inject
public AppUtilModule(Context context) {
this.context = context;
}
#Provides
public AppUtils getAppUtil(){
mAppUtils = new AppUtils(context);
return mAppUtils;
}
}
Now my AppUtils is getting context injected , which is perfectly alright.
public class AppUtils {
public Context mContext;
#Inject
public AppUtils(Context _context){
mContext = _context;
}
public boolean isNetworkConnected(){
//Using mContext to determine Network
}
... Other Utility methods which will be used throughout my application
}
But now how can I make AppUtil as a separate Dagger component (which
is internally having Context as a Dependency) and inject in other classes?
EDIT 1: After making constructor injection of context into AppUtils and using AppUtil component in SplashActivity which already had Network component
After making the AppUtil as Dagger dependency, now Project is giving
compile time error. Before this changes, NetworkProcessor in SplashActivity used to work
fine, as it was only the dependency SplashActivity had. What is that I
am missing/ doing wrong!
public class SplashActivity ....
{
#Inject
public NetworkProcessor mNetworkProcessor;
.....
#Inject
public AppUtils mAppUtils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApp.getInstance().getAppUtilComponent().inject(this);
MyApp.getInstance().getNetworkComponent().inject(this);
}
}
Error:
rror: [Dagger/MissingBinding] com.dev.myapp.networking.NetworkProcessor cannot be provided without an #Inject constructor or an #Provides-annotated method.
com.dev.myapp.networking.NetworkProcessor is injected at
com.dev.myapp.ui.activities.landing.SplashActivity.mNetworkProcessor
com.dev.myapp.ui.activities.landing.SplashActivity is injected at
com.dev.myapp.components.AppUtilComponent.inject(com.dev.myapp.ui.activities.landing.SplashActivity)
So a component can create multiple dependencies at once, which are usually grouped by lifetime scope (Application = whole app lifetime, Activity = activity lifetime).
So if your AppUtils class has the same scope as your ContextComponent, you can just use it to inject the AppUtils into classes that should use it, e.g. an activity:
public class MainActivity extens Activity {
#Inject AppUtils appUtils;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApp) getApplication()).getContextComponent().inject(this);
}
}
after extending the ContextComponent definition to
#Singleton
#Component (modules = ContextModule.class)
public interface ContextComponent {
void inject(AppUtils appUtils);
void inject(MainActivity activity);
}
and you need to change AppUtils to use conastructor injection:
public class AppUtils {
private Context mContext;
#Inject
public AppUtils(Context context){
this.mContext = context;
}
}
After Question Edit 1
Dagger does not know how to create the NetworkProcessor-class, hence the compiler error. To make it work, you should change NetworkProcessor to have a constructor that is annotated with #Inject like you did with AppUtils (the second option would be creating a #Provides method in a dagger module, but the first solution is easier).
You did not supply the source code of NetworkProcessor so I'm assuming here it only needs a Context as a dependency.
Now that both AppUtils and NetworkProcessor have an injectible constructor that only has Context as an argument, Dagger can create the missing links by itself.
You don't need the NetworkComponent and the AppUtilComponent, just one Component, so delete them. Also delete the NetworkModule and the AppUtilModule. ContextComponent now is sufficient to inject all the dependencies into SplashActivity:
public class SplashActivity .... {
#Inject public NetworkProcessor mNetworkProcessor;
.....
#Inject public AppUtils mAppUtils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApp.getInstance().getContextComponent().inject(this);
}
//...
}
This works because Dagger can create the wiring code itself, since it knows how to instantiate both AppUtils and NetworkProcessor.
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?
I'm trying to refactor my app using MVP pattern and Dagger 2 for Dependency Injection.
I Create module that provides Application Context and I want get Context to get SharedPreferences on Model Layer.
I inject Context on Presenter layer and it's working with SharedPreference, but when I move to Model layer, Dagger inject a null value on Context variable.
Inject
#Inject
public Context mContext;
App Module
AppModule provides Application Context
#Module
public class AppModule {
private App app;
public AppModule(App app){
this.app = app;
}
#Provides
public Context providesApp(){
return app.getApplicationContext();
}
}
Application
public class App extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.mainModule(new MainModule())
.build();
}
public AppComponent getAppComponent(){
return appComponent;
}
}
App Component
#Component(modules = {AppModule.class,MainModule.class})
public interface AppComponent {
void inject(MainActivity activity);
void inject(LoginActivity activity);
}
Main Module
#Module
public class MainModule {
#Provides
public MainContract.Model providesMainModel(){
return new MainModel();
}
#Provides
public LoginContract.Model providesLoginModel(){
return new LoginModel();
}
}
Since you have the Context in your App Module, you can add the Context as parameter to LoginModel constructor.
#Provides
public LoginContract.Model providesLoginModel(Context contect){
return new LoginModel(contect);
}
#Provides
public LoginContract.Presenter providesLoginPresenter(LoginContract.Model model){
return new LoginPresenter(model);
}
But bear in mind that you must use android packages only on your Activities and Fragments for easy testing. So your approach is wrong.
EDIT
Well for me, the best option is to create a class for SharedPreferences like this
public class Preferences {
private final SharedPreferences preferences;
private String key;
private String defaultValue;
public StringPreferences(#NonNull SharedPreferences preferences) {
this.preferences = preferences;
}
#Nullable
public String get(String mykey) {
return preferences.getString(mykey, defaultValue);
}
public void set(#NonNull String mykey, #Nullable String value) {
preferences.edit().putString(mykey, value).apply();
}
// same way for integers, longs, doubles and booleans
public boolean isSet() {
return preferences.contains(key);
}
public void delete() {
preferences.edit().remove(key).apply();
}
}
Then in App Module I Provide the SharedPreferences
#Provides
#Singleton
public SharedPreferences provideSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
#Provides
#Singleton
public Preferences providePreferences(SharedPreferences prefs) {
return new Preferences(prefs);
}
Finally in Main Module
#Provides
public LoginContract.Model providesLoginModel(Preferences prefs){
return new LoginModel(prefs);
}
#Provides
public LoginContract.Presenter providesLoginPresenter(LoginContract.Model model){
return new LoginPresenter(model);
}
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.
I want to use dagger (dagger v1 by Square) to create a singleton class whose constructor requires context as an argument. I then want to inject this singleton class into my MainActivity. What are the proper steps to define this?
I tried doing this:
SingletonClass:
#Module(
injects = {MainActivity.class}
)
#Singleton
public class SingletonClass {
...
#Inject
SingletonClass(Context ctx) {}
}
to which I receive the error:
no injectable members on android.content.Context
I understand that SingletonClass is supposed to receive it's context from somewhere in order to be injectable, but since I'm not "calling" it anymore in the traditional sense, but rather I'm injecting it like so in the MainActivity at the class-level:
#Inject SingletonClass singletonClass;
how am I supposed to pass it the context?
Here are additional files I created for dagger (two of which I saw used in official examples):
AndroidModule:
#Module(library = true, injects = MainActivity.class)
public class AndroidModule {
private final Context context;
public AndroidModule(Context context) {
this.context = context;
}
/**
* Allow the application context to be injected but require that it be
* annotated with to explicitly
* differentiate it from an activity context.
*/
#Provides
#Singleton
#ForApplication
Context provideApplicationContext() {
return context;
}
}
App.class (to extend my application):
public class App extends Application {
private static final String TAG = App.class.getSimpleName();
private static App instance;
public MainActivity mainActivity;
private static Context context;
private ObjectGraph objectGraph;
public void onCreate() {
super.onCreate();
instance = this;
context = getApplicationContext();
objectGraph = ObjectGraph.create(getModules().toArray());
}
public static App getInstance() {
return instance;
}
public static Context getContext() { return context; }
protected List<Object> getModules() {
return Arrays.asList(new AndroidModule(this), new App());
}
public void inject(Object object) {
objectGraph.inject(object);
}
}
ForApplication class:
import java.lang.annotation.Retention;
import javax.inject.Qualifier;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Qualifier #Retention(RUNTIME)
public #interface ForApplication {
}
You need to add the #ForApplication qualifier to your Context parameter in the SingletonClass constructor.
Dagger is now looking for a Context to inject, but only has a #ForApplication Context, which is a mismatch.
#Singleton
public class SingletonClass {
#Inject
SingletonClass(#ForApplication Context ctx) {
}
}
Now you can also get rid of the library = true line in your AndroidModule, which you've probably added because Dagger warned you that #ForApplication Context was unused (Don't ignore these warnings!).
Also, and that might just be a copy-paste error, this SingletonClass should not have an #Module annotation.