Injected fields null in RoboGuice 2.0 - android

I'm new to Roboguice, and I'd like to use it in my new Android application.
I have a test Activity that extends RoboActivity.
public class MainActivity extends RoboActivity {
#Inject
private TestService testService;
....
}
And here is my TestService class:
public class TestService {
#Inject
private TestDao testDao;
#Inject
protected static Provider<Context> contextProvider;
public TestService(){
Log.d("TEST_SERVICE", "Constructor test");
}
public Test getById(Integer id) throws Exception{
return testDao.queryForId(id);
}
}
I'm hoping that the #Injected annotated field inside and injected Class will be injected!
TestService is injected by MainActivity. But TestDao is null and also my contextProvider!
I've also defined a roboguice.xml file which defines my IoCModule class:
public class IoCModule extends AbstractModule{
#Override
protected void configure() {
bind(TestDao.class).to(TestDaoOrm.class);
}
}
I don't know why the inner #Inject won't work!!
Thank you for any suggestion!!
Thank you
Marco

I ve solved putting in my modules definition
requestStaticInjection( TestDaoOrm.class );

Related

Hilt: Why is a class in ApplicationComponent scope created twice?

I'm using Hilt in a multi-module project and have a module with ApplicationComponent scope. I would expect everything provided by this module to be like a singleton and only created once during the lifetime of the application. However, I see the constructor of a provided class get called once in each activity. Here is a test setup:
MyModule.java:
#Module
#InstallIn(ApplicationComponent.class)
public abstract class MyModule {
#Binds
public abstract Foo providesDomain(MyFoo domain);
}
MyApplication.java:
#HiltAndroidApp
public class MyApplication extends MultiDexApplication {
#Override
public void onCreate() {
super.onCreate();
}
}
Each activity:
#AndroidEntryPoint
public class MyActivity extends AppCompatActivity {
#Inject
Foo foo;
MyFoo.java:
class MyFoo implements Foo {
#Inject
public MyFoo(#NonNull Application application) {
Log.w("TEST", "My application: " + application);
}
}
For each activity, the MyFoo constructor is called, but the application is the same. My understanding is that this should not happen with a module that is in ApplicationComponent scope. Why is MyFoo being created again?
I'm going to point myself to the relevant Android documentation: The class itself needs to be annotated with the scope.
In the example above, MyFoo is only on the application scope if it is annotated like this:
#Singleton
class MyFoo implements Foo {
#Inject
public MyFoo(#NonNull Application application) {
Log.w("TEST", "My application: " + application);
}
}

Dagger 2 - Cannot inject members into raw type

I need to have three levels of inheritance and inject with dagger2
1.MainActivity
--1.1 MainSubActivity
----1.1.1 MainSubActivityOne
----1.1.2 MainSubActivityTwo (The same structure as MainSubActivityOne)
MainActivity
public abstract class MainActivity<T extends MainPresenter> extends BaseActivity implements MainView{
#Inject
protected T mPresenter;
}
MainPresenter
public abstract class MainPresenter<T extends MainView> extends BasePresenter<T> { ... }
MainView
public interface MainView extends BaseView{ ... }
-- MainSubActivity
public abstract class MainSubActivity extends MainActivity<MainSubPresenter> implements MainSubView { ... }
-- MainSubPresenter
public abstract class MainSubPresenter<T extends MainSubView> extends MainPresenter<T> { ... }
-- MainSubView
public interface MainSubView extends MainView { ... }
---- MainSubActivityOne (Same as MainSubActivityTwo):
public class MainSubActivityOne extends MainSubActivity implements MainSubViewOne{
#Override
protected void onCreatePresenter(){
mPresenter.setView(this);
mPresenter.onCreate();
}
#Override
protected void initializeDagger() {
getActivityComponent().inject(this);
}
}
---- MainSubPresenterOne (Same as MainPresenterTwo):
public class MainSubPresenterOne extends MainSubPresenter<MainSubViewOne> { ... }
---- MainSubViewOne (Same as MainSubViewTwo):
public interface MainSubViewOne extends MainSubView { ... }
ActivityComponent
#PerActivity
#Component(dependencies = ApplicationComponent.class, modules =
{ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
ActivityModule
#Provides
#PerActivity
MainPresenter provideMainPresenter() {
return new MainSubPresenterOne();
}
When I had only two levels, all is ok, but now I obtain this error:
...components/ActivityComponent.java:90: error: [Dagger/MembersInjection] Cannot inject members into raw type com.example.main.MainActivity
void inject(MainActivity mainActivity);
^
com.example.main.MainActivity is injected at
...components.ActivityComponent.inject(com.example.main.MainActivity)
If I change the activityComponent to:
void inject(MainSubActivityOne activity);
void inject(MainSubActivityTwo activity);
I obtain the next error instead:
.../components/ActivityComponent.java:92: error: [Dagger/MissingBinding] com.example.main.MainSubPresenterOne cannot be provided without an #Provides-annotated method.
void inject(MainSubActivityOne mainActivity);
^
com.example.main.MainSubPresenter is injected at
com.example.main.MainActivity.mPresenter
com.example.main.MainSubPresenterOne is injected at
...components.ActivityComponent.inject(com.example.main.MainSubActivityOne)
This line is your problem:
void inject(MainActivity mainActivity);
MainActivity<T> needs a generic type argument, but that's irrelevant. It's an abstract class. You're not injecting this common parent class. You're injecting the instances of its concrete children. Here's what you should do instead:
void inject(MainSubActivityOne activity);
void inject(MainSubActivityTwo activity);
[Dagger/MissingBinding] com.example.main.MainSubPresenterOne cannot be provided without an #Provides-annotated method.
This is all true. Your MainSubActivityOne expects a MainSubPresenterOne here:
#Inject
protected T mPresenter;
Yet you only created a binding for MainPresenter:
#Provides
#PerActivity
MainPresenter provideMainPresenter() {
return new MainSubPresenterOne();
}
This means that Dagger knows only how to inject a MainPresenter, it doesn't care that the MainPresenter is actually a MainSubPresenterOne.
Instead, what I would do is to scope the concrete presenters and let them have an #Inject constructor:
#PerActivity
public class MainSubPresenterOne extends MainSubPresenter<MainSubViewOne> {
#Inject
public MainSubPresenterOne() {
// ...
}
// ...
}
Now Dagger knows how to inject MainSubPresenterOne. Remove the #Provides method.
I recommend the official documentation, which – among other things – explains that #Provides is a kind of last resort and you should prefer #Inject on the types under your control.
Alternatively you would
#Inject
protected MainPresenter mPresenter;
and create a separate subcomponent for each of your activities with a module providing the actual presenter:
#Module
abstract class MainSubActivityOneModule {
#Binds
abstract MainSubPresenter<MainSubViewOne> bindMainPresenter(MainSubPresenterOne impl);
}
This assumes that the activity doesn't care about the concrete implementation of its presenter, which may or may not be what you want.

Why can't I add a keyword protected, private or public before class Todo?

The HttpHelper.java incude the two class HttpHelper and Todo, the code works well.
If I add a keyword before class Todo, such as
private class Todo{}
public class Todo{}
protected class Todo{}
the system will report error, why ?
HttpHelper.java
public class HttpHelper {
}
class Todo{
}
because the key word 'protected' can only be used before a inner class
as this
class A{
protected class B{
}
}

dagger cannot inject type parameter field

I'm working on an android application and I'm trying to inject a field which is type parameterized in an abstract class : BaseListFragment
public abstract class BaseListFragment<E, A extends ArrayAdapter<E>, S> extends BaseFragment
{
#Inject protected S service;
}
But I get this following error at compile :
error: cannot find symbol class S
Here is my code for BaseFragment :
public class BaseFragment extends Fragment
{
#Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
((App) getActivity().getApplication()).inject(this);
}
}
here is my service module :
#Module(
complete = false,
library = true
)
public class ServiceModule
{
#Provides #Singleton BucketService provideBucketService(RestAdapter restAdapter)
{
return restAdapter.create(BucketService.class);
}
#Provides #Singleton ProjectService provideProjectService(RestAdapter restAdapter)
{
return restAdapter.create(ProjectService.class);
}
#Provides #Singleton ShotService provideShotService(RestAdapter restAdapter)
{
return restAdapter.create(ShotService.class);
}
#Provides #Singleton TeamService provideTeamService(RestAdapter restAdapter)
{
return restAdapter.create(TeamService.class);
}
#Provides #Singleton UserService provideUserService(RestAdapter restAdapter)
{
return restAdapter.create(UserService.class);
}
}
And here is an example of a class extending BaseListFragment :
public class ProjectFragment extends BaseEntityFragment<Project, ProjectViewAdapter, ProjectService>
{
}
Is there anyway to inject a parameterized type ?
Regards,
As of the 2.0.1 release, Dagger 2 does support the type of injection that you're talking about. Take a look at GenericTest for all of the various permutations.
I had the same problem and got around it by adding a non-parameterized inner class and injecting into that. Then using a getter to get the injected class out.
It looks like this:
public class MainClass<T>{
UtilityClass utility;
public MainClass(){
utility = new InjectorHelper().getHelper();
}
...
public static class InjectorHelper{
#Inject
UtilityClass utilityClass;
public InjectorHelper(){
Injector.getInstance().getAppComponent().inject(this);
}
public UtilityClass getUtilityClass(){
return utilityClass
}
}
}
I also could not get it work either and I think it's against the design philosophy of dagger, which is to generate code that is exactly what a developer would also write.
In this case, it generates an injection adapter for the abstract class and another one for concrete class. With generic arguments to the abstract class, you essentially have to inject the fields from the abstract class in each concrete class injector.
If it wasn't an android object (which the android runtime creates), I'd suggest to pass the service in the call to the super constructor.
In this case, I'd suggest to inject the service in the concrete classes and to provide it using an abstract method:
protected abstract S getService();

