HI I come from a background C#/.NET and have learned to work with a bit of Android.Now I am wana start to build a small app for fun and I tought to learn an IoC framework.After a bit of googeling I found roboguice.But I can not figure out how to integrate it.On .
NET I have worked with Ninject and Unity , and am looking to create a similar form of constructor injection that I got from those frameworks.
Here is what I have so far and what I think I have figured out:
This class represent the app bootstrapper and here is where I will register my dependency config class:
public class IOCApplication extends Application{
#Override
public void onCreate(){
super.onCreate();
RoboGuice.setBaseApplicationInjector(this, RoboGuice.DEFAULT_STAGE, RoboGuice.newDefaultRoboModule(this), new IOCModule());
}
}
This is the Dependency config class:
public class IOCModule implements Module{
#Override
public void configure(com.google.inject.Binder binder) {
binder.bind(ITest.class).to(Test.class);
}
}
In my AndroidManifest I have added this:
<application ... android:name="com.example.project2.IOCApplication">
This part I do not realy understad why I had to add but I am thinking it's something to tell Android that IOCApplication should be isntantiated first.
This is the clas my MainActivily class and I have added a constructor for it:
public ITest test;
public MainActivity(ITest test){
this.test = test;
}
When I try to runthis on my android device it kind of looks like the app is entering an infinite loop and I do not think ITest get's instantiated.
What am I doing wrong?
One thing to know with Android is that you do not instantiate your own activities, it is the system which does.
Because of this, you cannot use constructor injection with activities.
However, you can use attribute injection, which is cleaner IMO.
Extending the RoboActivity class is the easiest way to use injection with Activity.
RoboGuice provides similar classes for other Android components (RoboFragment, RoboService, etc.)
public class MyActivity extends RoboActivity {
#Inject
ITest test;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// You can now use your test instance
}
}
All attribute with the #Inject will be instantiated after super.onCreate(savedInstanceState); is called.
With POJO (Plain Old Java Object), you have more options:
Attribute injection
public class Test {
#Inject
private Service1 service1;
#Inject
private Service2 service2;
public Test() {
}
}
Constructor injection
public class Test {
private Service1 service1;
private Service2 service2;
#Inject
public Test(Service1 service1, Service2 service2) {
this.service1 = service1;
this.service2 = service2;
}
}
Note that your constructor must have the #Inject annotation if it has arguments.
You need this line <application ... android:name="com.example.project2.IOCApplication"> to tell the system that you are using an extended Application class. Without it, Android will use the base class.
I encourage you to read the official documentation for more information.
Related
I have a simple app. It uses a network API (Volley). It uses Dagger 2.11 (using AndroidInjector) for dependency inversion. The NetworkAPI is injected in the MainActivity. For the acceptance tests I use my own TestRunner, extended from AndroidJUnitRunner.
To fake the real Network API, I use an ImmediateResponseNetwork object. This 'network' responses immediately. The instance is provided by a TestNetworkApiModule and declared in the module list of the TestAppComponent. See parts of the source code below.
At the times, before I used Dagger, I used the ImmediateResponseNetwork to define different responses of the networkRequest, dependend on the test. Which seem not be possible now, because the network is hidden in the activity under test. And after creation of the network I can't access it from the outside to prepare it.
I also can't test a slower network connection. That is why I would like to at least to inject another fake slow-response-network object and I would like to have the ability to prepare the response via ImmediateResponseNetwork.
How can I do this?
An example test I would like to write is:
Testing, that the loading indicator is displayed, while an network request is running.
I have no idea how to implement this now. It looks like, that DI is in the way for easier testing, instead of the other way around.
I guess, there is something missing in my thinking.
The source code on which the question is based upon: https://github.com/Minsky/SO_Question_AndroidInjectionInTestsWithDagger
Sample Test, I want to do:
#Rule
public ActivityTestRule<MainActivity> mainActivityTestRule = new ActivityTestRule<>(MainActivity.class, true, false);
public void afterStartLoadingPleaseWaitInfoIsDisplayed() throws Exception {
MainActivityTestRule.launchActivity(new Intent());
onView(withId(R.id.activity_loading_indicator)).check(matches(isDisplayed()));
}
My relevant test setup classes:
#Module
public class TestNetworkApiModule extends NetworkApiModule {
#Provides
static NetworkApi provideNetworkApi(Context context) {
return new ImmediateResponseNetwork();
}
}
#Singleton
#Component(modules = {
AppModule.class,
TestNetworkApiModule.class,
ActivityBindingModule.class,
AndroidSupportInjectionModule.class
})
public interface TestAppComponent extends AppComponent {
void inject(TestMyApplication app);
#Override
void inject(DaggerApplication instance);
#Component.Builder
interface Builder {
#BindsInstance
TestAppComponent.Builder application(Application application);
TestAppComponent build();
}
}
public class TestMyApplication extends DaggerApplication {
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
TestAppComponent appComponent = DaggerTestAppComponent.builder().application(this).build();
appComponent.inject(this);
return appComponent;
}
}
The activity where the networkAPI is injected:
public class MainActivity extends AppCompatActivity {
private TextView responseView;
private boolean progressBarVisible;
#Inject
NetworkApi networkApi;
...
}
I am trying to use #Async annotation provided by spring. Going through some of the blogs I found there are the following constraints for using it:
It must be applied to public methods only
Self-invocation – calling the async method from within the same class – won’t work
I have a method which is getting called from the same class which I want to mark #Async. Is there any way of achieving it from the same class?
In Spring v4.3+ you can use self injection, and call the method on the self injected reference.
So for example:
#Component
public class SomeClass {
#Autowired
private SomeClass selfInjected;
public void someMethod() {
selfInjected.someOtherMethod();
}
#Async
public void someOtherMethod(){
...;
}
}
Updated as OP is using version before 4.3:
This will work for you.
#Component
public class SomeClass {
#Autowired
private ApplicationContext applicationContext;
private SomeClass selfInjected;
#PostConstruct
private void init() {
selfInjected = applicationContext.getBean(SomeClass.class);
}
}
Or
The other option is to extract the method to separate class and autowire it. I would personally explore this option before doing the above method.
The application I have been working on has been getting more and more complicated, and it's gotten to the point where I have been running into the same problems over and over again with concurrency. It no longer made any sense to solve the same problems and not have any regression tests.
That's when I found ThreadWeaver. It was really nice for some simple concurrency cases I cooked up, but I started to get frustrated when trying to do some more complicated cases with my production code. Specifically, when injecting components using Guice.
I've had a bit of a hard time understanding the implications of the way ThreadWeaver runs tests, and looked for any mention of Guice or DI in the wiki documents, but with no luck.
Is Guice compatible with ThreadWeaver?
Here is my test
#Test
public void concurrency_test() {
AnnotatedTestRunner runner = new AnnotatedTestRunner();
runner.runTests(OPYLWeaverImpl.class, OPYLSurrogateTranscodingService.class);
}
Here is my test implementation
public class OPYLWeaverImpl extends WeaverFixtureBase {
#Inject private TaskExecutor taskExecutor;
#Inject private Serializer serializer;
#Inject private CountingObjectFileMarshaller liveFileMarshaller;
#Inject private GraphModel graphModel;
#Inject private CountingModelUpdaterService updaterService;
#Inject private BabelCompiler babelCompiler;
#Inject private EventBus eventBus;
OPYLSurrogateTranscodingService service;
private Path testPath;
#ThreadedBefore
public void before() {
service = new OPYLSurrogateTranscodingService(eventBus, taskExecutor, serializer, liveFileMarshaller,
() -> new OPYLSurrogateTranscodingService.Importer(graphModel, babelCompiler, updaterService, eventBus),
() -> new OPYLSurrogateTranscodingService.Validator(eventBus, babelCompiler),
() -> new OPYLSurrogateTranscodingService.Exporter(graphModel, updaterService));
}
#ThreadedMain
public void mainThread() {
testPath = FilePathOf.OASIS.resolve("Samples/fake-powershell-unit-test.opyl");
service.applyToExistingGraphModel(testPath);
}
#ThreadedSecondary
public void secondaryThread() {
}
#ThreadedAfter
public void after() {
}
And the WeaverFixtureBase
public class WeaverFixtureBase {
#Inject protected CountingEventBus eventBus;
#Before public final void setupComponents() {
Injector injector = Guice.createInjector(new WeaverTestingEnvironmentModule(CommonSerializationBootstrapper.class));
injector.getMembersInjector((Class) this.getClass()).injectMembers(this);
}
private class WeaverTestingEnvironmentModule extends AbstractModule {
private final Class<? extends SerializationBootstrapper> serializationBootstrapper;
public WeaverTestingEnvironmentModule(Class<? extends SerializationBootstrapper> serializationConfiguration) {
serializationBootstrapper = serializationConfiguration;
}
#Override protected void configure() {
bind(TaskExecutor.class).to(FakeSerialTaskExecutor.class);
bind(SerializationBootstrapper.class).to(serializationBootstrapper);
bind(ModelUpdaterService.class).toInstance(new CountingModelUpdaterService());
bindFactory(StaticSerializationConfiguration.Factory.class);
CountingEventBus localEventBus = new CountingEventBus();
bind(Key.get(EventBus.class, Bindings.GlobalEventBus.class)).toInstance(localEventBus);
bind(Key.get(EventBus.class, Bindings.LocalEventBus.class)).toInstance(localEventBus);
bind(CountingEventBus.class).toInstance(localEventBus);
bind(EventBus.class).toInstance(localEventBus);
}
#Provides
#Singleton
public GraphModel getGraphModel(EventBus eventBus, Serializer serializer) {
return MockitoUtilities.createMockAsInterceptorTo(new GraphModel(eventBus, serializer));
}
}
But when the classloader loads OPYLWeaverImpl, none of the Guice stuff goes off and I get a big pile of nulls.
I feel like this is one of those "missing-something-really-simple" kind of scenarios. Sorry if it is!
The above comment is right. Thread-weaver is fully agnostic of JUnit. Thread weaver is its own runner that executes a test case respecting its own annotations. You must not use any JUnit-specific annotation within a Thread Weaver test.
Other than that, Thread Weaver does not need any compatibility for a specific framework. It manipulates Java byte code and loads that manipulated code using aeperate class loaders.
Finally, a Thread Weaver test without any secondary test does not make any sense. Thread weaver works by interleaving seperate execution paths. Without a second thread, Thread Weaver only steps through a single thread without adding any value.
I am trying to convert my singleton CookieUtil to be injected with Dagger into LoginActivity. CookieUtil takes an application context therefor I have set up the following structure :
AndroidModule
#Module(
injects = {
CookieUtil.class,
LoginActivity.class
},
library = true
)
public class AndroidModule {
private final App application;
public AndroidModule(App application) {
this.application = application;
}
/**
* Allow the application context to be injected but require that it be annotated with
* {#link ForApplication #Annotation} to explicitly differentiate it from an activity context.
*/
#Provides
#Singleton
#ForApplication
Context provideApplicationContext() {
return application;
}
#Provides
#Singleton
CookieUtil provideCookieUtil() {
return new CookieUtil();
}
}
CookieUtil (What I want to inject into LoginActivity)
#Singleton
public class CookieUtil {
#Inject Context mContext; // gets injected with a app context. is this right ?
private PersistentCookieStore persistentCookieStore;
private CookieUtil() {
// use injected mContext
persistentCookieStore = new PersistentCookieStore(mContext);
// ...
}
}
LoginActivity (Where I want to inject CookieUtil)
public class LoginActivity extends BaseActivity {
#Inject CookieUtil cookieUtil;
#Override
protected void onCreate(Bundle savedInstanceState) {
// use CookieUtil ...
}
}
I have also set up all the bootstraping stuff from Dagger examples to enable everything to work
BaseActivity
public class BaseActivity extends ActionBarActivity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Perform injection so that when this call returns all dependencies will be available for use.
((App) getApplication()).inject(this);
}
}
App
public class App extends Application {
private ObjectGraph graph;
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(getModules().toArray());
// ...
}
protected List<Object> getModules() {
return Arrays.asList(
new AndroidModule(this),
new CupsModule() // Another module I haven't listed
);
}
public void inject(Object object) {
graph.inject(object);
}
}
ForApplication
#Qualifier
#Retention(RUNTIME)
public #interface ForApplication {
}
When I run the code, I get
No injectable members on android.content.Context. Do you want to add
an injectable constructor? required by CookieUtil for AndroidModule
What am I doing wrong ?
Everything still looks like magic to me as I don't fully aware of how to wire everything up, so detailed explanation would be very much appreciated.
Also, would be helpful if someone could point me to an explanation on dagger modules, when does it make sense to separate into two different modules ? What logical pieces do they usually bind ?
Thanks
EDIT
Changed suggested by Christian Gruber
#Singleton
public class CookieUtil {
private Context mContext;
private PersistentCookieStore persistentCookieStore;
#Inject
public CookieUtil(Context context) {
// use injected context
mContext = context
persistentCookieStore = new PersistentCookieStore(mContext);
// ...
}
}
Cookie cannot have a private constructor and still be created (provisioned) by Dagger. You can have a package-friendly constructor, and then it should work without #Provides CookiUtil .... Having the provides method for a class you control and could make injectable seems wasteful.
Generally speaking, Dagger considers a "binding" according to a "key" which is its type (with concrete type parameters, such as Foo<Bar>) along with any #Qualifier. So the type Foo<Bar> is different from #ForApplication Foo<Bar>. A binding is requested wherever #Inject occurs, and is supplied wherever #Provides occurs (or for unqualified bindings, if a class has an #Inject-marked constructor or fields, then an implicit binding is supplied. For every #Inject field or constructor parameter there must be a present binding for that key. Dagger needs to be able to see the methods it uses to create things, so generally private fields, methods, and constructors are not possible.
Also, please never simply inject Context without a qualifier. Or better, inject Application or Activity if that's the Context subtype you mean. Your graph WILL be impossible to manage if you don't distinguish between the 30,000 things that Android calls Context subtypes. (snark)
In most cases I have a lot of components which are having the same classes to be injected by an OSGi Declarative Service. The services will be used to execute some logic which is the same for all derived components. Therefore to avoid duplicated code it would be the best to use abstract classes. Is there any possibility to move the DI reference methods (set/unset) to an abstract class. I'm using Bnd.
For Example:
#Component
public class B implements IA {
private ServiceC sc;
#Reference
public void setServiceC(ServiceC sc) {
this.sc = sc;
}
public void execute() {
String result = executeSomethingDependendOnServiceC();
// do something with result
}
protected String executeSomethingDependendOnServiceC() {
// execute some logic
}
}
#Component
public class D implements IA {
private ServiceC sc;
#Reference
public void setServiceC(ServiceC sc) {
this.sc = sc;
}
public void execute() {
String result = executeSomethingDependendOnServiceC();
// do something different with result
}
protected String executeSomethingDependendOnServiceC() {
// execute some logic
}
}
I want to move the setter for ServiceC and the method executeSomethingDependendOnServiceC() to an abstract class. But how does it look like in OSGi in connection with Bnd annotation. Just annotate the class with #Component is not working, because A and D will create different instances of the abstract class and the #Component is alsp creating an instance.
Maybe someone experience the same problem and give me some advices how a workaround could look like. At least a best practice solution would be fine as well :)
The DS annotations must be on the class being instantiated for the component. Annotations on super classes are not supported. There is a proposal to change the in a future spec release.
What you can do is move the method to the super class, but you will need to trivially override the method in the subclass so that you can annotate it in the subclass.