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.