Issue
This Google Testing Blog post lists some strategies for making code testable. One item says in part:
Ask for things, Don't look for things (aka Dependency Injection / Law of Demeter): OK, you got rid of your new operators in you application code. But how do I get a hold of the dependencies. Simple: Just ask for all of the collaborators you need in your constructor.
In other words, do this:
Foo(final Bar bar) {
mBar = bar;
}
Not this:
Foo() {
mBar = Bar.getBar(); // or new Bar();
}
The reason for this is obvious: it lets you test Foo
by passing it a mock Bar
. Since Android components require no-arg constructors, the equivalent is to pass their arguments via an extras Bundle
.
How do you apply this principle in Android when the things the component needs are not Parcelable
or Serializable
?
Solution
What I use for that is Dagger2, where you only depend on the object graph (or one of its subscoped extended subgraphs) to receive all your dependencies.
Vaguely it works like this,
- Singleton
.
@Component(modules={SingletonModule.class})
@Singleton
public interface SingletonComponent {
Foo foo();
Bar bar();
void inject(MainActivity mainActivity);
}
@Module
public class SingletonModule {
@Provides
@Singleton
public Bar bar() {
return new Bar();
}
@Provides
@Singleton
public Foo foo(Bar bar) {
return new Foo(bar);
}
}
public class CustomApplication extends Application {
SingletonComponent singletonComponent;
@Override
public void onCreate() {
super.onCreate();
singletonComponent = DaggerSingletonComponent.builder()
.singletonModule(new SingletonModule())
.build();
}
public SingletonComponent getSingletonComponent() {
return singletonComponent;
}
}
public class MainActivity extends Activity {
@Inject
Foo foo;
@Inject
Bar bar;
@Override
public void onCreate(Bundle saveinstanceState) {
super.onCreate(saveinstanceState);
((CustomApplication)getApplicationContext()).getSingletonComponent().inject(this);
bar.doSomething();
foo.doSomething();
}
}
- Subscoping
.
@Component(modules=SingletonModule.class)
@Singleton
public interface SingletonComponent {
Foo foo();
}
@Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
@ActivityScope
public interface MainActivityCompoent extends SingletonComponent {
Bar bar();
void inject(MainActivity mainActivity);
}
@Module
public class SingletonModule {
@Provides
@Singleton
public Foo foo() {
return new Foo();
}
}
@Module
public class MainActivityModule {
@Provides
@ActivityScope
public Bar bar(Foo foo) {
return new Bar(foo);
}
}
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
public class CustomApplication extends Application {
SingletonComponent singletonComponent;
@Override
public void onCreate() {
super.onCreate();
singletonComponent = DaggerSingletonComponent.builder()
.singletonModule(new SingletonModule())
.build();
}
public SingletonComponent getSingletonComponent() {
return singletonComponent;
}
}
public class MainActivity extends Activity {
@Inject
Foo foo;
@Inject
Bar bar;
private MainActivityComponent mainActivityComponent;
@Override
public void onCreate(Bundle saveinstanceState) {
super.onCreate(saveinstanceState);
mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(((CustomApplication)getApplicationContext()).getSingletonComponent())
.mainActivityModule(new MainActivityModule())
.build();
mainActivityComponent.inject(this);
bar.doSomething();
foo.doSomething();
}
}
Answered By - EpicPandaForce
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.