I have the following (simplified) setup of 1 Application and 1 Activity, 1 Parent Component and 1 Subcomponent:
Application:
public class ExampleApp extends Application {
private AppComponent component;
#Override
public void onCreate() {
super.onCreate();
component = DaggerAppComponent.create();
}
public AppComponent getAppComponent() {
return component;
}
}
AppComponent:
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
ActivityComponent.Builder getActivityComponentBuilder();
}
Activity Subcomponent:
#PerActivity
#Subcomponent(modules = {ActivityModule1.class, ActivityModule2.class})
public interface ActivityComponent {
void inject(MainActivity mainActivity);
#Subcomponent.Builder
interface Builder {
#BindsInstance
Builder binding1(#Named("binding1") int binding1);
#BindsInstance
Builder binding2(#Named("binding2") int binding2);
ActivityComponent build();
}
}
MainActivity:
public class MainActivity extends AppCompatActivity {
#Inject
TheObjectIWant theObjectIWant1, theObjectIWant2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityComponent component = ((ExampleApp) getApplication()).getAppComponent()
.getActivityComponentBuilder()
.binding1(100)
.binding2(200)
.build();
component.inject(this);
theObjectIWant1.doStuff();
theObjectIWant2.doStuff();
}
}
I would now like to refactor this same example to use the Module.subcomponents attribute instead of the Subcomponent.Builder factory method.
For this, I remove getActivityComponentBuilder() from the AppComponent and add the Module.subcomponents attribute to the AppModule:
AppComponent (new):
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
}
AppModule (new):
#Module(subcomponents = ActivityComponent.class)
public abstract class AppModule {
[...]
}
My question is pretty simple. How do I now get the ActivityComponent.Builder into the MainActivity? I can't constructor inject the MainActivity, so there's only really field injection left:
MainActivity (new, theory):
public class MainActivity extends AppCompatActivity {
#Inject
ActivityComponent.Builder activityComponentBuilder
#Inject
TheObjectIWant theObjectIWant1, theObjectIWant2;
((ExampleApp) getApplication()).getAppComponent().inject(this)
[...]
}
But this will not work, since the AppComponent can't inject theObjectIWant1 and theObjectIWant1. They only come from the ActivityComponent.
So is this approach just not suitable here? Since this is more of a theoretical question, please ignore dagger.android here and assume we want to handle this without it.
Related
Using the google sample GithubsampleBrowser I have become stuck on trying to inject the ViewModelProvider.Factory.
When comparing with the sample, I see it goes GithubViewModelFactory fine but mine never does and i'm not sure what I am missing. Hopefully it is something very simple and presumably is because I am using androidx components instead.
Main Activity:
public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {
#Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingAndroidInjector;
}
}
CategoryFragment:
public class CategoryFragment extends Fragment implements Injectable {
#Inject
ViewModelProvider.Factory viewModelFactory;// <-- remains null
AppInjector:
public class AppInjector {
private AppInjector() {}
public static void init(CrosscareApp crossCareApp) {
DaggerAppComponent.builder().application(crossCareApp)
.build().inject(crossCareApp);
crossCareApp
.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
........
}
private static void handleActivity(Activity activity) {
if (activity instanceof HasSupportFragmentInjector) {
AndroidInjection.inject(activity);
}
if (activity instanceof FragmentActivity) {
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}
}
AppComponent:
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class,
MainActivityModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(CrosscareApp crosscareApp);
}
AppModule:
#Module(includes = ViewModelModule.class)
class AppModule {
#Singleton #Provides
CrosscareService provideCrossCareService() {
return new Retrofit.Builder()
..........
}
ViewModelModule:
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(CategoryViewModel.class)
abstract ViewModel bindCategoryViewModel(CategoryViewModel categoryViewModel);
#Binds
abstract ViewModelProvider.Factory bindViewModelFactory(CrosscareViewModelFactory factory);
}
CrosscareViewModel:
#Singleton
public class CrosscareViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
#Inject
public CrosscareViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
Greatly appreciate any help.
Well after all that, I was missing one thing. In the manifest, I needed:
android:name=".CrosscareApp"
In the sample, it was
android:name=".GithubApp"
Presumably the file at the root of the directory.
And then it works.
I have an object SQLDatabaseHelper contains methods onCreate , onUpgrade and others but I can't send the object to another activity.
Don't pass it between activities, per se.
1.Use Service locator pattern
(not recommended)
public class Locator {
public SQLDatabaseHelper getDbHelper() {
}
}
2.Use a dependency injection framework like Dagger. Something like this:
#Component(modules = {DbModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MainActivity mainActivity);
}
#Module
public class DbModule {
#Provides
#Singleton
public static SQLDatabaseHelper dbHelper() {
//create your helper here
}
}
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
Injector.init(this);
}
}
public class Injector {
private static AppComponent appComponent;
public static void init(Application app) {
appComponent = DaggerAppComponent
.builder()
.application(app)
.build();
}
public static AppComponent appComponent() {
return appComponent;
}
}
public class MainActivity {
protected void onCreate() {
Injector.appComponent().inject(this);
}
}
I want to use dagger approach to for helper classes.
Bellow is the code i am currently using for Activity. But i am not able to understand how should i be using this for helper class.
Where helper class is called inside a receiver.
AppComponent.java
#Singleton
#Component(modules = { AndroidSupportInjectionModule.class, AppModule.class, ActivityBuilder.class})
public interface AppComponent extends AndroidInjector<DaggerApplication> {
void inject(MyApplication app);
#Override
void inject(DaggerApplication instance);
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
ActivityBuilder.java
#Module
public abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity bindMainActivity();
}
AppModule.java
#Module
public abstract class AppModule {
#Binds
abstract Context provideContext(Application application);
}
MainActivityModule.java
#Module
public abstract class MainActivityModule {
#Provides
static MainPresenter provideMainPresenter(MainView mainView) {
return new MainPresenterImpl(mainView);
}
#Binds
abstract MainView provideMainView(MainActivity mainActivity);
}
The above code is what i am using for activities. I am now willing to use same structure for my helper classes too. I did a plenty of research and tried some methods which were failing as my Helper class requires 2 parameter. First one is context and another is IPhoneCallReceiver. I know IPhoneCallReceiver can be accessed in #Module using #Binds, but i am having issue getting Context. Here Helper class is CallStateHelper
PhoneCallReceiver.java
public class PhoneCallReceiver extends BroadcastReceiver implements IPhoneCallReceiver {
CallStateHelper callStateHelper;
#Override
public void onReceive(Context context, Intent intent) {
callStateHelper.stateChange(intent);
}
}
CallStateHelper.java
public class CallStateHelper extends PhoneStateListener {
private IPhoneCallReceiver receiver;
public CallStateHelper(Context context, IPhoneCallReceiver receiver) {
this.receiver = receiver;
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
}
#Override
public void onCallStateChanged(int state, String incomingNumber) {
}
}
I want to use CallStateHelper by #Inject and want it as Singleton Class. I am very new to Dagger
I finally did it.
You just have to provide DispatchingAndroidInjector<BroadcastReceiver>
I am trying to use Dagger 2 to inject my application class, MyApplication as I use it in various places. This is my setup using Dagger 2.11
MyApplication.java
public class MyApplication extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
AppInjector.init(this);
}
#Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
AppInjector.java
public class AppInjector {
public static void init(MyApplication application){
//Initialize dagger and inject the application
DaggerAppComponent.builder().application(application).build().inject(application);
application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle aBundle) {
handleActivity(activity);
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle aBundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
private static void handleActivity(Activity activity){
if(activity instanceof HasSupportFragmentInjector ){
AndroidInjection.inject(activity);
}
if (activity instanceof FragmentActivity){
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
Log.i("LifecycleCallbacks", "injected:" + f);
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}
AppComponent.java
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
ActivityBuilder.class,
AppModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(MyApplication application);
}
However, every time I try use #Inject MyApplication application in a constructor, dagger throws an error that it has no way to provide it without an #Provides
Furthure more, I am not sure I should be using the Application everywhere, and rather only its Context? If so, how would I provide the Context?
Have a look at your Builder...
#Component.Builder
interface Builder {
--> #BindsInstance Builder application(Application application);
AppComponent build();
}
All Dagger knows about is your Application, you're never mentioning MyApplication, hence injecting it will fail.
I don't know why you'd have to inject MyApplication specifically, but the easiest solution would be to change it to bind your MyApplication instead...
#Component.Builder
interface Builder {
#BindsInstance Builder application(/** --> */ MyApplication application);
AppComponent build();
}
Then Dagger knows about MyApplication but not of Application. To fix this, you can just add a module that binds the other types which is easy enough because you have the subtype...e.g.
#Module interface AppModule { // could also be an abstract class
#Binds Application bindApplication(MyApplication application);
// if you also want to bind context
#Binds Context bindContext(MyApplication application);
}
And just add this module to your component.
Sorry for my english, now i begin learn dagger2 and i cant understand why i have error:
Error:(9, 10) error:
test.dagger.dagger.modules.MainActivityPresenterModule cannot be
provided without an #Inject constructor or from an #Provides- or
#Produces-annotated method.
test.dagger.dagger.modules.MainActivityPresenterModule is injected at
test.dagger.view.activitys.MainActivity.mainActivityPresenterModule
test.dagger.view.activitys.MainActivity is injected at
test.dagger.dagger.components.AppComponent.injectMainActivity(mainActivity)
App
public class App extends Application {
private static AppComponent component;
#Override
public void onCreate() {
super.onCreate();
component = DaggerAppComponent.create();
}
public static AppComponent getComponent() {
return component;
}
}
AppComponent
#Component(modules = {MainActivityPresenterModule.class})
public interface AppComponent {
void injectMainActivity(MainActivity mainActivity);
}
MainActivityPresenterModule
#Module
public class MainActivityPresenterModule {
#Provides
MainActivityPresenter provideActivityPresenter(NetworkUtils networkUtils) {
return new MainActivityPresenter(networkUtils);
}
#Provides
NetworkUtils provideNetworkUtils(){
return new NetworkUtils();
}
}
NetworkUtils
public class NetworkUtils {
public boolean isConnection() {
return true;
}
}
MainActivityPresenter
public class MainActivityPresenter {
NetworkUtils networkUtils;
public MainActivityPresenter(NetworkUtils networkUtils) {
this.networkUtils = networkUtils;
}
public void getUser(){
if(networkUtils.isConnection()) {
Log.e("getUser", "getUser");
} else {
Log.e("no internet", "no internet connection");
}
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
#Inject
MainActivityPresenterModule mainActivityPresenterModule;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
App.getComponent().injectMainActivity(MainActivity.this);
}
}
You can inject only the things that are provided in the classes annotated with #Module (only methods inside that module which are annotated with #Provides). So, you can do #Inject MainActivityPresenter presenter, for instance, not try to inject the whole module, like you tried to do. Modules should be registered on Dagger initialisation, like this (in App#onCreate)
component = DaggerAppComponent.builder()
.mainActivityPresenterModule(MainActivityPresenterModule())
.build()
In MainActivity you only need to call inject to be able to inject your #Inject MainActivityPresenter presenter or any other injects defined in the module, like so:
#Inject MainActivityPresenter presenter
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
(application as App).component.inject(this)
// after #inject(this) above you can start using your injections:
presenter.getUser()
}
Sorry, I wrote code snippets in Kotlin as it was much less to write that way, hopefully you get the idea how it looks in Java.