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.)
Related
I'm using Room to implement database, code for working with database is located into Repository, to get instance of database Repository needs Application as class atribute, I want to get instance of Repository in class that extends IntentService and in class that extends Worker class, now, they don't need Application as atribute but to be able to use Repostiory into them I must add it, is this good programming practice? Maybe some other soluction?
public class SunshineSyncIntentService extends IntentService {
private Application mApplication;
public SunshineSyncIntentService(#NotNull Application application) {
super("SunshineSyncIntentService");
mApplication = application;
}
#Override
protected void onHandleIntent(Intent intent) {
SunshineSyncTask.syncWeather(mApplication);
}
}
In this case, is not a bad practice. But I rather use some dependency injection as a more robust solution. You can take a look on Dagger or Koin (for Kotlin).
I have an application with let's say 15 screens.
There are 3 main, completely separate Activities:
LoginActivity - quite obvious one, some login things
MainActivity - THE big one, most importat
TheOtherOneNotRelevantAtTheMoment
I decided to use Conductor, because I've found Fragment's lifecycle too complicated, and for me Conductor is now "hot shit"
I've got many Conductor's controllers. Most of them are like XXXListController with corresponding XXXDetailController. All of these Controllers live only inside MainActivity. It's something like "single-activity-application". LoginActivity was introduced basically due to callbacks from third party OAuths like FbLogin & Google Login. I am just trying to make MainActivity completely mine - without any other strange behaviours.
To organize the dependencies a little bit, I've decided to use Dagger2. I've got quite good experience with Spring and JavaEE so I though it would be easy.
I recognized few Modules without any problems:
AppModule - with my App-related things like server address etc
AndroidModule - with things like SharedPreferences, Application
Then I've pretty much "improvised" with how to organize my views. This is what I've got:
3 additional Scopes: ActivityScope, ControllerScope, and ServiceScope - not relevant.
each Controller has it's own corresponding Module & Component. I've read that this could not be a very good idea, but I am ok with it - each Controller is pretty independend and has it's own unique set of dependencies.
ApplicationComponent is of course root of the hierarchy.
MainActivityComponent is a #Subcomponent of the ApplicationComponent
XXXControllerComponent is a #Subcomponent of the MainActivityComponent
To inject dependencies inside MainActivity I am using and I found this code pretty common:
protected void injectDependencies(ApplicationComponent component) {
component.plus(new MainActivityModule(this)).inject(this);
}
The problem appears when I want to create & inject dependencies to my Controllers.
The MainActivity looks as follows:
#ActivityScope
#Subcomponent(modules = {
MainActivityModule.class
})
public interface MainActivityComponent {
XXXListComponent newXXXListComponent(XXXListModule xxxListModule);
void inject(MainActivity activity);
}
At the moment, the typical Controller looks like this:
#ControllerScope
#Subcomponent(modules = {
XXXListModule.class
})
public interface XXXListComponent {
void inject(XXXListController controller);
}
and the corresponding Module:
#Module
public class XXXListModule {
private XXXListController listController;
public XXXListModule(XXXListController listController) {
this.listController = listController;
}
#Provides
#ControllerScope
public XXXListController getMainView() {
return ListController;
}
// other not important
}
Basically every Controller should be singleton - I don't want to have two instances inside MainActivity - but this not a 'must-have'.
The problem is how to create Controller the right way. At the moment, MainActivity do it as follows:
router.pushController(RouterTransaction.with(new XXXListController()));
I am not sure about this, why to create Controller by hand?
Inside Controller in onCreateView() I've injecting all needed dependencies - in my opinion in very ugly way:
((MainActivity) getActivity()).getMainActivityComponent()
.newXXXListComponent(new XXXListModule(this))
.inject(this);
This long question helps me organize my knowledge about Dagger - maybe someone find it helpful. But! For those Stackoverflowers who reach this line, is this a good way or is there any other, better way to do it?
I'm not sure I fully understand your question, but since you have multiple Activities you probably want to do your injecting inside your Application rather than MainActivity.
Otherwise they will no longer be singletons as the activities get re-created when you move between them. For example if you make a class called ConductorApplication:
public class ConductorApplication extends Application {
static AppComponent app_component;
static ClockComponent component;
#Override
public void onCreate() {
super.onCreate();
app_component = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
component = createComponent();
}
protected ClockComponent createComponent() {
return DaggerClockComponent.builder().build();
}
public static ClockComponent getClockComponent() {
return component;
}
public ClockComponent component() {
return component;
}
public static AppComponent GetAppComponent() {
return app_component;
}
}
Then inside when your controller is created eg inside your inflateView() within HomeController
public class HomeController extends BaseController {
private HomeViewModel homeViewModel;
private ControllerHomeBinding binding;
#Inject
Clock clock;
#NonNull
#Override
protected View inflateView(#NonNull LayoutInflater inflater, #NonNull ViewGroup container) {
ConductorApplication.getClockComponent().inject(this);
return inflater.inflate(R.layout.controller_home, container, false);
...
}
If you haven't already, you may need to add:
<application
android:name=".ConductorApplication"
...
<activity
android:name=".MainActivity"
...
</activity>
</application>
inside your AndroidMainifest.xml
In my Android app I have to query some user/session dependent data from a rest webservice. Now I need a way to keep the received webservice results in memory, so that serveral activities/fragments can access them.
I don't want to persist the data (for example a list of the users bank accounts) into a database on the device, because the data expires after a while or when the user logs out.
I also don't want to request the data again and again from webservice, when the user navigates to another activity.
Are there any approved patterns to keep a set of data (some pojo's with more or less properties) in memory during the application is running?
Just for info: I'm experimenting with dagger2, mvp, retrofit2, rxandroid
Regards
Martin
If you already experimenting with Dagger 2, then all you need to do is instantiate a component in Application and use this component in your Activities and Fragments in order to inject a scoped "service".
For example:
Create a class named XyzManager (where Xyz = the actual functionality this manager is responsible for)
Annotate its #Provides method (in Dagger's module) with #Singleton scope
Make sure that the component that injects XyzManager instantiated in Application and add getComponent() method to your custom Appliaction class
In your Activities and Fragments inject XyzManager while using the same component - ((MyApplication)getApplication()).getComponent().inject(this)
If you take the above steps, then all your Activities and Fragments will get a reference to exactly the same instance of XyzManager, and the data you cache in this manager will be accessible everywhere.
The structure you would get is very similar to the structure described in this answer.
Please note that this approach is much better than resolving to static things (e.g. Singleton pattern, or what #KhalidTaha suggested in his answer).
You might want to take a look at my post concerning Dagger 2 scopes if you need a detailed information on that aspect of the framework.
here is a solution:
1- create a DefaultUtil class:
public calss DefaultUtil{
private List<User> listOfUsers;
public static DefaultUtil getInstance(){
if(instance == null)
{
instance = new DefaultUtil();
}
return instance;
}
public List<User> getUserList(){ return listOfUsers; }
public void setUserList(List<User> userList) {
this.listOfUsers = userList ;
}
}
2- when you finish the webservice, call this code:
DefaultUtil.getInstance().setUserList(myWebserviceListOfUsersResult);
and then you can access the list of users from any class by this:
DefaultUtil.getInstance().getUserList();
#Vasiliy
I've studied the linked answer, but I don't get it. I don't use my BankingSession singleton in an activity directly, so calling "getComponent().inject(this).... " won't work. I use the singleton in other service classes (not Android services... just business logic).
// this should be a single instance across the whole app
#Singleton
public class BankingSession {
#Inject
public BankingSession() {
}
}
public class SessionServiceImpl implements SessionService {
private final BankingSession bankingSession;
#Inject
public SessionServiceImpl(BankingSession bankingSession) {
this.bankingSession = bankingSession;
}
}
#Module
public class SessionModule {
#Provides
public SessionService provideSessionService(SessionServiceImpl sessionService) {
return sessionService;
}
}
#Singleton
#Component(modules = {AppModule.class, NetworkModule.class, SessionModule.class})
public interface AppComponent {
Application application();
LoginComponent plus(LoginModule module);
AccountComponent plus(AccountModule module);
BankingSession bankingSession();
}
No matter how I try it, the constructor of BankingSession get's called multiple times
We used RoboGuice, but it's deprecated I start replace it with Dagger2.
// https://github.com/google/dagger
compile('com.google.dagger:dagger:2.7')
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
provided 'org.glassfish:javax.annotation:10.0-b28'
#Module
public class ApplicationModule {
Application mApp;
public ApplicationModule(#NonNull Application app) {
Preconditions.checkNotNull(app);
mApp = app;
}
#Provides
#Singleton
public SharedPreferences providesSharedPrefs() {
return PreferenceManager.getDefaultSharedPreferences(mApp);
}
#Provides
#Singleton
public DateHelper providesDateHelper() {
return new DateHelper(mApp);
}
#Provides
#Singleton
public PersistentConfig providesPersistentConfig() {
return new PersistentConfig(mApp);
}
#Provides
#Singleton
public OttoBus providesOttoBus() {
return new OttoBus();
}
}
public class Application extends MultiDexApplication {
private ApplicationComponent mApplicationComponent;
#Override
public void onCreate() {
super.onCreate();
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
mApplicationComponent.inject(this);
}
public static Application getApp(#NonNull Context context) {
return (Application) context.getApplicationContext();
}
public static ApplicationComponent getApplicationComponent(#NonNull Context context) {
return getApp(context).getApplicationComponent();
}
}
And after everywhere when I want to inject ApplicationComponent
For example MainActivity
public class MainActivity extends AppCompatActivity {
#Inject
PersistentConfig mPersistentConfig;
#Inject
OttoBus mOttoBus;
#Override
public void onCreate(Bundle savedInstanceState) {
Helper.manageRotation(this);
super.onCreate(null);
setContentView(R.layout.main_layout);
Application.getApplicationComponent(this).inject(this);
}
}
Application.getApplicationComponent(context).inject(this);
First question: I'm really confused about interface ApplicationComponent which must provide all activities/fragments/services (etc) where I want to use injection. But I can't use generic objects like Activity / Fragment. Or am I really out of reality and don't understand how Dagger2 works?
Because this is really crazy for project with about 50+ activities and a tons of fragments/services...
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(#NonNull Application app);
void inject(#NonNull MainActivity object);
void inject(#NonNull DispatcherActivity object);
void inject(#NonNull DateTimeHelper object);
void inject(#NonNull DatabaseHelper object);
void inject(#NonNull LandingPageActivityFragment object);
void inject(#NonNull RedirectActivity object);
void inject(#NonNull CategoryFragment object);
void inject(#NonNull BaseModuleFragment object);
void inject(#NonNull NotificationHelper object);
void inject(#NonNull RecordAdapter object);
void inject(#NonNull PagingProvider object);
void inject(#NonNull FilterDialog object);
... next 100+ injections?
}
Said me, that it can't be real...
Second question: How I can provide to inject generic classes, when I can't use it like void inject(#NonNull NotificationHelper<? extends GenericObject> object); because it require specific object. So I must write all this objects inside ApplicationComponent and not use ? notation?
It's a much more than just crazy :(. Maybe better stay with RoboGuice which is much more developer friendly and don't need make this overhead and manual check every injected objects? When I forgot add them to this list, I will get NPE in runtime (when I will not test it a lot it will crash customers).
It's much faster write it manually, than make a list of all object when it's not possible to use generic objects like Activity / Fragment / Service.
Is there a better solution, when I don't want use same generic BaseActivity which will inject every part of ApplicationModule and every activity will be extended by this huge BaseActivity?
This question has aspects of a complaint, but to attempt an answer:
I'm really confused about interface ApplicationComponent which must provide all activities/fragments/services (etc) where I want to use injection. But I can't use generic objects like Activity / Fragment. Or am I really out of reality and don't understand how Dagger2 works?
This is, indeed, how Dagger 2 works; it you must statically supply the type of the injection target inside the injector (component) and you cannot use 'generic' (covariant) types. Dagger 2 does this in order to maintain a DI framework that is 100% static.
Note that you are specifying RecordAdapter and DatabaseHelper as injection sites. You probably don't need to do that, you should try and only specify top level objects for which the constructor is not visible (Activity, Fragment, and Service) as injection sites. The rest of the objects should be able to be constructed through annotating their dependencies with #Inject and specifying their dependencies, if any, in a Module.
Maybe better stay with RoboGuice which is much more developer friendly and don't need make this overhead and manual check every injected objects
Yes Roboguice is more friendly in the sense that you don't have to worry about specifying the injection targets. However, consider the following in Roboguice: 1. The 'red stacktrace of death' you get when you set up your object graph incorrectly
2. The fact that you cannot get see which implementations of interfaces are actually being used in your project with Find Usages which can also be 'developer unfriendly'
Is there a better solution, when I don't want use same generic BaseActivity which will inject every part of ApplicationModule and every activity will be extended by this huge BaseActivity?
Well, it would depend which dependencies you are using and where. If you have a small list of dependencies that you want to inject everywhere, that may be the best solution i.e., make a BaseActivity that receives injection of these and makes this available to all of your subclasses. Alternatively, you can use sub-components and modules you can divide up your object graph so that you can group consumers/injection targets together with the correct modules. Then you don't need to have one 'god' component that lists all of the injection sites.
Second question: How I can provide to inject generic classes, when I can't use it like void inject(#NonNull NotificationHelper object); because it require specific object. So I must write all this objects inside ApplicationComponent and not use ? notation?
Yes, you must supply the invariant type of the injection target. I am not sure if your NotificationHelper<String> is a top level type. Why not inject it through the object graph when you inject in a Fragment, Activity or Service?
If it absolutely must be an injection target you will need to subclass: NotificationHelper<String> and Notification<Integer> become StringNotificationHelper extends NotificationHelper<String>, IntegerNotficationHelper extends NotificationHelper<Integer>. This is a practice recommended in the book Clean Code.
You don't need to write it all the injection sites inside the ApplicationComponent, you may create subcomponents that correspond with the consumption patterns of the dependencies in your project.
(disclosure: as someone who is currently trying to migrate a project from Roboguice to Dagger 2 I am sympathetic to your complaint)
Thanks, we solved it as you described a week ago. Using every objects as injected.
Better solution for it is don't use only inject but complex name. Why? Because it will help to resolve why some object is not injected (you know, base classes and so on).
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void injectApplication(#NonNull Application app);
void injectMainActivity(#NonNull MainActivity object);
void injectDispatcherActivity(#NonNull DispatcherActivity object);
...
}
We finally use for genericity UtilityWrapper as is described here: https://medium.com/#patrykpoborca/dagger-2-and-base-classes-generics-and-presenter-injection-7d82053080c#.b58ykd4cm
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");
} }