Roboguice 2.0 injecting application into POJO

I'm newbe in Roboguice, please help. I have an application calss MyApplication in which in onCreate method i initialize some data. Also i have a POJO with buisiness logic which i want to use in my MainActivity (See code snippets below). I need to inject MyApplication into POJO to get access to data which i initialize in application's onCreate, but this code called before onCreate and i've got a NullPointerException.
public class MyApplication extends Application {
private Properties applicationProperties;
#Override
public void onCreate() {
super.onCreate();
applicationProperties = loadApplicationProperties(APPLICATION_PROPERTIES_ASSET);
}
#SuppressWarnings("unchecked")
public String getProperty(String key) {
return applicationProperties.getProperty(key);
}
}
#Singleton
public class POJO {
#Inject
private MyApplication application;
#Inject
public void init() {
// NPE here, because application onCreate not called at this moment
serverURL = application.getProperty(Constants.SERVER_URL);
}
}
public class MainActivity extends RoboActivity {
#Inject
private POJO myPOJO;
}
EDIT: Found a way to do this in RoboGuice 2.0 based on the answer in RoboGuice custom module application context.
Inject the application context in AbstractModule constructor, then bind it in configure() for later injection:
public final class MyModule extends AbstractModule
{
private final MyApplication context;
#Inject
public MyModule(final Context context)
{
super();
this.context = (MyApplication)context;
}
#Override
protected void configure() {
bind(MyApplication.class).toInstance(context);
}
}
If the data you need doesn't require a context, just access to XML resources or res/raw, you can inject that from anywhere.
Just use Roboguice.getInjector() to obtain a copy of the Resources object.

Categories

Resources