Dagger 1.x #Inject throws an IllegalArgumentException - android

I want to replace our component registry (with dexfile class loader magic) with an dependency injection framework for Android.
The first try is dagger.
When trying I get the following error:
11-06 13:05:41.040 16269-16269/com.daggertoolkitexample E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{daggertoolkitexample/com.dagger.MyActivity}: java.lang.IllegalArgumentException: No inject registered for members/com.dagger.MyActivity. You must explicitly add it to the 'injects' option in one of your modules.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2295)
...
Caused by: java.lang.IllegalArgumentException: No inject registered for members/com.dagger.MyActivity. You must explicitly add it to the 'injects' option in one of your modules.
at dagger.ObjectGraph$DaggerObjectGraph.getInjectableTypeBinding(ObjectGraph.java:302)
at dagger.ObjectGraph$DaggerObjectGraph.inject(ObjectGraph.java:279)
at com.dagger.MyApplication.inject(MyApplication.java:39)
at com.dagger.MyBaseActivity.onCreate(MyBaseActivity.java:18)
at com.dagger.MyActivity.onCreate(MyActivity.java:22)
at android.app.Activity.performCreate(Activity.java:5372)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1104)
...
I can fix it if i inject my activity in the #Module. Its work without exception.
#Module(
library = true,
injects = MyActivity.class)
public class AuthManagementModul {...}`
But this is not that i want.
I don´t can and want to know all users of my component.
Has everyone an idea what's wrong?
Here is my example code:
public class MyBaseActivity extends ActionBarActivity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication) getApplication()).inject(this);
}
}
...
public class MyActivity extends MyBaseActivity {
#Inject AuthManagement authManagement;
...
}
...
public class MyApplication extends Application{
private ObjectGraph graph;
#Override
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(new AuthManagementModul(this));
}
public void inject(Object object) {
graph.inject(object);
}
}
...
#Module(
library = true
)
public class AuthManagementModul {
private final Application application;
public AuthManagementModul(Application application) {
this.application = application;
}
#Provides
#Singleton
AuthManagement provideAuthManagement() {
return new AuthManagementImpl(application);
}
}

In this case, you don't want to add injects= to your AuthManagementModul, but rather to an activity-specific module which includes it.
Dagger 1.x uses injects= as a signal for what graph-roots to analyze, so they must be present -but they need not be present on leaf-node library modules - just on a module the activity uses. Consider breaking up your modules on more partitioned lines like so:
#Module(
injects = {
... all your activities
},
includes = {
AuthManagementModul.class,
ApplicationModule.class
}
)
class EntryPointsModule {}
#Module(library = true, complete = false)
class AuthManagementModul {
#Provides
#Singleton
AuthManagement provideAuthManagement(Application application) {
return new AuthManagementImpl(application);
}
}
#Module(library = true)
class ApplicationModule {
private final Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application application() {
return application;
}
}
Then create your graph like so:
public class MyApplication extends Application{
private ObjectGraph graph;
#Override
public void onCreate() {
super.onCreate();
// AuthManagementModul is automatically included because it has a default
// constructor and is included by EntryPointsModule
graph = ObjectGraph.create(new EntryPointsModule(), new ApplicationModule(this));
}
public void inject(Object object) {
graph.inject(object);
}
}
There are other ways to structure this - you could just have ApplicationModule include the AuthModule and declare injects, so you only have two modules, etc. I suggested this way because ApplicationModule is then a separate concern whose only role is to hoist the Application instance into the graph, AuthManagementModul is exclusively there to support the auth function, and EntryPointsModule is there to be the front of the whole graph.
If you migrate to Dagger2, this structure is also convenient in that EntryPointsModule naturally converts to a #Component.

Related

Android, can't integrate Dagger2 bacause of error “Activity cannot be provided without an #Inject constructor ”

I want to integrate Dagger2, but i cant' build my project, build failed with:
error: com.example.animalslibrary.ui.home.activity.HomeActivity cannot be provided without an #Inject constructor or from an #Provides-annotated method.
com.example.animalslibrary.ui.home.activity.HomeActivity is injected at
com.example.animalslibrary.AppComponent.injectsHomeActivity(homeActivity)
I looking for answer about 3 hours, and i asking you for help now.
My actions step by step:
1) Add depenceses to Gradle:
implementation 'com.google.dagger:dagger:2.7'
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
2) Create empty test class NetworkUtils:
public class NetworksUtils {
}
3) Create module for it:
#Module
public class NetworksModule {
#Provides
NetworksUtils provideNetworksUtils() {
return new NetworksUtils();
}
}
4) Create "connection" interface:
import dagger.Component;
#Component(modules = NetworksModule.class)
public interface AppComponent {
void injectsHomeActivity(HomeActivity homeActivity);
}
5) Create App class. I don't completle understand why i did this(teaching by guide),exactly i don't understand why i need to extend by Application. Maybe to create all components when application starts. DaggerAppComponent is red, because of failed while building, this class does't created yet.
public class App extends Application {
private static AppComponent component;
#Override
public void onCreate() {
super.onCreate();
component = DaggerAppComponent.create();
}
public static AppComponent getComponent() {
return component;
}
}
6) Add App to manifest:
<application
android:name="com.example.animalslibrary.ui.App"
...
7) Now i write my HomeActivity class...
public class HomeActivity extends AppCompatActivity implements
HomeContract.View {
...
#Inject
private NetwotkUtils netwotkUtils;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
...
App.getComponent().injectsHomeActivity(this);
...
}
8)...and goes down when try to build project.
I think my error about App, but i can't understand where and how fix it.
UPD:
This modification is not helped:
public class NetwotkUtils {
#Inject
public NetwotkUtils() {
}
}
There are two problems with your code and one is leading to error you're getting.
You must include your module into building here component = DaggerAppComponent.create();
Dagger is NOT injecting into private fields.
Example of p1:
DaggerAppComponent.builder()
.networkModule(new NetworkModule())
.build()

Android Test with Dagger mock inject constructor

Hi i've got a following Problem. I want to write android tests with espresso for the Ui and in order to have tests that are not flaky i want to mock my presenter.
I use Dagger in the App. My Configuration is as Following:
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
//some injections
//some providings
}
I have a Module for the Component
#Module
public class AppModule {
//providings for component
}
then i have also a component for the activities with a module for the component
#PerActivity
#Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
//inject activites
//provide subcomponents for activites
}
then i have subcomponents for my pages
#PerActivity
#Subcomponent(modules = InfoModule.class)
public interface InfoComponent {
void inject(DetailActivity activity);
}
and a module for the subcomponent
#Module
public class InfoModule {
#Provides
public DetailPresenter provideDetailPresenter(ShowDetailsUseCase showDetailsUseCase,
OtherUseCase getPoisUseCase,
AccountManager accountManager, Navigator
navigator) {
return new DetailPresenter(showDetailsUseCase, otherUseCase, accountManager, navigator);
}
}
and then the detail Activity Injects the DetailPresenter
public class DetailActivity extends BaseActivity {
#Inject
DetailPresenter mPresenter;
InfoComponent mComponent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mComponent = getActivityComponent().provideInfoModule(new InfoModule());
mComponent.inject(this);
mPresenter.bindView(this);
mPresenter.onCreate(new PresenterBundle(getIntent().getExtras(), savedInstanceState));
}
//functionality of detailActiviy
}
then i have the presenter which uses constructor injection
public class DetailPresenter extends BasePresenter {
private ShowDetailsUseCase mDetailsUseCase;
private final OtherUseCase getPoisUseCase;
private AccountManager accountManager;
private Navigator navigator;
#Inject
public DetailPresenter(ShowDetailsUseCase getDetailsUseCase, OtherUseCase getPoisUseCase,
AccountManager
accountManager, Navigator navigator) {
this.mDetailsUseCase = getDetailsUseCase;
this.getPoisUseCase = gotherUseCase;
this.accountManager = accountManager;
this.navigator = navigator;
}
#Override
public void onCreate(#Nullable PresenterBundle bundle) {
super.onCreate(bundle);
//other things to do on initialization
((DetailView) getView()).showDetails(getDetailsFromUseCase());
}
}
Now in the test i want to do mock the presenter:
#RunWith(AndroidJUnit4.class)
public class DetailActivityTest {
#Rule
public final ActivityTestRule<DetailActivity> main = new ActivityTestRule<DetailActivity>(DetailActivity.class, true, false);
#Rule
public final DaggerMockRule<AppComponent> rule=new EspressoDaggerMockRule();
#Mock
DetailPresenter presenter; //does not work because #Inject constructor
#Test
public void locationTest() {
Details details = generateDetails();
launchActivity();
doAnswer(answer -> {
activity.showDetails(details);
return null;
}
).when(presenter).onCreate(any());
//espresso verify afterwards
}
}
but if i try to mock the following error shows:
java.lang.RuntimeException: Error while trying to override objects:
a.b.c.ui.mvp.presenter.DetailPresenter
You must define overridden objects using a #Provides annotated method instead of using #Inject annotation
does someone have an idea how I am able to mock the presenter even with #Inject constructor and dependencies.
I do not want to mock the data layer because then I have to mock database, apiClient, cacheData and so on. And some of the datalayer also have inject dependencies so i cannot mock them either.
Thank you in advance
The DetailPresenter class is created in the InfoModule, so you don't need the Inject annotation. The error you get is because using DaggerMock you can replace only the objects created in a module. In your example you are already creating it in a module, you just need to remove the Inject annotation.

Dagger 2 error: "RepositoryImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method"

For example, I have following interface:
public interface Repository {
Observable<Pojo> getPojos();
}
And its implementation:
public class RepositoryImpl implements Repository {
public RepositoryImpl() {
}
#Override
public Observable<Pojo> getPojos() {
return null;
}
}
Module:
#Module
class AppModule {
public AppModule() {
}
#Provides
#Singleton
Repository provideRepositoryImpl() {
return new RepositoryImpl();
}
}
And component:
#Singleton
#Component(modules = { AppModule.class })
public interface AppComponent {
void inject(MainActivity mainActivity);
}
When I trying to build project, I receive error as in question title. What problem in my code?
Read your error carefully (emphasis mine):
Dagger 2 error: “RepositoryImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method”
Generally this means you've tried to #Inject RepositoryImpl, not #Inject Repository. This is especially important because your Module directly calls the RepositoryImpl constructor rather than letting Dagger create your RepositoryImpl using an #Inject-annotated constructor. (If you had, you could make RepositoryImpl a parameter of your #Provides method or switch to a #Binds method, and you have your choice between injecting the interface versus the implementation.)
The way I setup Dagger 2 is in my projects Application I add the injection component. like so.
public class NyApplication extends Application {
InjectionComponent component;
#Override
public void onCreate() {
super.onCreate();
setDagger();
}
private void setDagger() {
component = DaggerAppComponent.builder()
.appComponent(new AppModule())
.build();
component.inject(this);
}
public InjectionComponent getComponent() {
return component;
}}
and then int my activity whatever it is. I inject on it's onCreate like this.
public class MainActivity extends Activity {
#Inject
Object object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication) getApplication()).getComponent().inject(this);
}}
I hope this helps you.

Dagger injects, complete confusion

I'm using dagger:1.2.2. There're two modules. BaseModule serves an EventBus for an Activity and ActivityModule serves NavigationController for the same Activity.
#Module(injects = { `does NOT have MainActivity.class registered`},
library = true
)
public class BaseModule {
#Provides #Singleton public EventBus provideEventBus() {
return DefaultEventBus.create();
}
#Provides #Singleton public Application provideApplication() {
return mApp;
}
#Provides #IsTablet public boolean isTablet(Application app) {
return app.getResources().getBoolean(R.bool.is_tablet);
}
}
#Module(
addsTo = BaseModule.class,
injects = MainActivity.class
)
public class ActivityModule {
#Provides
NavigationController provideNavigationController(Application app,#IsTablet boolean isTablet) {
return new NavigationController(app, isTablet);
}
}
Activity's fields:
#Inject
EventBus mEventBus;
#Inject
NavigationController mNavigationController;
NavigationController's ugly ctor:
public NavigationController(Context context, FragmentManager fm, List<Behaviour> behaviourList, #Nullable Class lastRoot,
#IsTablet boolean isTablet, Application application) {
This code passes static validation and performs well with
protected void onCreate(Bundle savedInstanceState) {
ObjectGraph activityGraph = getBaseModuleGraph(this).plus(new ActivityModule(this));
activityGraph.inject(this);
}
It crushes as expected due to unsatisfied dependency provided by ActivityModule if i just apply:
getBaseModuleGraph(this).inject(this);
The thing that bugs me is that BaseModule's inject doesn't contain MainActivity while it provides EventBus dependency for it. If I add this inject it gonna complain about unresolved NavigationController that is provided by ActivityModule. Setting complete=false on the BaseModule results in runtime exception
NavigationController has no injectable members. Do you want to add an
injectable constructor? required by class
MainActivity
I don't quite get it.
Ultimately I wanted BaseModule's injects to be more explicit by including MainActivity because it provides EventBus as I mentioned.
Could you help me out understanding it?

Dagger v2: Inject 2 different scopes into one object

I have moduleA setup as an application wide singleton provider, ModuleB as a user related object provider
My user display fragment will use system wide bus to send message to others and use user related object to display.
Problem cannot inject different scrope class into one object. Use component.getX method works fine, but inject is prefered way.
Error message:
#UserScope may not reference bindings with difference scopes: #Provides #Singleton Bus ModuleA.provideBus()
#Module
public class ModuleA {
#Provides #Singleton Bus provideBus() {...}
}
Module B as user related Info provider
#Module
public class ModuleB{
private final User user;
public ModuleB(User user) {...}
#Provides #UserScope User provideUser() {}
#Provides #UserScope UserManager provideUserManager() {}
}
Components setup like following:
#Component (modules={ModuleA.class})
#Singleton
public interface ComponentA {
Bus getBus();
void inject(ClassA target);
}
#Component(modules={ModuleB.class})
#UserScope
public interface ComponentB {
User getUser();
UserManager getUserManager();
void inject(ClassA target);
}
class UserFragment exrtends Fragment {
#Inject Bus bus;
#Inject UserManager userManager;
public void onCreate() {
getComponentA().inject(this);
getComponentB().inject(this);
}
}
Try this configuration, it works for me. There is really a lack of good documentation about Dagger2 so I studied a few open-source examples of code that you can find in GitHub etc by keyword like Dagger2.
Application level Component
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
// exported for child-components
Bus eventBus();
}
Application level Module
#Module
public class AppModule {
#Provides #Singleton
Bus provideBus() {
return BusProvider.getInstance();
}
}
Activity level Component
#ActivityScope
#Component(dependencies=AppComponent.class, modules=MainActivityModule.class)
public interface MainActivityComponent {
void inject( MainActivity mainActivity );
}
Activity level Module
#Module
public class MainActivityModule {
private final MainActivity mActivity;
public MainActivityModule( MainActivity activity ) {
mActivity = activity;
}
#Provides
MainActivityTitleController provideTitleController() {
return new MainActivityTitleController( mActivity );
}
}
Android Application class
public class MyApplication extends Application {
private AppComponent mAppComponent;
#Override
public void onCreate() {
super.onCreate();
// Dagger2
mAppComponent = Dagger_AppComponent.builder()
.appModule( new AppModule( this ))
.build();
}
public AppComponent getComponent() {
return mAppComponent;
}
public static AppComponent getComponent( Context context ) {
return ((MyApplication)context.getApplicationContext()).getComponent();
}
}
And finally Activity
public class MainActivity extends ActionBarActivity {
// Injectable fields
#Inject Bus mEventBus;
#Inject MainActivityTitleController mTitleController;
private MainActivityComponent mComponent;
#Override
protected void onCreate( Bundle savedInstanceState ) {
// Dagger2
mComponent = Dagger_MainActivityComponent.builder()
.appComponent( ((MyApplication)getApplication()).getComponent() )
.mainActivityModule( new MainActivityModule( this ) )
.build();
mComponent.inject( this );
}
}
I think the main problem in your code snippets you provided, is that your ModuleB should have a dependency on ModuleA to correctly provide the singleton with the error you were getting. I.e. this should work:
#Component(modules={ModuleB.class}, dependencies = ComponentA.class)
#UserScope
public interface ComponentB {
User getUser();
UserManager getUserManager();
void inject(MainActivity target);
}
I recreated your classes and made some assumptions to fill in the blanks, and it seems to work fine. You can see the full working code here on GitHub. The only difference in my code is, what you called ClassA/UserFragment I just called MainActivity but otherwise the structure is the same.

Categories

Resources