I am trying to learn Dagger2 from this medium article and pass RequestQueue as an activity level dependency:
https://proandroiddev.com/dagger-2-annotations-binds-contributesandroidinjector-a09e6a57758f
I can create application components just fine but I am facing a lot of trouble with ContributesAndroidInjector.
Application Class:
public class PokemonApplication extends Application {
private static AppComponent appComponent;
public static AppComponent getAppComponent(){
return appComponent;
}
#Override
public void onCreate() {
super.onCreate();
appComponent=buildMyComponent();
}
private AppComponent buildMyComponent() {
return DaggerAppComponent.builder().appmod(this).build();
}
}
AppModule:
#Module
public abstract class AppModule {
#ContributesAndroidInjector(modules = VolleyModule.class)
abstract MainActivity mainActivity();
#Provides
#Singleton
static SharedPreferences providePreferences(Application application) {
return application.getSharedPreferences("data", Context.MODE_PRIVATE);
}
#Provides
#Singleton
static Context getContext(Application application){
return application.getApplicationContext();
}
}
AppComponent:
#Singleton
#Component(modules = {RetrofitModule.class,AppModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity); //Error here.
#Component.Builder
interface Builder
{
AppComponent build();
#BindsInstance Builder appmod(Application application);
}
}
VolleyModule:
#Module
public abstract class VolleyModule {
#Provides
static RequestQueue getRequestQueue(Context context) {
return Volley.newRequestQueue(context);
}
}
MainActivity:
#Inject
RequestQueue requestQueue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PokemonApplication.getAppComponent().inject(this);
VolleyModule has to be also declared in the list of modules in your Component
#Singleton
#Component(modules = {RetrofitModule.class,AppModule.class, VolleyModule.class})
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 confused about Dagger2 in Android.
I use two scope. #Singleton, #PerActivity
This is my Code. I simplyfy my code.
//ApplicationComponent.java
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
#Named("packageName") String packageName();
}
//ApplicationModule.java
#Module
public class ApplicationModule {
#Provides
#Singleton
public Context provideApplicationContext() {
return MyApplication.getContext();
}
#Provides
#Singleton
#Named("packageName")
public String providePackageName(Context context) {
return context.getPackageName();
}
}
//UserComponent.java
#PerActivity
#Component(modules = {UserModule.class})
public interface UserComponent {
void inject(MainActivity activity);
}
//UserModule.java
#Module
public class UserModule {
String packageName;
public UserModule(String packageName) {
this.packageName = packageName;
}
#Provides
#PerActivity
UserRepositoryImpl provideUserRepositoryImpl() {
return new UserRepositoryImpl(packageName);
}
}
for inject appVersion, packagename in UserModule
DaggerChatComponent.builder()
.userModule(new UserModule(getApplicationComponent().packageName()))
.build();
but it looks not great. how can i inject when use different Scope??
your ApplicationModule.java is correct
#Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application){
this.application = application;
}
#Provides
#Singleton
Context provideContext(){
return application;
}
#Provides
#Singleton
#Named("packagename")
public String providePackageName(Context context) {
return context.getPackageName();
}
}
and it's component class is also right ApplicationComponent.java
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
#Named("packagename") String providepackagename();
}
but in the UserModule.java you need not pass the package name object , dagger's object graph does this for you.
#Module
public class UserModule {
public UserModule() {
}
#Provides
#PerActivity
UserRepositoryImpl provideUserRepositoryImpl(#Named("packagename") String packageName) {
return new UserRepositoryImpl(packageName);
}
}
and the next step is while writing the component class for this module add the application component as a dependency ie, your UserComponent.java looks like this
#PerActivity
#Component(dependencies = {ApplicationComponent.class},modules = {UserModule.class})
public interface UserComponent {
void inject(MainActivity mainActivity);
}
with the activity scope as PerActivity.lava
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerActivity {
}
with this example UserRepositoryImpl.java as
class UserRepositoryImpl {
private String packagename;
public UserRepositoryImpl(String packagename){
this.packagename = packagename;
}
String getPackagename(){
return packagename;
}
}
you can finally inject this in your activity.(MainActivity.java)
public class MainActivity extends AppCompatActivity {
#Inject
UserRepositoryImpl userRepository;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApplicationComponent component=DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(getApplication())).build();
UserComponent userComponent=DaggerUserComponent.builder().applicationComponent(component).userModule(new UserModule()).build();
userComponent.inject(this);
Log.e("name"," "+userRepository.getPackagename());
}
}
How can I provide activity context in mainModule class ? Thanks! The code looks like this:
#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(MvmApp app);
}
Activtity builder:
#Module
public abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = {MainModule.class})
abstract MainActivity bindMainActivity();
}
And I have an appModule, and a module for each activity:
#Module
public class AppModule {
#Provides
#Singleton
#ApplicationContext
Context provideContext(Application application) {
return application;
}
#Provides
#Singleton
DataManager provideDataManager(AppDataManager appDataManager) {
return appDataManager;
}
#Provides
#DatabaseInfo
String provideDatabaseName() {
return "carDatabase";
}
#Provides
#Singleton
AppDataBase provideAppDatabase(#DatabaseInfo String dbName, #ApplicationContext Context context) {
return Room.databaseBuilder(context, AppDataBase.class, dbName)
.build();
}
#Provides
#Singleton
DbHelper provideDbHelper(AppDbHelper appDbHelper) {
return appDbHelper;
}
}
AppClass:
public class MvmApp extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this);
}
#Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
All my activities extends a base activity which inject the Dagger dependecies.
public abstract class BaseActivity extends AppCompatActivity implements MvpView {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidInjection.inject(this);
}
}
In mainModule I need to provide the context of the activity.
#Module
public class MainModule {
#Provides
MainMvpPresenter<MainMvpView> provideMainPresenter(
MainPresenter<MainMvpView> presenter) {
return presenter;
}
#Provides
CompositeDisposable provideCompositeDisposable() {
return new CompositeDisposable();
}
#Provides
CarAdapter provideCarAdapter( #ActivityContext Context context) {
return new CarAdapter(context);
}
}
The solution was:
#Module
public class MainModule {
#Provides
MainMvpPresenter<MainMvpView> provideMainPresenter(
MainPresenter<MainMvpView> presenter) {
return presenter;
}
#Provides
CompositeDisposable provideCompositeDisposable() {
return new CompositeDisposable();
}
#Provides
CarAdapter provideCarAdapter(MainActivity activity) {
return new CarAdapter(activity);
}
}
For people facing a similar issue, I've made a project with Kotlin, and the new android-dagger extension with a lengthy explanation on how things work over here: https://github.com/Obaied/BareBonesAndroidDagger
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?
I have created NetComponent and NetModule where I am initialise all required libs for network communication.
Here is NetComponent code:
#Singleton
#Component(modules={AppModule.class, NetModule.class})
public interface NetComponent {
Retrofit retrofit();
}
I have initalised that NetComponent inside Application class.
public class App extends Application {
private NetComponent mNetComponent;
#Override
public void onCreate() {
super.onCreate();
mNetComponent = DaggerNetComponent.builder()
.appModule(new AppModule(this))
.netModule(new NetModule("mock.com"))
.build();
}
public NetComponent getNetComponent() {
return mNetComponent;
}
}
Now I want to use SharedPreferences inside my application, so I have created SharedPreferencesModule and Component classes.
#Module
public class SharedPreferenceModule {
Application mApplication;
public SharedPreferenceModule(Application mApplication) {
this.mApplication = mApplication;
}
#Provides
#Singleton
Application providesApplication() {
return mApplication;
}
#Provides
#Singleton
SharedPreferences providesSharedPreferences(Application application) {
return PreferenceManager.getDefaultSharedPreferences(application);
}
}
Here is the code from SharedPreferencesComponent:
#Singleton
#Component(modules = {SharedPreferenceModule.class})
public interface SharedPreferenceComponent {
SharedPreferences getSharedPreference();
}
I want to use that inside my LoginAccountFragment class. LoginAccountFragment class has it's own module and component.
#Module
public class LoginAccountModule {
private LoginAccountView view;
public LoginAccountModule(LoginAccountView view){
this.view = view;
}
#Provides
#FragmentScoped
LoginAccountView providesLoginAccountView(){
return view;
}
#Provides
#FragmentScoped
LoginAccountPresenter providesLoginAccountPresenter(LoginAccountPresenterImpl presenter){
return presenter;
}
}
#FragmentScoped
#Component(dependencies = {NetComponent.class}, modules = LoginAccountModule.class)
public interface LoginAccountComponent {
void inject(LoginAccountFragment fragment);
}
How I can inject SharedPreferences in LoginAccountFragment?
#Singleton
#Component(modules={AppModule.class, NetModule.class, SharedPreferenceModule.class})
public interface ApplicationComponent {
Retrofit retrofit();
SharedPreferences sharedPreferences();
}
public class App extends Application {
private ApplicationComponent applicationComponent;
#Override
public void onCreate() {
super.onCreate();
this.applicationComponent = DaggerNetComponent.builder()
.appModule(new AppModule(this))
.netModule(new NetModule("https://mock.com"))
.build();
}
public ApplicationComponent applicationComponent() {
return applicationComponent;
}
}
#Module
public class SharedPreferenceModule {
#Provides
#Singleton
SharedPreferences providesSharedPreferences(Application application) {
return PreferenceManager.getDefaultSharedPreferences(application);
}
}
#Module
public class LoginAccountModule {
private LoginAccountView view;
public LoginAccountModule(LoginAccountView view){
this.view = view;
}
#Provides
LoginAccountView loginAccountView(){
return view;
}
#Provides
#FragmentScoped
LoginAccountPresenter loginAccountPresenter(LoginAccountPresenterImpl presenter) {
return presenter;
}
}
#FragmentScoped
#Component(dependencies = {ApplicationComponent.class}, modules = LoginAccountModule.class)
public interface LoginAccountComponent extends ApplicationComponent {
void inject(LoginAccountFragment fragment);
}