I use Dagger2 in my android app.
Basically I inject a HttpClient (interface) in MainActivity.
#Module
public class MainActivityModule{
#Provides public HttpClient providesHttpComponent(){
return new RealHttpClient();
}
}
#Component( modules = MainActivityModule.class )
public interface MainActivityComponent {
public MainActivity injectActivity(MainActivity);
}
public class MainActivity extends Activity {
public void onCreate(Bundle saved){
super.onCreate();
injectDependencies();
}
protected void injectDependencies(){
Dagger_MainActivityComponent
.builder()
.mainActivityComponent( new MainActivityModule())
.build()
.injectActivity(this);
}
}
So far so good, that works like expected. Now I want to write some unit tests (not android instrumentation tests) for MainActivity where I want to use TestMainActivityModule instead of MainActivityModule.
#Module (overrides = true )
public class TestMainActivtiyModule extends MainActivityModule {
#Provides public HttpClient(){
return new MockHttpClient();
}
}
My question is: How do I force MainActivity to use TestMainActivitiyModule instead of MainActivityModule? Is there a good solution for that?
My current approach is to use inheritance and to override getModule(), something like this
public class TestMainActivity extend MainActivity {
#Override
protected void injectDependencies(){
Dagger_MainActivityComponent
.builder()
.mainActivityComponent( new TestMainActivtiyModule())
.build()
.injectActivity(this);
}
}
and to run unit test against TestMainActivity instead of MainActivity.
I guess it works, but one of the problems I'm facing with this approach is that I can't start TestMainActivity with an Intent because I can't specify it in AndroidManifest.xml
Does anyone know a better approach for unit testing with dagger2 on android?
The approach I've started using has involved maintaining two modules (one for the app, one for testing) in parallel build variants (ex: app and integration). Still not sure how well that solution scales so YMMV. I'd be very happy to see a better solution!
This is also a great read: http://engineering.circle.com/instrumentation-testing-with-dagger-mockito-and-espresso/
I would really suggest you to check this boilerplate since it is fully based on DI using Dagger2. It also shows how you can replace your dependencies in the test environment in a very neat way.
The dependencies currently handled by the boiler plate are the following:
Database dependency: encapsulates all the database operations.
Shared preferences dependency: deals with shared preferences.
Local files dependency: which deals with saving on files.
Analytics dependency: covers all the operation of reporting events to your analytics backend (GA, Segment, FB, Flurry ..)
Logging dependency: encapsulates all the operations related to logging to your console
Api dependency: encapsulates all the API related operations
The power of dependency injection comes really handy especially for testing since you can easily switch your dependencies in the test environment to dummy dependencies.
Related
I cannot get my head around how to setup a couple of things when porting the code from Dagger 2 to Android Dagger 2.11. In Dagger 2 the setup was something like this:
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
this.initializeInjector();
}
private void initializeInjector() {
//this class should be auto-generated by Dagger on build
this.applicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.netModule(new NetModule())
.build();
}
public ApplicationComponent getApplicationComponent() {
return this.applicationComponent;
}
}
You then had access to the applicationComponent and you could inject any object by using the:
getApplicationComponent().inject(MyCustomObject);
In Android Dagger 2.11 you don't do that anymore. For Activities and Fragments you can inject with the AndroidInjector.inject() method but what about other types? Let's look at an example below. We have a JobManager that can post jobs. The jobs are persisted to a file and when they should be posted they are first deserialized. The problem is that its dependencies are not set of course. So the question is: How to do that?
public class JobManager {
private Context context;
#Inject
public JobManager(Context context) {
this.context = context;
}
public void postJob(String jobId) {
MyJob myJob = deserializePersistedJobFromFile(jobId);
//((App) context).getApplicationComponent().inject(myJob); //This was the old way of doing injection
AndroidInjector.inject(myJob); //This doesn't work - what to do now?
}
.
.
.
}
public class MyJob {
#Inject
ApiService apiService;
.
.
.
}
You can get Dagger to inject a MembersInjector<T> and then use that to inject dependencies into your own objects...
public class JobManager {
private Context context;
#Inject
public JobManager(Context context, MembersInjector<MyJob> jobInjector) {
this.context = context;
}
public void postJob(String jobId) {
MyJob myJob = deserializePersistedJobFromFile(jobId);
jobInjector.inject(myJob);
}
.
.
.
}
Is there any problem you experience with the "old" approach that makes you want to move to the "new" one?
I couldn't find one single real advantage in performing dependency injection using the static AndroidInjector class, but it does increase the complexity of the code.
Therefore, if you don't have a very specific reason to move in that direction, I would suggest to stay with the "old" working approach.
As for injection into non-Activity/Fragment classes, I think you shouldn't use DI framework for this. Use Dagger for injection into Application, Activity, Fragment and Service only. More information available in this post: Dependency Injection in Android.
Writing this on a phone so apologies for any typos.
TL;DR Would it not make sense that, MyJob needs an #Inject(ed) constructor that takes the ApiService, rather than a member injection? Is there a reason why MyJob can't have it's own constructor? (Unfamiliar if this is an Android SDK class or not). If that's not the answer then I have another observation that your JobManager seems to do what the JobScheduler does? Unless it's simply a confusion of terms (i.e. both "jobs")
Outside of Android and it's lifecycle uniqueness dependency inversion's most common use is constructor injection. Learning the DI pattern within the Android framework isn't the best introduction and you get caught up in the 'lying to the complier' DI framework functionality that is meant to simply help move off a legacy codebase (c.f. Dagger 1's static injection or Guide's 'private' member injection).
As a side note, I have avoided moving to "Android Dagger" since it seems to go against having modular separation of code. I think I have more to learn here but right now the boilerplate distruption advantage of "Android Dagger" is outweighed by my need for loosely coupled feature modules to support Instant Apps and an altogether more modular code base.
Hope that helps a bit.
I wrote this article which should be helpful to you. It links to the sample code on Github.
https://proandroiddev.com/exploring-the-new-dagger-android-module-9eb6075f1a46
Pay close attention to the AppModule and how it keeps track of the application so you can pass application.getApplicationContext() into constructors for your JobManager
Using dagger android library has advantages :
1) Using dagger violates dependency injection principle that classes shouldn't know about how dependencies are injected, this violation can be fixed using dagger android library
2) Dagger code can be decoupled from android components.
3) Clean code can be achieved as you don't need to initialize dagger components and call inject methods in android components, leading to creation of easy to maintain apps.
You can decouple dagger from android using dagger android components such as DaggerActivity,DaggerApplication, and DaggerFragment and moving AndroidInjection.inject() calls to central location, instead of calling in each and every activity and fragment, by using activity lifecycle callback on application.
You can read detailed explanation about dagger android at
http://www.zoftino.com/android-framework-classes-dependency-injection-using-dagger-android
I'm working on setting up Dagger 2 into my android project. It is my first time with this framework and everything goes well so far. But I'm seeing different approaches on the way you can set up this framework in your project and I wonder which one is better, because I compare both and for me the result is kind of the same.
I followed this guide: https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2
Searching on Internet all of them use this approach.
It use #Module and #Component to define the dependencies.
and your application ends up like this:
public class MyApp extends Application {
private NetComponent mNetComponent;
#Override
public void onCreate() {
super.onCreate();
// Dagger%COMPONENT_NAME%
mNetComponent = DaggerNetComponent.builder()
// list of modules that are part of this component need to be created here too
.appModule(new AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
.netModule(new NetModule("https://api.github.com"))
.build();
// If a Dagger 2 component does not have any constructor arguments for any of its modules,
// then we can use .create() as a shortcut instead:
// mNetComponent = com.codepath.dagger.components.DaggerNetComponent.create();
}
public NetComponent getNetComponent() {
return mNetComponent;
}
}
But I found another way (I haven't tested it):https://google.github.io/dagger/android.html
And It looks like completely different, using different classes and annotations.
It uses something like this:
#Subcomponent(modules = ...)
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
}
#Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
#Binds
#IntoMap
#ActivityKey(YourActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
}
#Component(modules = {..., YourActivityModule.class})
interface YourApplicationComponent {}
public class YourApplication extends Application implements HasDispatchingActivityInjector {
#Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerYourApplicationComponent.create()
.inject(this);
}
#Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
}
So, my questions are:
which one is better?
What are the reasons for choosing one approach instead of the other?
The method of setting up Dagger 2 for Android that is now prescribed in official Dagger 2 documentation has a number of advantages and should be preferred. The advantages are just those elaborated there, namely:
Copy-pasting code makes it hard to refactor later on. As more and more developers copy-paste that block, fewer will know what it actually does.
More fundamentally, it requires the type requesting injection (FrombulationActivity) to know about its injector. Even if this is done through interfaces instead of concrete types, it breaks a core principle of dependency injection: a class shouldn’t know anything about how it isinjected.
Let's apply those reasons to your first example.
Reason 1
Assume we have an Activity that wants to use your NetComponent. Let's call it NetActivity. The onCreate(Bundle savedInstanceState) method of that NetActivity will look something like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApp) getApplicationContext()).getNetComponent().inject(this);
}
This code has all the visual appeal of toenail clippings scattered on oatmeal (not my simile) and will end up copy-pasted in all of the injection site Activities where you use NetComponent. If you use more complicated components, such as this example from the docs:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// DO THIS FIRST. Otherwise frombulator might be null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ... now you can write the exciting code
}
Even worse. It can easily degenerate into a magical piece of code that must be copied and pasted throughout the injection sites. If it changes, it's easy to forget to update just one site and have your app crash.
Reason 2
One of the great advantages of dependency injection is injection sites need not know or care about their injectors, just as dependencies do not know or care about their dependents. To return to ourNetActivity, we have:
((MyApp) getApplicationContext()).getNetComponent().inject(this);
The Activity "knows" about its injector (NetComponent), and the Activity is now coupled with a concretion MyApp and the method getNetComponent() from the same. If either of these classes changes, NetActivity will have to change as well.
The advantages of following the new way of injection inside Activity and Fragments available in Dagger versions 2.10 and forward are just the opposite of these disadvantages:
You end up with less copy-paste code
Types requesting injection no longer have to know or care about their injectors or the sources of their injectors.
Furthermore, as pointed out in this blog, preferring subcomponents over dependent components reduces the method count of your app.
While using subcomponents may initially seem more difficult, there are some clear advantages. However, for the purposes of learning Dagger dependent components may be initially easier to understand. If the second example is too complicated initially, you can graduate to the preferred method when you have gained finesse.
For 'regular' Java project overriding the dependencies in the unit tests with mock/fake ones is easy. You have to simply build your Dagger component and give it to the 'main' class that drives you application.
For Android things are not that simple and I've searched for a long time for decent example but I was unable to find so I had to created my own implementation and I will really appreciate feedback is this a correct way to use Dagger 2 or there is a simpler/more elegant way to override the dependencies.
Here the explanation (project source can be found on github):
Given we have a simple app that uses Dagger 2 with single dagger component with single module we want to create android unit tests that use JUnit4, Mockito and Espresso:
In the MyApp Application class the component/injector is initialized like this:
public class MyApp extends Application {
private MyDaggerComponent mInjector;
public void onCreate() {
super.onCreate();
initInjector();
}
protected void initInjector() {
mInjector = DaggerMyDaggerComponent.builder().httpModule(new HttpModule(new OkHttpClient())).build();
onInjectorInitialized(mInjector);
}
private void onInjectorInitialized(MyDaggerComponent inj) {
inj.inject(this);
}
public void externalInjectorInitialization(MyDaggerComponent injector) {
mInjector = injector;
onInjectorInitialized(injector);
}
...
In the code above:
Normal application start goes trough onCreate() which calls initInjector() which creates the injector and then calls onInjectorInitialized().
The externalInjectorInitialization() method is ment to be called by the unit tests in order to set the injector from external source, i.e. a unit test.
So far, so good.
Let's see how the things on the unit tests side looks:
We need to create MyTestApp calls which extends MyApp class and overrides initInjector with empty method in order to avoid double injector creation (because we will create a new one in our unit test):
public class MyTestApp extends MyApp {
#Override
protected void initInjector() {
// empty
}
}
Then we have to somehow replace the original MyApp with MyTestApp. This is done via custom test runner:
public class MyTestRunner extends AndroidJUnitRunner {
#Override
public Application newApplication(ClassLoader cl,
String className,
Context context) throws InstantiationException,
IllegalAccessException,
ClassNotFoundException {
return super.newApplication(cl, MyTestApp.class.getName(), context);
}
}
... where in newApplication() we effectively replace the original app class with the test one.
Then we have to tell the testing framework that we have and want to use our custom test runner so in the build.gradle we add:
defaultConfig {
...
testInstrumentationRunner 'com.bolyartech.d2overrides.utils.MyTestRunner'
...
}
When a unit test is run our original MyApp is replaced with MyTestApp. Now we have to create and provide our component/injector with mocks/fakes to the app with externalInjectorInitialization(). For that purpose we extends the normal ActivityTestRule:
#Rule
public ActivityTestRule<Act_Main> mActivityRule = new ActivityTestRule<Act_Main>(
Act_Main.class) {
#Override
protected void beforeActivityLaunched() {
super.beforeActivityLaunched();
OkHttpClient mockHttp = create mock OkHttpClient
MyDaggerComponent injector = DaggerMyDaggerComponent.
builder().httpModule(new HttpModule(mockHttp)).build();
MyApp app = (MyApp) InstrumentationRegistry.getInstrumentation().
getTargetContext().getApplicationContext();
app.externalInjectorInitialization(injector);
}
};
and then we do our test the usual way:
#Test
public void testHttpRequest() throws IOException {
onView(withId(R.id.btn_execute)).perform(click());
onView(withId(R.id.tv_result))
.check(matches(withText(EXPECTED_RESPONSE_BODY)));
}
Above method for (module) overrides works but it requires creating one test class per each test in order to be able to provide separate rule/(mocks setup) per each test. I suspect/guess/hope that there is a easier and more elegant way. Is there?
This method is largely based on the answer of #tomrozb for this question. I just added the logic to avoid double injector creation.
1. Inject over dependencies
Two things to note:
Components can provide themselves
If you can inject it once, you can inject it again (and override the old dependencies)
What I do is just inject from my test case over the old dependencies. Since your code is clean and everything is scoped correctly nothing should go wrong—right?
The following will only work if you don't rely on Global State since changing the app component at runtime will not work if you keep references to the old one at some place. As soon as you create your next Activity it will fetch the new app component and your test dependencies will be provided.
This method depends on correct handling of scopes. Finishing and restarting an activity should recreate its dependencies. You therefore can switch app components when there is no activity running or before starting a new one.
In your testcase just create your component as you need it
// in #Test or #Before, just inject 'over' the old state
App app = (App) InstrumentationRegistry.getTargetContext().getApplicationContext();
AppComponent component = DaggerAppComponent.builder()
.appModule(new AppModule(app))
.build();
component.inject(app);
If you have an application like the following...
public class App extends Application {
#Inject
AppComponent mComponent;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().appModule(new AppModule(this)).build().inject(this);
}
}
...it will inject itself and any other dependencies you have defined in your Application. Any subsequent call will then get the new dependencies.
2. Use a different configuration & Application
You can chose the configuration to be used with your instrumentation test:
android {
...
testBuildType "staging"
}
Using gradle resource merging this leaves you with the option to use multiple different versions of your App for different build types.
Move your Application class from the main source folder to the debug and release folders. Gradle will compile the right source set depending on the configuration. You then can modify your debug and release version of your app to your needs.
If you do not want to have different Application classes for debug and release, you could make another buildType, used just for your instrumentation tests. The same principle applies: Duplicate the Application class to every source set folder, or you will receive compile errors. Since you would then need to have the same class in the debug and rlease directory, you can make another directory to contain your class used for both debug and release. Then add the directory used to your debug and release source sets.
There is a simpler way to do this, even the Dagger 2 docs mention it but they don't make it very obvious. Here's a snippet from the documentation.
#Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
The Thermosiphon implements Pump and wherever a Pump is requested Dagger injects a Thermosiphon.
Coming back to your example. You can create a Module with a static boolean data member which allows you to dynamically switch between your real and mock test objects, like so.
#Module
public class HttpModule {
private static boolean isMockingHttp;
public HttpModule() {}
public static boolean mockHttp(boolean isMockingHttp) {
HttpModule.isMockingHttp = isMockingHttp;
}
#Provides
HttpClient providesHttpClient(OkHttpClient impl, MockHttpClient mockImpl) {
return HttpModule.isMockingHttp ? mockImpl : impl;
}
}
HttpClient can be the super class which is extended or an interface which is implemented by OkHttpClient and MockHttpClient. Dagger will automatically construct the required class and inject it's internal dependencies just like Thermosiphon.
To mock your HttpClient, just call HttpModule.mockHttp(true) before your dependencies are injected in your application code.
The benefits to this approach are:
No need to create separate test components since the mocks are injected at a module level.
Application code remains pristine.
I'm currently developing an Android MVP Application, and I'm trying to separate my dependencies in different Dagger2 Modules.
The problem I'm having is about changing a module in Unit Test Time. The scenario is the following:
LoginComponent, which uses two modules: LoginModule and HTTPModule
LoginModule in one of its methods requires an OkHttp instance, which is provided by HTTPModule.
The code is the following:
#Singleton
#Component(modules = {LoginModule.class, HTTPModule.class})
public interface LoginComponent {
}
#Module(includes = {HTTPModule.class})
public class LoginModule {
#Provides
#Singleton
public MyThing provideMyThing(OkHttpClient client) {
// Do things with it
}
}
#Module
public class HTTPModule {
#Provides
#Singleton
public OkHttpClient provideOkHttpClient(){
// Return the OkHttpClient
}
}
The thing is, at test time I would need to change the OkHttpClient that is returned (by making it accept all the certificates, as when I run it on the JVM it does not accept the LetsEncrypt certificate).
Also I would need that because I need to declare that MyTest.class can be injected with module, and as MyTest.class is under the app/src/test/ folder, it's not visible for the classes that are placed under app/src/main/. What I've done until now is to copy and paste the Component and the modules to the /test/ folder, and make the injected class declaration there. But I know there must be a proper way to achieve what I'm looking for.
Another thing I've tried is annotating the methods with custom Scopes (creating a #TestScope annotation). However this leads me to the same problem that I had commented before: I cannot make the MyTest.class visible to the component, because it's placed under the /test/ folder.
I've already checked other similar questions, such as this one and this another one, but this last one is for running tests with Robolectric, and by now I'm able to unit test most of my code with JUnit4 only (Android Studio 2-Beta 8).
If anyone could point me to the right direction, I would be more than grateful.
Thanks in advance!
You're using Dependency injection in a way that still keeps your code tightly coupled. Generally speaking you want your dependencies to be interfaces instead of actual classes. This keeps your code nice and loose, easy to read, modify and maintain.
Hide your network operations behind an interface to allow you to modify the network implementation whenever you need to. This could be done for testing - in your case, but it will also allow you to switch out the network library if you'll want to or need to in the future without changing any other code.
Try something like this:
#Module
public class HTTPModule {
#Provides
#Singleton
public NetworkProvider provideNetworkProvider(){
// Return the Network provider
}
}
The network abstraction layer:
public interface NetworkProvider {
// Methods to send requests and receive async responses
}
The OkHttp implementation:
public class OkHttpNetworkProvider implements NetworkProvider {
// Implement NetworkProvider. This is the only class that
// knows about OkHttp and its components
}
Now you can create a mock version of NetworkProvider and use it for testing, whether via a test module or directly.
I have a library project/module that is used by both Android apps and regular java apps.
In Dagger 1 this project/module has property complete = false. Within there is an #Inject field that is not satisfied by any class implementation or #Provides method. The idea is to force the "top" module(s) which has complete = true to provide system specific implementation
Just for the sake of example: In the library project I have ActLogin activity that have field #Inject #Named("app version") mAppVersion. The value of this field is used when logging in into a server. ActLogin is used by several apps that use this library. Each app's module has complete = true and provides value with #Provides #Named("app version") provideAppVersion()
Documentation for migration of Dagger 2 (http://google.github.io/dagger/dagger-1-migration.html) states:
Dagger 2 modules are all declared as complete = false and library = true
and in the same time the "main" documentation page (http://google.github.io/dagger/) states:
The Dagger annotation processor is strict and will cause a compiler error if any bindings are invalid or incomplete.
The latter is obviously the correct one because when trying to build with unsatisfied inject error is produced (error: java.lang.String cannot be provided without an #Provides- or #Produces-annotated method).
The question is: is it possible to migrate this approach (deferring providing inject) to Dagger 2 and how?
P.S. Initially I thought as a dirty workaround to provide some dummy values in the library's #Module but then again - you cannot have module overrides in Dagger 2 (which is kind of WTF(!!!). Module overrides were the most useful feature for me when creating unit tests). Probably I am missing something very basic and I hope that someone can point it out :-).
It turns out that there is dedicated construct for this but it takes some time to find it out.
If you need to have a component that have a module which contains unsatisfied inject(s) - make it #Subcomponent. As documentation clearly states:
That relationship allows the subcomponent implementation to inherit the entire binding graph from its parent when it is declared. For that reason, a subcomponent isn't evaluated for completeness until it is associated with a parent
So in my case, my library project needs to be a dagger subcomponent. When I use it in my app project, my app dagger component have to include the lib subcomponent.
In code:
The library subcomponent:
#Subcomponent(modules = Mod1.class)
public interface MyLibraryComponent {
void inject(Mod1Interface1 in);
}
The app component:
#Component(modules = Mod2.class)
#Singleton
public interface MyAppComponent {
void inject(MainActivity act);
MyLibraryComponent newMyLibraryComponent();
}
Please note the MyLibraryComponent newMyLibraryComponent(); - that is how you tell dagger that your component contains that subcomponent.
Graph instantiation:
MyAppComponent comp = DaggerMyAppComponent.builder().build();
Please note that contrary to using component composition with dependencies (#Component's property) in this case you don't have to "manually" construct your subcomponent. The component will "automatically" take care for that in case the subcomponent's modules don't need special configuration (i.e. constructor parameters). In case some subcomponent's module requires configuration you do it trough the component instantiation like this:
MyAppComponent comp = DaggerMyAppComponent.builder().
mod2(new Mod2SpecialConfiguration()).
build();
For android there is a special twist if your library project contains activities because each activity have to be separately injected "on demand" contrary to regular java application where you usually inject the whole application once at start up time.
For the sake of example let's say our library project contains login activity "ActLogin" that we use as common for several applications.
#Subcomponent(modules = Mod1.class)
public interface MyLibraryComponent {
void injectActLogin(ActLogin act);
void inject(Mod1Interface1 in);
}
The problem is that in Android we usually create our dependency graph in the Application object like this:
public class MyApplication extends Application {
private MyAppComponent mAppDependencyInjector;
#Override
public void onCreate() {
super.onCreate();
mAppDependencyInjector = DaggerMyAppComponent.builder().build();
}
public MyAppComponent getAppDependencyInjector() {
return mAppDependencyInjector;
}
}
and then in your activity you use it like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
((MyApplication) getApplication()).getAppDependencyInjector().inject(this);
// ...
}
but our ActLogin activity is part of the library project (and dagger component) that is not event aware of what application will it be used in so how are we going to inject it?
There is a nice solution but please note that I am not sure it is canonical (i.e. it is not mentioned in the documentation, it is not given as an example by the "authorities" (afaik))
Project's source can be found at github.
First you will have to extend the library dagger component in you app component:
public interface MyAppComponent extends MyLibraryComponent {
That way your app component will contain all the inject methods from the subcomponent so you will be able to inject it's activities too. After all, top component is in fact the whole object graph (more precisely the Dagger generated DaggerMyAppComponent represent the whole graph) so it is able to inject everything defined in itself + in all subcomponents.
Now we have to assure that the library project is able to access it. We create a helper class:
public class MyLibDependencyInjectionHelper {
public static MyLibraryComponent getMyLibraryComponent(Application app) {
if (app instanceof MyLibraryComponentProvider) {
return ((MyLibraryComponentProvider) app).getMyLibraryComponent();
} else {
throw new IllegalStateException("The Application is not implementing MyLibDependencyInjectionHelper.MyLibraryComponentProvider");
}
}
public interface MyLibraryComponentProvider {
MyLibraryComponent getMyLibraryComponent();
}
}
then we have to implement MyLibraryComponentProvider in our Application class:
public class MyApplication extends Application implements
MyLibDependencyInjectionHelper.MyLibraryComponentProvider {
// ...
#Override
public MyLibraryComponent getMyLibraryComponent() {
return (MyLibraryComponent) mAppDependencyInjector;
}
}
and in ActLogin we inject:
public class ActLogin extends Activity {
#Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
// ...
MyLibDependencyInjectionHelper.getMyLibraryComponent(getApplication()).
injectActLogin(this);
// ...
}
}
There is a problem with this solution: If you forget to implement the MyLibraryComponentProvider in your application you will not get an error at compile time but at runtime when you start ActLogin activity. Luckily that can be easily avoided with simple unit test.