After successful implementing Dagger on project i have to specify dagger to each of class i want to use and inject modules, for example RestClient of retrofit, i want to know is any way to define automatically components into classes?
for example my implementation is:
public class CoreApplication extends MultiDexApplication {
private static ProjectApplicationComponent component;
private RestClient restClient;
private Picasso picasso;
private Handler handler;
#Override
public void onCreate() {
super.onCreate();
...
component = DaggerProjectApplicationComponent.builder()
.contextModule(new ContextModule(this))
.networkServiceModule(new NetworkServiceModule(ClientSettings.SERVER_URL))
.build();
restClient= component.apiService();
picasso = component.getPicasso();
handler = component.getHandler();
}
public static ProjectApplicationComponent getComponent() {
return component;
}
}
and my ApplicationComponent which i define witch class or activity or fragment i want to inject modules:
#ActivitiesScope
#Component(dependencies = ProjectApplicationComponent.class)
public interface ApplicationComponent {
void inject(PersonsRemoteRepository personsRemoteRepository);
}
and PersonsRemoteRepository class which i want to inject RestClient to use Retrofit
public class PersonsRemoteRepository implements PersonsRepository {
#Inject
private RestClient restClient;
private final ApplicationComponent component;
public PersonsRemoteRepository() {
component = DaggerApplicationComponent.builder()
.projectApplicationComponent(CoreApplication.getComponent())
.build();
component.inject(this);
}
...
}
my RestClient class is:
public interface RestClient {
#Headers("Content-Type: application/json")
#POST("/api/v1/getPersons")
Observable<List<Person>> getPersons();
}
my mean is removing component and component.inject(this); from all of classes that i want to inject RestClient
#Inject
private RestClient restClient;
for example simplified PersonsRemoteRepository class should be:
public class PersonsRemoteRepository implements PersonsRepository {
#Inject
private RestClient restClient;
public PersonsRemoteRepository() {
}
...
}
Thanks in advance
UPDATE POST
in this my activity inject(this) is not available on this line of code:
CoreApplication.getComponent().inject(this);
My activity:
public class LoginActivity extends AppCompatActivity{
#Inject
PersonsRemoteRepository personsRemoteRepository;
#Inject
RestClient restClient;
private LoginActivityBinding mBinding;
private LoginMethodsToPageViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CoreApplication.getComponent().inject(this);
mBinding = DataBindingUtil.setContentView(this, R.layout.login_activity);
personsRemoteRepository = new PersonsRemoteRepository(restClient);
viewModel = new LoginMethodsToPageViewModel(personsRemoteRepository, this, mBinding);
mBinding.setViewModel(viewModel);
}
...
}
in this screen shot as you see i dont have inject() method
PersonsRemoteRepository class after change:
public class PersonsRemoteRepository implements PersonsRepository {
private RestClient restClient;
#Inject
PersonsRemoteRepository(RestClient restClient) {
this.restClient = restClient;
}
#SuppressWarnings("unchecked")
#Override
public Observable<List<Person>> getAllPersons() {
Observable<List<Person>> observable = restClient.getPersons();
return observable
.flatMap((Function<List<Person>, Observable<List<Person>>>) Observable::fromArray);
}
}
There are two questions, how to inject into CoreApplication and how to inject into activities. And there are two corresponding components, ProjectApplicationComponent and ApplicationComponent, connected by component dependency.
To Inject into the application, the answer from Gustavo is useful:
Annotate fields of CoreApplication as #Inject,
Replace provision methods in ProjectApplicationComponent with a members-injection method:
#ApplicationScope
#Component(
modules = {
ContextModule.class,
NetworkServiceModule.class,
...,
})
public interface ProjectApplicationComponent {
// Members-injection method
void inject(CoreApplication coreApplication);
}
Construct a ProjectApplicationComponent and call the inject method:
// CoreApplication.onCreate
component =
DaggerProjectApplicationComponent.builder()
.contextModule(new ContextModule(this))
.networkServiceModule(...)
.build();
component.inject(/* coreApplication= */ this);
To inject into LoginActivity, the depending ApplicationComponent should have a members-injection method:
#ActivitiesScope
#Component(dependencies = ProjectApplicationComponent.class)
public interface ApplicationComponent {
void inject(LoginActivity loginActivity);
}
Recall that your LoginActivity has two #Injected fields, with types RestClient and PersonsRemoteRepository.
public class LoginActivity extends AppCompatActivity {
#Inject PersonsRemoteRepository personsRemoteRepository;
#Inject RestClient restClient;
}
In order for the depending ApplicationComponent to obtain a RestClient, the depended ProjectApplicationComponent should expose a provision method:
#ApplicationScope
#Component(modules = {...})
public interface ProjectApplicationComponent {
// Members-injection method
void inject(CoreApplication coreApplication);
// Provision method
RestClient getRestClient();
}
For PersonsRemoteRepository, Dagger can construct one using constructor injection:
// May be scoped #ActivitiesScope, or not
public class PersonsRemoteRepository implements PersonsRepository {
private final RestClient restClient;
#Inject
PersonsRemoteRepository(RestClient restClient) {
this.restClient = restClient;
}
}
Then when you create your LoginActivity, build the Dagger-generated component as follows:
// LoginActivity.onCreate
ApplicationComponent component =
DaggerApplicationComponent.builder()
.projectApplicationComponent(CoreApplication.getComponent())
.build();
component.inject(/* loginActivity= */ this);
You don't need to build your component for every class that you want to inject dependencies. The dependencies can be provided via constructor annotated with #Inject:
public class PersonsRemoteRepository implements PersonsRepository {
private RestClient restClient;
#Inject
public PersonsRemoteRepository(RestClient restClient) {
this.restClient = restClient;
}
}
And any other class that needs this repository can do the same:
public class AnyOtherClass {
private PersonsRemoteRepository personsRemoteRepository;
#Inject
public AnyOtherClass(PersonsRemoteRepository personsRemoteRepository) {
this.personsRemoteRepository = personsRemoteRepository;
}
You only need to use component.inject for classes that instances are created by Android, like Application, Activities and Fragments.
public class MyActivity {
#Inject PersonsRemoteRepository personsRemoteRepository;
#Override
public void onCreate() {
super.onCreate();
CoreApplication.getComponent().inject(this);
}
}
Changes needed in your CoreApplication:
public class CoreApplication extends MultiDexApplication {
private static ProjectApplicationComponent component;
#Inject private RestClient restClient;
#Inject private Picasso picasso;
#Inject private Handler handler;
#Override
public void onCreate() {
super.onCreate();
...
component = DaggerProjectApplicationComponent.builder()
.contextModule(new ContextModule(this))
.networkServiceModule(new NetworkServiceModule(ClientSettings.SERVER_URL))
.build();
component.inject(this);
}
}
Changes needed in your ApplicationComponent:
#ActivitiesScope
#Component(dependencies = ProjectApplicationComponent.class)
public interface ApplicationComponent {
void inject(CoreApplication coreApplication);
void inject(MyActivity myActivity);
}
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'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);
I have a class that's being injected using DI via dagger. However when the activity is destroyed and recreated, the model class seems to contain the data automatically without me insert/repopulating the data.
public class MyApplication extends Application {
private ExpressionFactoryComponent mExpressionFactoryComponent;
#Override
public void onCreate() {
super.onCreate();
// Building dagger DI component
mExpressionFactoryComponent = DaggerExpressionFactoryComponent.builder().
expressionFactoryModule(new ExpressionFactoryModule(new ExpressionFactory(this))).build();
}
}
Module:
#Module
public class ExpressionFactoryModule {
private ExpressionFactory mExpressionFactory;
public ExpressionFactoryModule(ExpressionFactory expressionFactory) {
this.mExpressionFactory = expressionFactory;
}
#Provides
ExpressionFactory provideStringExpressionFactory() {
return mExpressionFactory;
}
}
The one reason for this is that ExpressionFactory is instantiated in MyApplication class and then passed into the constructor of ExpressionFactoryModule while creating ExpressionFactoryComponent. You should pass an Application instance in your module constructor and then create an instance of your class withing a metod with #Provide annotation passing that Application instance in your class's constructor.
This how things should be done with dagger. But to solve your problem you need to create another component class and build the component in an activity if you need to have an instance of your class living withing an activity only.
Here is the solution (ExpressionFactoryComponent is renamed to AppComponent here):
public class MyApplication extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
// Building dagger DI component
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this)).build();
}
public static AppComponent getAppComponent() {
return appComponent;
}
}
#Component(modules = {AppModule.class})
public interface AppComponent {
Application getApplication();
void inject(App application);
}
#Module
public class AppModule {
private Application application;
public AppModule(Application application) {
this.application = application;
}
#Provides
Application provideApplication() {
return application;
}
}
#Component(dependencies = AppComponent.class, modules = {
ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity activity);
}
#Module
public class ActivityModule {
private Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
#Provides
Activity provideActivity() {
return activity;
}
#Provides
ExpressionFactory provideStringExpressionFactory(Application application) {
return new ExpressionFactory(application);
}
}
public class MainActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityComponent activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(activity))
.appComponent(App.getAppComponent())
.build();
activityComponent.inject(this);
}
}
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'm using Dagger for Android for dependency injections.
I have a UserService object in a Main Class:
public class Main implements Runnable {
#Inject
UserService service;
#Override
public void run() {
for (User f : service.getUserByName("toto")) {
System.out.print(f.getM_Nom());
}
}
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new UserModule());
Main m = objectGraph.get(Main.class);
m.run();
}
}
I managed to inject the "service" field and to call the method "getUserByName("")".
But in my "UserService", I have an other custom object ("RepositoryUser" class):
public class UserService implements IUserService {
#Inject
RepositoryUser m_Repository;
#Override
public List<User> getUserByName(String name) {
return m_Repository.getAll();
}
}
My problem is that this field is not inject: the "m_Repository" field is null and I get a null pointer exception when I try to use my RepositoryUser object.
Here is my Provider:
#Module(
injects = {UserService.class, Main.class, RepositoryUser.class}
)
public class UserModule {
#Provides
RepositoryUser provideRepositoryUser() {
return new RepositoryUser();
}
#Provides
UserService provideUserService() {
return new UserService();
}
}
Any idea ?
Thanks in advance !
It is preferrable to use Constructor Injection in this case. This can be achieved as follows:
Main:
public class Main implements Runnable {
private final IUserService service;
#Inject
public Main(IUserService service) {
this.service = service;
}
#Override
public void run() {
for (User f : service.getUserByName("toto")) {
System.out.print(f.getM_Nom());
}
}
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new UserModule());
Main m = objectGraph.get(Main.class);
m.run();
}
}
UserService:
public class UserService implements IUserService {
private final RepositoryUser m_Repository;
#Inject
public UserService(RepositoryUser repository) {
m_Repository = repository;
}
#Override
public List<User> getUserByName(String name) {
return m_Repository.getAll();
}
}
RepositoryUser:
public class RepositoryUser {
#Inject
public RepositoryUser() {
}
/* ... */
}
UserModule:
#Module(injects = Main.class)
public class UserModule {
#Provides
IUserService provideIUserService(UserService userService){
return userService;
}
}
Everywhere the #Inject annotation is present on a constructor, Dagger can automatically create an instance of that item. So when you request a RepositoryUser instance in the UserService constructor, Dagger will see the #Inject annotation on RepositoryUser's constructor, and use that to create a new instance. We do not need an #Provides method here.
The IUserService parameter on the Main constructor cannot be instantiated, since it is an interface. Using the provideIUserService method in the module, we tell Dagger that we want it to create a new UserService instance.
We do have an #Inject annotation on the Main constructor, but we request it using ObjectGraph.get(Class<T> clzz). Therefore, we need to add injects = Main.class to our module.