In my Android app project, I am using RoboGuice .
In my project, I have a singleton Class A:
#ContextSingleton
public class A{
…
public void method1(){…}
}
Then, I have another class B which needs an instance of A, so, in RoboGuice way, I normally declare the instance of A inside class B with injection :
public class B {
#Inject private A a ;
public void action(){
a.method1(); // call method1() of class A's instance
}
}
Sometimes, I got NullPointerException for the instance of A declared in class B. I just want to verify one concept of RoboGuice:
Is it so that in order to inject an instance of a custom class (e.g. class A) in class B, the class B has to be either injected in RoboActivity or be injected into another class (e.g. Class C) which has injected in RoboActivity?
You probably instantiate B somewhere yourself (new B()) and then you need to call the Injector manually.
When RoboGuice creates the instance B it will automatically inject the dependency A, but when you create B yourself, RoboGuice wil not know about it and you have to call the inject code yourself. This can be done by calling:
RoboInjector injector = RoboGuice.getInjector(context);
injector.injectMembersWithoutViews(yourObjectB);
Related
I am using Hilt for dependency injection, and I wanted to start a singleton Android Service (DeviceConnectionService), and be able to access that Service object to do something to it
I observed 2 instances of DeviceConnectionService being created even though it was denoted as Singleton. Any idea or advice on this? Thanks in advance!
Following is my code setup:
Android Library: DeviceLibrary
Android Service
#AndroidEntryPoint
#Singleton
public class DeviceConnectionService extends Service {
#Inject
public DeviceConnectionService () {
Timber.d("DEVICE connection : " + hashCode());
}
}
Another classes that wants to be injected with the Android Service - to do something:
#Singleton
public class Connection implements IHololensConnection {
#Inject
DeviceConnectionService connectionService;
...
}
App
MainActivity.java
#AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
...
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
...
Intent intent = new Intent(getApplicationContext(), DeviceConnectionService.class);
startService(intent);
}
...
}
Dagger doesn't start services, it just injects them once Android starts them. If you're observing that your Service is instantiated multiple times, that's unrelated to Hilt/Dagger/#Singleton: You'll need to ensure that your Service doesn't stop itself, and that nothing else stops it.
(This would be slightly different if your Service were instantiated exactly once, and objects that your Service injects were instantiated more times than you wanted. In that case you would need to put the #Singleton annotation on your binding for that class in a Module. However, that only applies to objects that Dagger creates, which excludes Service, Activity, or anything else marked with #AndroidEntryPoint.)
I have a ViewModel called RecipesViewModel. Usually, I instantiated it this way:
RecipesViewModel viewModel = ViewModelProviders.of(this, new ViewModelProvider.Factory() {
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
return (T) new RecipesViewModel(recipesRepository);
}
}).get(RecipesViewModel.class);
But now I'm using dagger2 and so I put a #Inject annotation on the constructor of this ViewModel, so I'm able to inject it directly in my fragment, using field injector.
My question is: do I lose something starting the viewmodel this way instead of ViewModelProviders.of way? My ViewModel is already Scoped, so only one instance is create in context.
Other option is to move only the factory instantiation to a dagger2 module, but if there is no problem I prefer the first aproach.
-- EDIT --
Reading the documentation android.arch.lifecycle.ViewModel, I'm a little more afraid. Whe use ViewModelProviders.of to provide a Scope (fragment or activity). If I instantiate it directly what will be the Scope?
ViewModel is a class that is responsible for preparing and managing
the data for an Activity or a Fragment. It also handles the
communication of the Activity / Fragment with the rest of the
application (e.g. calling the business logic classes).
A ViewModel is always created in association with a scope (an fragment
or an activity) and will be retained as long as the scope is alive.
E.g. if it is an Activity, until it is finished.
In other words, this means that a ViewModel will not be destroyed if
its owner is destroyed for a configuration change (e.g. rotation). The
new instance of the owner will just re-connected to the existing
ViewModel.
-- /EDIT --
The RecipesViewModel code is showing below:
#PerActivity
public class RecipesViewModel extends ViewModel {
private static final String TAG = "RecipesViewModel";
private final RecipesRepository recipesRepository;
private LiveData<List<Recipe>> recipes = null;
#Inject
public RecipesViewModel(RecipesRepository recipesRepository) {
this.recipesRepository = recipesRepository;
}
public final void loadAll() {
recipes = recipesRepository.getRecipes();
}
public LiveData<List<Recipe>> getRecipes() {
return recipes;
}
}
For me right now (and I need to research this), but injecting a view model instead of using the ViewModelProviders functionality means you lose some easy activity-fragment communication.
For example from the docs they provide an example of an activity hosting 2 fragments. If one fragment needs to talk to another, the previous method was to maintain an interface via the activity who also had to take care of the lifecycle of that interface. Instead now you can just fetch it from the the ViewModelProviders 'repo' whenever you need.
I am studying a Dagger 2 from many sources such as this one: http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/
but I still haven't found an answer to my question.
I work on quite complex application with tens of fragments and several activities in which I want to use DI (dagger 2). For all of those fragments and activities I have one BaseActivity and one BaseFragment. However, as far as I read and tried, in order to use #Inject in my let's say MainActivity, I have to specify it in Component interface and also invoke getApplicationComponent().inject(this) in onCreate method. When I do this for BaseActivity only, #Inject annotated fields in MainActivity is never injected. And what is even worse, I do not find out about that until that specific part of code is executed and NPE is thrown.
So far it is a deal breaker for me, because this can be source of many crashes. I would need to specify tens of fragments and activities in Component interface and not forget to call inject in each onCreate method.
I would be very glad to hear any solution to this since I would really like to use DI..
code example:
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
Analytics analytics();
}
public class BaseActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getApplicationComponent().inject(this);
}
}
public class MainActivity extends BaseActivity {
#Inject
Analytics analytics;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
analytics.log("event1"); // THROWS NPE!
}
}
You can not inject properties in your subclass by injecting the super (since dagger2 works at compile time and there is no way to dynamically check subclasses for annotated properties.)
You can move analytics up to the super, then it will be injected there. To inject annotated fields in your subclass you will have to call the injection there again.
You can make an abstract method in your baseclass e.g. inject(App app)where you just handle the injection. That way you can't 'miss' it.
As stated in the official documentation:
While a members-injection method for a type will accept instances of its subtypes, only Inject-annotated members of the parameter type and its supertypes will be injected; members of subtypes will not.
move the
#Inject
Analytics analytics;
to your BaseActivity class, the Analytics object is initialized in the superclass and is inherited by sub-classes automatically, therefor u wouldn't get null any more.
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
analytics.log("event1");
} }
I have two classes, Class A and Class B. In Class B I created a constructor. In class A I created onresume method. From the onresume method of class A I want to call the class B constructor.
How can I do that? Please guide me regarding this.
Thanks in Advance
When you create a new object of class B in onResume of class A, it will automatically call the constructor for class B for its object.
For example:
B obj = new B(); //the 'new' keyword will call the constructor itself
If both classes are public and are of same package, you can call constructor of class A in class B by using the code,
B objb = new B();
Otherwise you need to extend class B from class A.
The following is my situation:
I have a library project and a project based on it. Now in the library I have two classes A and B, whereby A uses B. In the project using the library, I have another class B, which should override the class B from the library.
But every time class A makes a call, it ends up in the class B from the library.
How can I tell Android that class B from my project should be used INSTEAD of class B from the library?
That does not work with the current layout. You have to use the strategy pattern. In your library define LibA with a constructor that takes a object of type LibB in the constructor:
class LibA{
LibB b;
public LibA(LibB b)
this.b = b;
}
}
Then you can override LibB in your project and create LibA with the class that extends LibB:
class ProjectB extends LibB{
}
LibA a = new LibA(new ProjectB());
Answer to Turbos question:
You want to start Project-Activities from your Library. So then move the code that creates the Intent into your Projects, because only in your project you know the type or name of the Activity to be started.
The solution you mentioned in your comment (here) creates the Intent in the library project, by guessing the name of the Activity that should be started. There is nothing really wrong with that but it's not an elegant solution. You can only start Activities that follow that special naming scheme. And because of that you cannot start arbitrary Activities that are visible in your projects like Activities from other libraries, where you cannot change the name of the class.
To move the Intent creation into your libraries you can i.e. use the strategy, template or factory-method pattern. See Design Patterns on Wikipedia for even more (creational) patterns that match your library design.
A simple solution would be:
Create an abstract LibraryActivity and extend your ProjectActivities from it. The LibraryActivity defines an abstract method that returns the Intent. The implementation of that abstract method is done in your ProjectActivities.
abstract class LibActivity{
private void doSomething(){
//Library does something
//and finally calls createIntent() to start an project specific Activity
startActivity(this.createIntent());
}
protected abstract Intent createIntent();
}
class ProjectActivity extends LibActivity{
protected Intent createIntent(){
return new Intent(ProjectActivity.this, AnyActivityYouWant.class);
}
}
I dont know if this is the best way to do it but i do it this way;
I create a class A from the project and this class extends form the library project
public class A extends libraryProject_A{
//here i put all methods from the new class B to override methods from library class B
}
As I see from your comment you have class A that uses class B
But class B should be different according to which project you're using.
I think you need to create a base class say BaseB that will be an instance variable in class A, and you might have a setter and getter for this Or you can make it a parameter passed to the constructor of class A. and when instantiating A you should choose which one to use.
Let's have a look at code
Class A {
private BaseB b;
public A(BaseB aB) {
b = aB;
}
public void set(BaseB aB) {
b = aB;
}
public BaseB get() {
return b;
}
}
interface BaseB {
}
// in the library have this
class B implements BaseB {
}
// in your project have the other implementation
class B implements BaseB {
}
// you can then instantiate A like this
A a = new A(new B());
// you can choose which one to use here in the previous statement.