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.
Related
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 {...
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 am attempting to add Dagger 2 to my Android Project. I think I understand the concepts up to the point of where I build the graph. At that point I'm shooting in the dark and that is where I'm going wrong.
Everything compiles, but the injected field is null at Runtime.
I am attempting to start simply by injecting the Presenter into my MainActivity. I have written the following code and would appreciate some help figuring out where I have gone wrong.
My PresenterModule.java:
#Module
public class PresenterModule {
#Provides MainActivityPresenter providesMainActivityPresenter() {
return new DefaultMainActivityPresenter();
}
}
My Application class which also includes my Component following the Dagger2 example code:
public class App extends Application {
private PresenterComponent component;
#Singleton
#Component(modules = PresenterModule.class)
public interface PresenterComponent {
void inject(App app);
void inject(MainActivity activity);
}
#Override public void onCreate() {
Log.d("App.java", "Starting Application");
super.onCreate();
component = DaggerApp_PresenterComponent.builder()
.presenterModule(new PresenterModule())
.build();
component.inject(this);
}
public PresenterComponent component() {
return component;
}
}
And finally my MainActivity.
public class DefaultMainActivity
extends ActionBarActivity
implements MainActivity
{
#Inject MainActivityPresenter mPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((App)getApplication()).component().inject(this);
mPresenter.getCurrentDetailLineNumber();
setContentView(R.layout.base_layout);
getSupportActionBar();
mContainer = (Container) findViewById(R.id.container);
mPresenter.requestCurrentScreen();
}
The actual object to be injected is an implementation of an interface but is other wise a POJO object:
public class DefaultMainActivityPresenter implements MainActivityPresenter {
private static final int SCREEN_BROWSER = 0;
private static final int SCREEN_DETAIL = 1;
LineNumber mCurrentDetailLineNumber;
int mCurrentScreen;
#Inject
public DefaultMainActivityPresenter() {
}
...
}
Changing PresenterComponent to following will fix your problem:
#Singleton
#Component(modules = PresenterModule.class)
public interface PresenterComponent {
void inject(App app);
void inject(DefaultMainActivity activity);
}
This is due to covariance:
While a members-injection method for a type will accept instances of its subtypes, only Inject-annotated members of the parameter type and its supertypes will be injected; members of subtypes will not. For example, given the following types, only a and b will be injected into an instance of Child when it is passed to the members-injection method injectSelf(Self instance):
class Parent {
#Inject A a;
}
class Self extends Parent {
#Inject B b;
}
class Child extends Self {
#Inject C c;
}
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.
Im trying to add some dependency injection in my android project, so following example what i found i created my module
#Module(entryPoints = {MyActivity.class})
public class MyModule {
private final Context context;
public MyModule (Context context) {
this.context = context.getApplicationContext();
}
#Provides
#Singleton
Context applicationContext() {
return context;
}
#Provides
#Singleton
EventBus provideEventBus() {
return EventBusFactory.create();
}
}
i have my application:
public class MyApplication extends Application{
private ObjectGraph objectGraph;
#Override
public void onCreate() {
super.onCreate();
objectGraph = ObjectGraph.get(new MyModule(this));
}
public ObjectGraph getObjectGraph() {
return objectGraph;
}
}
and activity
public class MyActivity extends Activity {
#App
MyApplication application;
#Inject
EventBus eventBus;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ObjectGraph objectGraph = application.getObjectGraph();
objectGraph.inject(this);
....
i'm not sure what i'm doing wrong or what i missed, but my eclipse shows me error:
The method get(Class) in the type ObjectGraph is not applicable for
the arguments (MyModuleModule)
in this line objectGraph = ObjectGraph.get(new MyModule(this));
and when i'm trying to do build i get:
No binding for my.android.lib.EventBus required by
my.android.app.MyActivity for my.android.app.MyModule
is anyone can tell me what i'm doing wrong and what i forgot about?
The instance of ObjectGraph class can be obtained by invoking the create() static method.
Try
objectGraph = ObjectGraph.create(new MyModule(this));
For the binding error, you have to add the EventBus library into your build classpath.