I studied some tutorials about new dagger.android approach but can't make it right. I built a simple tutorial with subcomponent builder but i can't convert this to new approach. All tutorials and articles leave required parts empty.
This example is for learning purposes may not be suitable for practical situations and i only intended implementing field injection, constructor injection may be more appropriate.
Scope
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
Parent Component
#Component(modules = ApplicationModule.class)
#Singleton
public interface ApplicationComponent {
// Calls SubComponent Builder from MainActivity using ApplicationComponent
ToastMakerSubComponent.Builder toastMakerBuilder();
}
Parent Module, i didn't use #BindsInstance in app module for learning different approaches
#Module(subcomponents = {ToastMakerSubComponent.class})
public class ApplicationModule {
private Context context;
public ApplicationModule(Context context) {
this.context = context;
}
#Provides
#Singleton
SharedPreferences provideSharedPreferences() {
System.out.println("ApplicationModule context: " + context);
return context.getSharedPreferences("PrefName", Context.MODE_PRIVATE);
}
}
Sub Component
#ActivityScope
#Subcomponent(modules = {ToastMakerModule.class})
public interface ToastMakerSubComponent {
void inject(MainActivity mainActivity);
/*
Builder is called from Parent Component,
and parent component is called from scope(Activity, Fragment, etc.)
*/
#Subcomponent.Builder
interface Builder {
ToastMakerSubComponent build();
#BindsInstance
Builder context(Context context);
}
}
Sub Component Module
#Module
public class ToastMakerModule {
#ActivityScope
#Provides
ToastMaker provideToastMaker(Context context) {
System.out.println("ToastMakerModule context: " + context);
return new ToastMaker(context);
}
}
Instantiate ApplicationComponent inside MyApplication
mApplicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(getApplicationContext()))
.build();
And get sub component inside Activity
ApplicationComponent applicationComponent = ((MyApplication) getApplication()).getApplicationComponent();
ToastMakerSubComponent toastMakerSubComponent = applicationComponent
.toastMakerBuilder()
.context(this)
.build();
toastMakerSubComponent.inject(this);
After following this, this, and this tutorial i built the following as
#Component(modules = ApplicationModule.class)
#Singleton
public interface ApplicationComponent {
// What should i put here ???
}
Parent module but i didn't get how i should provide provideSharedPreferences
#Module(subcomponents = {ToastMakerSubComponent.class})
abstract class ApplicationModule {
#Binds
#IntoMap
#ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends MainActivity>
bindYourActivityInjectorFactory(ToastMakerSubComponent.Builder builder);
}
Sub component is missing ToastMakerSubComponent build(); and #BindsInstance Builder context(Context context); how should i add these to this component.
#ActivityScope
#Subcomponent(modules = {ToastMakerModule.class})
public interface ToastMakerSubComponent extends AndroidInjector<MainActivity> {
// ??? Is this required?
void inject(MainActivity mainActivity);
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
???
}
}
Also i see #ContributesAndroidInjector, where does it fit in this example?
Here's the diff of changes: https://www.diffchecker.com/3IL8UQ7P
Make app component extend AndroidInjector<MyApplication> and include the AndroidSupportInjectionModule
#Component(modules = {ApplicationModule.class, AndroidSupportInjectionModule.class})
#Singleton
public interface ApplicationComponent extends AndroidInjector<MyApplication> {
}
Make the subcomponent extend AndroidInjector<MainActivity> and the builder AndroidInjector.Builder<MainActivity>, use seedInstance to provide arguments to #BindsInstance methods.
#ActivityScope
#Subcomponent(modules = {ToastMakerModule.class})
public interface ToastMakerSubComponent extends AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
#Override
public void seedInstance(MainActivity instance) {
context(instance);
}
#BindsInstance
public abstract Builder context(Context context);
}
}
Add a binding for the subcomponent builder to a module installed in the app component:
#Module(subcomponents = {ToastMakerSubComponent.class})
public abstract class ApplicationModule {
private Context context;
public ApplicationModule(Context context) {
this.context = context;
}
#Provides
#Singleton
SharedPreferences provideSharedPreferences() {
System.out.println("ApplicationModule context: " + context);
return context.getSharedPreferences("PrefName", Context.MODE_PRIVATE);
}
#Binds
#IntoMap
#ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindMainActivityFactory(ToastMakerSubComponent.Builder builder);
}
Let framework classes extend the Dagger* counterparts, or implement Has*Injector interfaces if you can't use inheritance
public class MyApplication extends DaggerApplication {
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerApplicationComponent.create();
}
}
public class MainActivity extends DaggerAppCompatActivity {
#Inject
SharedPreferences sharedPreferences;
#Inject
ToastMaker toastMaker;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toastMaker.showToast("sharedPreferences " + sharedPreferences);
}
}
Related
I'm really confused about dagger 2. There are so many different ways people provide a solution. How can i inject my application to provide my database?
I already have my database module, an application module that provide my application. my applicationComponent with a builder inside it (have no idea what it does). With that builder it is not possible to build it inside my application class!?
Application class:
public class OnSiteApplication extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
AppComponent:
#Singleton
#Component(modules = {ApplicationModule.class, AndroidInjectionModule.class})
public interface AppComponent extends AndroidInjector<OnSiteApplication> {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(OnSiteApplication onSiteApplication);
}
Application Module:
#Module(includes = DatabaseModule.class)
public class ApplicationModule {
private static final String SHARED_PREFERENCE_NAME = "OnSitePreferences";
private final OnSiteApplication application;
public ApplicationModule(OnSiteApplication appContext) {
this.application = appContext;
}
#Provides
public OnSiteApplication provideOnSiteApplication() {
return application;
}
}
Database Module:
#Module
public class DatabaseModule {
#Provides
public static OnSiteDatabase provideDatabase(Application appContext) {
return Room.databaseBuilder(appContext,
OnSiteDatabase.class, OnSiteDatabase.DATABASE_NAME)
.fallbackToDestructiveMigration()
.build();
}
#Provides
public static ProjectDao provideProjectAccess(OnSiteDatabase onSiteDatabase) {
return onSiteDatabase.projectDao();
}
}
Most of the solution are one year or older. What is the most modern way of injecting OS based classes like Application?
In your implementation there are some problems:
Problem - 1:
You provide DatabaseModule as dependency to ApplicationModule which is not correct as DatabaseModule never used inside ApplicationModule. Rather you can pass
ApplicationModule to DatabaseModule as application context is used to create database. [But which is not required, check below]
Problem - 2:
Here ApplicationModule is useless as Application is already provided through #BindsInstance. So you can delete ApplicationModule from here.
So final implementation looks like:
#Singleton
#Component(modules = {DatabaseModule.class, AndroidInjectionModule.class})
public interface AppComponent extends AndroidInjector<OnSiteApplication> {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(OnSiteApplication onSiteApplication);
}
And OnSiteApplication, Create the AppComponent like below:
public class OnSiteApplication extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().application(this).build();
}
public AppComponent getAppComponent() {
return appComponent;
}
#Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
I am trying to figure out how to use Dagger components/subcomponents. I know it is the older style of things but I have a legacy app that is using this Dagger structure and I'm using this sample project to better understand it.
This sample app has just a AppComponent, AppModule, ActivityComponent and ActivityModule. I'm trying to inject MainActivityDependency into my MainActivity but the compiler says it can provide it without an explicit #Provides. But I am already explicitly providing it. I know I can just annotate the class with #Inject but for the sake of exercise I need to use the #Provides method.
It seems like I have the dependency structure correct but can't figure it out. I've pasted sample code below but here is the github link as well:
https://github.com/fkruege/daggerSubComponentPractice/tree/master/app/src/main/java/com/franctan/daggerpractice
Thank you
AppComponent.java
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
ActivityComponent.Builder activityComponentBuilder();
Application application();
void inject(MyApp myApp);
}
AppModule
#Module(subcomponents = {ActivityComponent.class})
public class AppModule {
private Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides
#Singleton
public Application provideApplication() {
return app;
}
#Provides
#Singleton
public AppDependency provideAppDependency() {
return new AppDependency();
}
}
ActivityComponent
#ActivityScope
#Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
#Subcomponent.Builder
interface Builder {
Builder activityModule(ActivityModule module);
ActivityComponent build();
}
void inject(MainActivity mainActivity);
}
ActivityModule
#Module
public class ActivityModule {
private Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
#ActivityScope
#Provides
public Activity provideActivity() {
return activity;
}
#ActivityScope
#Provides
public MainActivityDependency provideMainActivityDependency() {
return new MainActivityDependency();
}
}
MyApp
public class MyApp extends Application {
public AppComponent appComponent;
#Inject
AppDependency appDependency;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent
.builder()
.appModule(new AppModule(this))
.build();
appComponent.inject(this);
}
}
MainActivity
#Inject
MainActivityDependency mainActivityDependency;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApp myApp = (MyApp) getApplication();
myApp.appComponent
.activityComponentBuilder()
.activityModule(new ActivityModule(this))
.build()
.inject(this);
So figured it out. It turns out the ActivityScope interface was defined incorrectly:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Scope
public #interface ActivityScope {
}
The #Qualifier annotation was messing things up. Dumb mistake.
Looking at some examples I was able to work with Dagger 2.11 for activities and fragments, however, I'm not getting any progress when creating modules for calls to Webservice using Retrofit + RxJava. Sorry if it is a repeated question, I have not found solution here.
Follow my application code below:
App.java
public class App extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().create(this).inject(this);
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
} else {
Fabric.with(this, new Crashlytics());
Timber.plant(new FirebaseCrashReportingTree());
}
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
AppComponent.java
#Singleton
#Component(modules = {
AppModule.class})
interface AppComponent extends AndroidInjector<App> {
#Component.Builder
abstract class Builder extends AndroidInjector.Builder<App> {
}
}
AppModule.java
#Module(includes = {AndroidInjectionModule.class})
abstract class AppModule {
#Binds
#Singleton
abstract Application application(App app);
#PerActivity
#ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity mainActivityInjector();
}
I am new to Dagger and learning implementation of Dagger 2.11 in an android application. I followed some tutorials and created a sample project. But I am getting this error :
Error:(19, 8) error: [dagger.android.AndroidInjector.inject(T)] Found a dependency cycle:
com.saharan.daggerart.DaggerArtApplication is injected at
com.saharan.daggerart.AppModule.provideApplicationContext(application)
com.saharan.daggerart.DaggerArtApplication is injected at
com.saharan.daggerart.AppModule.provideSharedPreferences(application)
android.content.SharedPreferences is injected at
com.saharan.daggerart.MainActivity.preferences
com.saharan.daggerart.MainActivity is injected at
dagger.android.AndroidInjector.inject(arg0)
AppComponent :
#Singleton
#Component(modules = {AndroidInjectionModule.class, AppModule.class, ActivityModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(DaggerArtApplication application);
}
AppModule:
#Singleton
#Module(subcomponents = {MainActivityComponent.class})
public class AppModule {
#Provides
#Singleton
DaggerArtApplication provideApplicationContext(DaggerArtApplication application) {
return application;
}
#Provides
#Singleton
SharedPreferences provideSharedPreferences(DaggerArtApplication application) {
return application.getSharedPreferences(Constants.PrefKeys.PREF_NAME, Context.MODE_PRIVATE);
}
}
ActivityModule:
#Module
public abstract class ActivityModule {
#Binds
#IntoMap
#ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindMainActivity(MainActivityComponent.Builder builder);
}
MainActivityComponent:
#Subcomponent(modules = MainActivityModule.class)
public interface MainActivityComponent extends AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
}
}
MainActivityModule:
#Module
public class MainActivityModule {
#Provides
String provideUserName() {
return "Mr. Bond";
}
}
And now MainActivity :
public class MainActivity extends AppCompatActivity {
#Inject String username;
#Inject
SharedPreferences preferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidInjection.inject(this);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
What am I doing wrong?
Please help!!
Looks like a typo.
DaggerArtApplication provideApplicationContext(DaggerArtApplication application)
This means that Dagger needs an DaggerArtApplication to provide an DaggerArtApplication. I guess you wanted to say
DaggerArtApplication provideApplicationContext(Application application)
Which would bind DaggerArtApplication to Application.
i am trying to inject the activity context inside ImageUtls class:
public class ImageUtls {
private Context mContext;
#Inject
public ImageUtls(#ActivityContext Context context) {
this.mContext = context;
}
}
AppModule:
#Module
public class AppModule {
#Provides
#ActivityContext
Context provideActivityContext(Activity activity) {
return activity;
}
#Provides
#ApplicationContext
Context provideContext(Application application) {
return application;
}
}
ActivityBuilder:
#Module
public abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = {OwnerActivityModule.class})
abstract OwnerActivity bindOwnerActivity();
}
OwnerActivityModule:
#Module
public abstract class OwnerActivityModule {
#Provides
abstract Activity bindActivity(OwnerActivity activity);
}
OwnerActivity injection:
public class OwnerActivity extends BaseActivity {
#Inject
ImageUtls imageUtls;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
AppComponent:
#Singleton
#Component(modules = {AndroidInjectionModule.class, AppModule.class, ActivityBuilder.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(MyApplication application);
}
I am not getting a relevant error about what is going wrong..Can anybody address the issue?