1 - Scope:
#Scope
#Retention(RetentionPolicy.CLASS)
public #interface PerInstance {}
2 - AppContextModule:
#Module
public class AppContextModule {
Application application;
public AppContextModule(Application application){
this.application = application;
}
#Provides
public Application application(){
return this.application;
}
#Provides
public Context context(){
return this.application;
}
#Provides
public LocationManager locationManager(Context context){
return (LocationManager) context.getSystemService(context.LOCATION_SERVICE);
}
}
3 - MeuPrimeiroModule:
#Module
public class MeuPrimeiroModule {
#Provides
#PerInstance
public String nome() {
return new String("Gorick");
}
}
4 - MeuSegundoModule:
#Module(includes = MeuPrimeiroModule.class)
public class MeuSegundoModule {
#Provides
#Singleton
public String nomeCompleto(MeuPrimeiroModule meuPrimeiroModule) {
return new String(meuPrimeiroModule + " Silva");
}
}
5 - MeuPrimeiroComponent:
#PerInstance
#Component(modules={MeuPrimeiroModule.class})
public interface MeuPrimeiroComponent {
void inject(MainActivity mainActivity);
}
6 - MeuSegundoComponent:
#Singleton
#Component(modules={MeuSegundoComponent.class})
public interface MeuSegundoComponent extends MeuPrimeiroComponent {
void inject(MainActivity mainActivity);
}
7 - AppContextComponent:
public interface AppContextComponent {
Application app(); //provision method
Context applicationContext(); //provision method
LocationManager locationManager(); //provision method
}
8 - ApplicationComponent:
#Singleton
#Component(modules={AppContextModule.class})
public interface ApplicationComponent extends AppContextComponent {
void inject(MainActivity mainActivity);
}
9 - MainActivity:
public class MainActivity extends AppCompatActivity {
#Inject
MeuPrimeiroComponent meuPrimeiroComponent;
#Inject
MeuSegundoComponent meuSegundoComponent;
TextView nome, nomeCompleto;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
nome = (TextView) findViewById(R.id.nome);
nomeCompleto = (TextView) findViewById(R.id.nomeCompleto);
setNome(nome, nomeCompleto);
}
public void setNome(TextView nome, TextView nomeCompleto){
nome.setText(meuPrimeiroComponent.toString());
nomeCompleto.setText(meuSegundoComponent.toString());
}
}
Build:
Error:(16, 10) error: gorick.dagger2.Dagger2.Component.MeuPrimeiroComponent cannot be provided without an #Provides- or #Produces-annotated method.
gorick.dagger2.Dagger2.Component.MeuPrimeiroComponent is injected at
gorick.dagger2.MainActivity.meuPrimeiroComponent
gorick.dagger2.MainActivity is injected at
gorick.dagger2.Dagger2.Component.ApplicationComponent.inject(mainActivity)
PS: If i use meuPrimeiroComponent.nome(), android studio doesn't find the nome() method.
You cannot inject values in component instead you need to inject in object whose constructor contains values which is returns by the providers
For more info Ref this Example
You need build Component like this on onCreate():
DaggerMeuPrimeiroComponent.builder()
// list of modules that are part of this component need to be created here too
.appContextModule(new AppContextModule(getApplicationContext())) // This also corresponds to the name of your module: %component_name%Module
.build().inject(this);
Related
I'm trying to learn dagger 2 but I'm confused in injecting of constructor with interface. This is my below code :
MainActivity.java
public class MainActivity extends AppCompatActivity implements MainView {
// this keyword of request dependency . At compiling process, dagger will look at all of these annotations
//to create the exact dependency
#Inject MainPresenter mainPresenter ;
TextView textView ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textview) ;
DaggerPresenterComponent.create().inject(this);
textView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
mainPresenter.doThings(8555) ;
}
});
}
/**********************************/
#Override
public void invokeRandomViewMethod(String msg) {
textView.setText(msg);
}
}
MainPresenter.java
public class MainPresenter {
private MainView mainView ;
#Inject
public MainPresenter(MainView mainView) {
this.mainView = mainView;
}
public void doThings(int value){
Random random = new Random();
int rand= random.nextInt(value) ;
if(mainView != null){
mainView.invokeRandomViewMethod("You random number is "+rand);
}
}
public interface MainView {
void invokeRandomViewMethod(String msg) ;
}
}
This is the Module :
#Module
public class PresenterModule {
#Provides
// this is the method that will provide the dependancy
MainPresenter provideMainPresenter(MainView mainView){
return new MainPresenter(mainView);
}
}
And this is the Component
#Component (modules = PresenterModule.class)
public interface PresenterComponent {
void inject(MainActivity activity) ;
}
When I run the code it shows me this error :
Error:(15, 10) error: com.imennmn.hellodagger2example.MainView cannot
be provided without an #Provides-annotated method.
com.imennmn.hellodagger2example.MainView is injected at
com.imennmn.hellodagger2example.presenterInjection.PresenterModule.provideMainPresenter(mainView)
com.imennmn.hellodagger2example.MainPresenter is injected at
com.imennmn.hellodagger2example.MainActivity.mainPresenter
com.imennmn.hellodagger2example.MainActivity is injected at
com.imennmn.hellodagger2example.simpleInjection.DataComponent.inject(activity)
My Question is how I can provide the interface MainView by inject it with dagger and bind the MainPresenter and MainActivity ?
Any help would be appreciated !
By following code:
MainPresenter provideMainPresenter(MainView mainView) {
return new MainPresenter(mainView);
}
You are telling dagger: "hey, whenever I ask you to inject MainPresenter, construct it using MainView".
But dagger complaints, because you haven't specified how exactly he should build/acquire MainView.
So, in your PresenterModule do this:
#Module
public class PresenterModule {
MainView mainView;
public PresenterModule(MainView mainView) {
this.mainView = mainView;
}
#Provides
MainPresenter provideMainPresenter() {
return new MainPresenter(mainView);
}
}
Then when building the component:
DaggerPresenterComponent.builder()
.presenterModule(new PresenterModule(this))
.build();
Your provideMainPresenter implicitly depends on a MainView. Dagger has no way to get it. You need to add a method to provide it:
#Module
public class PresenterModule {
#Provides
MainView provideMainView(){
// Provide MainView here somehow so Dagger can use this to create a MainPresenter
}
#Provides
// this is the method that will provide the dependancy
MainPresenter provideMainPresenter(MainView mainView){
return new MainPresenter(mainView);
}
}
Add abstract module with #Binds annotation, look at my impl of : AbstractTestSettingsFragmentModule.java
TestFragment.java
public class TestFragment extends Fragment{
#Inject TestFragmentContract.Presenter mPresenter;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidSupportInjection.inject(this);
}
}
TestFragmentPresenterImpl.java
public class TestFragmentPresenterImpl implements TestFragmentContract.Presenter {
#Inject
public TestFragmentPresenterImpl(){
}
}
AbstractTestSettingsFragmentModule.java
#Module
public abstract class AbstractTestSettingsFragmentModule {
#Binds
#NonNull
public abstract TestSettingsFragmentContract.Presenter testSettingsFragmentPresenterImpl(TestSettingsFragmentImpl presenter);
}
ContributesModule.java
#Module
public abstract class ContributesModule {
#ContributesAndroidInjector(modules = {AbstractTestSettingsFragmentModule.class})
abstract TestSettingsFragment testSettingsFragment();
}
AppComponent.java
#Singleton
#Component(
modules = {
AndroidSupportInjectionModule.class,
ContributesModule.class,
AppModule.class,
})
public interface AppComponent extends AndroidInjector<DaggerApplication> {
void inject(TheApplication theApplication);
#Override
void inject(DaggerApplication instance);
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
You can use Assisted Injection with Dagger: https://dagger.dev/dev-guide/assisted-injection.html
Assisted injection is a dependency injection (DI) pattern that is used to construct an object where some parameters may be provided by the DI framework and others must be passed in at creation time (a.k.a “assisted”) by the user.
This question already has an answer here:
How do I fix Dagger 2 error '... cannot be provided [...]'?
(1 answer)
Closed 5 years ago.
I am using MVP Architecture along with Dagger2. I want to get Context in my Presenter using constructor injection.
My Activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_appintroduction);
activityComponent().inject(this);
ButterKnife.bind(this);
skipall.bringToFront();
}
My Presenter:
#ConfigPersistent
public class AppIntroPresenter extends BasePresenter<AppIntroMvpView> {
private final DataManager mDataManager;
private final Context ctx1;
#Inject
public AppIntroPresenter(DataManager dataManager,Context context) {
mDataManager = dataManager;
ctx1=context;
}
public void openCamView(Context ctx) {
ctx.startActivity(new Intent(ctx, CameraActivity.class));
}
}
My Application Component :
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(SyncService syncService);
#ApplicationContext Context context();
Application application();
RibotsService ribotsService();
PreferencesHelper preferencesHelper();
DatabaseHelper databaseHelper();
DataManager dataManager();
RxEventBus eventBus();
ClassService classService();
AnsweredQuestionService answeredQuestionService();
}
My Activity Component:
#PerActivity
#Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
void inject(CameraActivity cameraActivity);
void inject(AppIntroActivity appIntroActivity);
}
My Application Module:
#Module
public class ApplicationModule {
protected final Application mApplication;
public ApplicationModule(Application application) {
mApplication = application;
}
#Provides
Application provideApplication() {
return mApplication;
}
#Provides
#ApplicationContext
Context provideContext() {
return mApplication;
}
#Provides
#Singleton
RibotsService provideRibotsService() {
return RibotsService.Creator.newRibotsService();
}
#Provides
#Singleton
ClassService provideClassService() {
return ClassService.Creator.newClassService();
}
#Provides
#Singleton
AnsweredQuestionService provideAnsweredQuestionService() {
return AnsweredQuestionService.Creator.newAnsweredQuestionService();
}
}
My Activity Module:
#Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
mActivity = activity;
}
#Provides
Activity provideActivity() {
return mActivity;
}
}
After running the code, i get error:
Error:(17, 8) error: [uk.co.ribot.androidboilerplate.injection.component.ActivityComponent.inject(uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroActivity)] android.content.Context cannot be provided without an #Provides-annotated method.
android.content.Context is injected at
uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroPresenter.<init>(…, context)
uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroPresenter is injected at
uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroActivity.mAppIntroPresenter
uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroActivity is injected at
uk.co.ribot.androidboilerplate.injection.component.ActivityComponent.inject(appIntroActivity)
I know when i add context to constructor injection in My Presenter i get this error, but i am not able to figure out why? If i remove the context from constructor :
#Inject
public AppIntroPresenter(DataManager dataManager,Context context) {
mDataManager = dataManager;
ctx1=context;
}
Then everything works fine. But i need the context from injection. Please help me with this?
Since your constructor has members, you need to create a module and create there an instance annotated with #Provides and #Singleton, same as ApplicationModule.
Why does the below code fail to compile with the following error, and what should be done to fix it?
Error:(9, 8) error: [SubComponent.inject(MainActivity)] java.lang.Integer cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
java.lang.Integer is injected at
MainActivity.abc
MainActivity is injected at
SubComponent.inject(activity)
TL;DR: I am trying to create a subcomponent with a different scope than the parent component, and inject a dependency from the subcomponent into an activity.
App.java
public class App extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.create();
appComponent.inject(this);
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
}
public AppComponent getAppComponent() {
return appComponent;
}
public static App app(Context context) {
return (App) context.getApplicationContext();
}
}
AppComponent.java
#Singleton
#Component
public interface AppComponent {
void inject(App app);
SubComponent.Builder subComponent();
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Inject
int abc;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
App.app(this).getAppComponent()
.subComponent()
.userModule(new SubModule())
.build()
.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
}
}
SubComponent.java
#SubScope
#Subcomponent(modules = {SubModule.class})
public interface SubComponent {
void inject(MainActivity activity);
#Subcomponent.Builder
interface Builder {
Builder userModule(SubModule module);
SubComponent build();
}
}
SubModule.java
#Module
public class SubModule {
#Provides
#SubScope
public int provideAbc() {
return 1;
}
}
SubScope.java
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface SubScope {
}
To make it work, I just had to change #Qualifier to #Scope in SubScope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface SubScope {
}
First time using Dagger2.
In my android application I have a MyApplication class that extends Application.
I also have an ImageAssistant class that is a collection of related image-processing methods.
In my MyApplicaiton class I used to instantiate an ImageAssistant for all the activities to use.
Now I am trying to make it work with Dagger2, but I dont know how to pass a context in the module that provides ImageAssistant
This is how my code looked:
public class ImageAssistant {
Context context;
public ImageAssistant(Context context){
this.context = context;
}
// A bunch of methods...
}
public class MyApplication extends Application {
public ImageAssistant imageAssistant;
public void onCreate() {
imageAssistant = new ImageAssistant(this);
}
}
Now, enter Dagger 2, here is what I have
public class ImageAssistant {
Context context;
#Inject
public ImageAssistant(Context context){
this.context = context;
}
// A bunch of methods...
}
public class MyApplication extends Application {
#Inject
public ImageAssistant imageAssistant;
public void onCreate() {
}
}
in package .modules:
AppModule.java
#Module
public class AppModule {
#Provides
ImageAssistant provideImageAssistant() {
return new ImageAssistant(); // HERE A CONTEXT IS NEEDED. WHERE TO GET IT FROM?
}
}
EDIT: This is how my module looks now, but I still dont know how to tie everything together:
#Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
#Provides
Context provideApplicationContext() {
return this.application;
}
#Provides
ImageAssistant provideImageAssistant(ImageAssistant imageAssistant) {
return imageAssistant;
}
}
And this is the AppComponent:
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
ImageAssistant provideImageAssistant();
Context context();
}
Your module should look like this:
#Module(injects = {MainActivity.class})
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
#Provides
public ImageAssistant provideImageAssistant() {
return new ImageAssistantImpl(application); // implementation of ImageAssistant
}
}
Usage in Activity:
public class MainActivity extends Activity{
#Inject
ImageAssistant imageAssistant;
#Override
protected void onCreate(Bundle savedInstanceState) {
SharedObjectGraph.inject(this);
imageAssistant.doSomething();
}
}
Don't forget to init the ObjectGraph in Application class. I use SharedObjectGraph static class to share ObjectGraph created from AppModule across the whole app.
Read building the graph section.
I did something like this:
#Module
public class ApplicationModule {
private final SpendApplication app;
public ApplicationModule(SpendApplication app) {
this.app = app;
}
#Provides
#Singleton
public Context providesContext() {
return app;
}
}
and:
#Module
public class GattManagerModule {
#Provides
#Singleton
GattManager providesGattManager(Context context) {
return new GattManager(context);
}
}
And declare all places where it's used:
#Component(modules = { ApplicationModule.class, GattManagerModule.class, ...})
#Singleton
public interface ApplicationComponent {
void inject(MainScreenActivity activity);
...
}
In my app application class:
#Override
public void onCreate() {
super.onCreate();
component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
And in my MainActivity:
#Inject
GattManager gattManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getAppComponent().inject(this);
}
I am trying to inject my android context from one module into another. This is my code so far:
UserProfileModule.java
#Module(
library = true
)
public class UserProfileModule {
#Inject Context _context;
#Provides
public AccountUtils.UserProfile provideUserProfile() {
return AccountUtils.getUserProfile(_context);
}
}
RootModule.java
#Module(
injects = {
PizzaApplication.class,
UserProfileModule.class,
MainActivity.class
},
includes = {
UserProfileModule.class
},
library = true
)
public class RootModule {
private final Context _context;
public RootModule(Context context) {
_context = context;
}
#Provides
#Singleton
public Context provideApplicationContext() {
return _context;
}
}
Anytime it tries to get the user profile it fails saying the object is null.]
EDIT:
PizzaApplication.java
public class PizzaApplication extends Application {
private ObjectGraph objectGraph;
#Override
public void onCreate() {
super.onCreate();
injectDependencies();
}
private void injectDependencies() {
objectGraph = ObjectGraph.create(new RootModule(this));
objectGraph.inject(this);
}
public void inject(Object object) {
objectGraph.inject(object);
}
}
MainActivity.java
public class MainActivity extends BaseActivity {
#InjectView(R.id.toolbar) public Toolbar _toolbar;
#InjectView(R.id.drawer) public DrawerFrameLayout _drawer;
#Inject public AccountUtils.UserProfile _profile;
#Inject public Context _context;
// private NavigationDrawerFragment navigationDrawerFragment;
#Override
protected void onCreate(Bundle saveInstanceState) {
tl;dr (Short version):
For a minimal working example of your code, see my code on GitHub
You don't need to / should't inject the Context from one module into another.
The order/direction of your module includes is reversed, the UserProfileModule should include the RootModule.
More details and comments on your code:
You don't need to inject something into a module, a module only provides dependencies. In your case simply making use of the module includes gives the functionality you want.
Remove the library = true from UserProfileModule, because you only need this when the module's providers aren't all used directly by the classes specified in the injects list.
As Niek Haarman said, you need to pass both RootModule and UserProfileModule instances to ObjectGraph.create in your PizzaApplication's onCreate.
You're doing inject(this) in PizzaApplication but it's got no dependencies, so the inject isn't necessary. Based on the sample code you've provided, this makes me think you're assuming that injecting on the Application-level will also inject Activity dependencies...? You need to do the inject on your Activity too.
You don't show if you're doing an inject in your Activity's onCreate -- that's most likely what's missing.
You're injecting Context into the Activity but that's not necessary since you can just use getApplicationContext() in the Activity.
Here's the working code:
RootModule:
#Module(
injects = {MainActivity.class},
library = true,
complete = false
)
public class RootModule {
private final Context _context;
public RootModule(Context context) {
_context = context;
}
#Provides
#Singleton
public Context provideApplicationContext() {
return _context;
}
}
UserProfileModule:
#Module(includes = {RootModule.class})
public class UserProfileModule {
#Provides
public AccountUtils.UserProfile provideUserProfile(Context context) {
return AccountUtils.getUserProfile(context);
}
}
MainActivity:
public class MainActivity extends BaseActivity {
#Inject
public AccountUtils.UserProfile _profile;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((PizzaApplication) getApplication()).inject(this);
_profile.message();
}
PizzaApplication:
public class PizzaApplication extends Application {
private ObjectGraph objectGraph;
#Override
public void onCreate() {
super.onCreate();
objectGraph = ObjectGraph.create(new RootModule(this), new UserProfileModule());
}
public void inject(Object object) {
objectGraph.inject(object);
}
}
You have
objectGraph = ObjectGraph.create(new RootModule(this));
But, includes = { UserProfileModule.class } is no magic. You have to create it yourself:
objectGraph = ObjectGraph.create(new RootModule(this), new UserProfileModule() );
Furthermore, the #Inject annotation on the Context doesn't work. You're better off modifying your provider method:
#Provides
public AccountUtils.UserProfile provideUserProfile(Context context) {
return AccountUtils.getUserProfile(context);
}
I cannot guarantee this will solve all your problems, but it will help you in the right direction.