Android - keep webservice results in memory - android

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

Related

MVVM in android,accessing assetManager without breaking the pattern

I have a JSON file in the assets folder and DataManager(repository) class needs it so assetManager(and context) should have access to the assets.
The problem is that based on Best practice, Android context or android specific code should not be passed into the data layer(ViewModel-Repo-Model) because of writing unit tests or etc easily and also view should not interact with the data layer directly.
I ended up providing the list using and injecting it to the repository.
Is this the right thing to do?
-Thanks
P.S: my Module class which provides the list
#Module
public class UtilModule {
#Provides
#JsonScope
JsonUtil provideJsonUtil(AssetManager assetManager){
return new JsonUtil(assetManager);
}
#Provides
#JsonScope
String provideJson(JsonUtil util){
return util.getJson();
}
#Provides
#JsonScope
Type provideType(){
return new TypeToken<List<Data>>() {}.getType();
}
#Provides
#JsonScope
DataManager provideDataManager (Gson gson, Type type,String json) {
return new DataManager (gson.fromJson(json, type));
}
}
It's not a violation of MVVM for a ViewModel and/or Repository to access the Application context directly, which is all you need to access the AssetsManager. Calling Application.getAssets() is OK because the ViewModel doesn't use any particular Activity's context.
For example, you can use the Google-provided AndroidViewModel subclass instead of the superclass ViewModel. AndroidViewModel takes an Application in its constructor (ViewModelProviders will inject it for you). You could pass your Application to your Repository in its constructor.
Alternately, you could use Dagger dependency injection to inject an Application directly into your Repository. (Injecting the Application context is a bit tricky. See Dagger 2 injecting Android Context and this issue filed on the Danger github repo.) If you want to make it really slick, you could configure a provider for AssetManager and inject it directly into your Repository.
Finally, if you are using Room, and all you want is to pre-populate your Room database with a pre-configured database stored in assets, you can follow instructions here: How to use Room Persistence Library with pre-populated database?
Since you are using MVVM for the first time, we can try to keep things simple.
[ View Component C] ---- (observes) [ ViewModel Component B ] ---- [ Repository ]
According to the Separation of Concerns rule, the ViewModel should expose LiveData. LiveData uses Observers to observe data changes. The purpose of the ViewModel is to separate the data layer from UI. ViewModel should not know about Android framework classes.
In MVVM Architecture, the ViewModel's role is to fetch data from a Repository. You can consider either storing your json file as a local data source using Room, or keeping the Json API as a remote data source. Either way, the general implementation is as follows:
Component A - Entity (implements your getters & setters)
Method 1: Using Room
#Entity(tableName = "file")
public class FileEntry{
#PrimaryKey(autoGenerate = true)
private int id;
private String content; // member variables
public FileEntry(String content){ // constructor
this.id = id;
this.content = content;
}
public int getId(){ // getter methods
return id;
}
public void setId(int id){ // setter methods
this.id = id;
}
public String getContent(){
return content;
}
public void setContent(String content){
this.content = content;
}
}
Method 2: Using Remote Data Source
public class FileEntry implements Serializable{
public String getContent(){
return content;
}
private String content;
}
Component B - ViewModel (Presentation Layer)
Method 1: Using Room
As you asked about how android context can be passed, you can do so by extending AndroidViewModel like below to include an application reference. This is if your database requires an application context, but the general rule is that Activity & Fragments should not be stored in the ViewModel.
Supposing you have "files" as a member variable defined for your list of objects, say in this case, "FileEntry" objects:
public class FileViewModel extends AndroidViewModel{
// Wrap your list of FileEntry objects in LiveData to observe data changes
private LiveData<List<FileEntry>> files;
public FileViewModel(Application application){
super(application);
FilesDatabase db = FilesDatabase.getInstance(this.getApplication());
Method 2: Using Remote Data Source
public class FileViewModel extends ViewModel{
public FileViewModel(){}
public LiveData<List<FileEntry>> getFileEntries(String content){
Repository repository = new Repository();
return repository.getFileEntries(content);
}
}
In this case, getFileEntries method contains MutableLiveData:
final MutableLiveData<List<FileEntry>> mutableLiveData = new MutableLiveData<>();
If you are implementing using Retrofit client, you can do something similar to below code using asynchronous callbacks. The code was taken from Retrofit 2 Guide at Future Studio with some modifications for this discussion example.
// asynchronous
call.enqueue(new Callback<ApiData>() {
#Override
public void onResponse(Call<ApiData> call, Response<ApiData> response) {
if (response.isSuccessful()) {
mutableLiveData.setValue(response.body().getContent());
} else {
int statusCode = response.code();
// handle request errors yourself
ResponseBody errorBody = response.errorBody();
}
}
#Override
public void onFailure(Call<ApiData> call, Throwable t) {
// handle execution failures like no internet connectivity
}
return mutableLiveData;
Component C - View (UI Controller)
Whether you are using Method 1 or 2, you can do:
FileViewModel fileViewModel = ViewModelProviders.of(this).get(FileViewModel.class);
fileViewModel.getFileEntries(content).observe(this, fileObserver);
Hope this is helpful.
Impacts on Performance
In my opinion, deciding whether to use which method may hinge on how many data calls you are implementing. If multiple, Retrofit may be a better idea to simplify the API calls. If you implement it using Retrofit client, you may have something similar to below code taken as provided from this reference article on Android Guide to app architecture:
public LiveData<User> getUser(int userId) {
LiveData<User> cached = userCache.get(userId);
if (cached != null) {
return cached;
}
final MutableLiveData<User> data = new MutableLiveData<>();
userCache.put(userId, data);
webservice.getUser(userId).enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
data.setValue(response.body());
}
});
return data;
}
The above implementation may have threading performance benefits, as Retrofit allows you to make asynchronous network calls using enqueue & return the onResponse method on a background thread. By using method 2, you can leverage Retrofit's callback pattern for network calls on concurrent background threads, without interfering with the main UI thread.
Another benefit of the implementation above is that if you are making multiple api data calls, you can cleanly get the response through an interface webservice above, for your LiveData. This allows us to mediate responses between different data sources. Then, calling data.setValue sets the MutableLiveData value & then dispatches it to active observers on the main thread, as per Android documentation.
If you are already familiar with SQL & only implementing 1 database, opting for the Room Persistence Library may be a good option. It also uses the ViewModel, which brings performance benefits since chances of memory leaks are reduced, as ViewModel maintains fewer strong references between your UI & data classes.
One point of concern may be, is your db repository (example, FilesDatabase implemented as a singleton, to provide a single global point of access, using a public static method to create the class instance so that only 1 same instance of the db is opened at any one time? If yes, the singleton might be scoped to the application scope, & if the user is still running the app, the ViewModel might be leaked. Thus make sure your ViewModel is using LiveData to reference to Views. Also, it might be helpful to use lazy initialization so that a new instance of the FilesDatabase singleton class is created using getInstance method if there are no previous instances created yet:
private static FilesDatabase dbInstance;
// Synchronized may be an expensive operation but ensures only 1 thread runs at a time
public static synchronized FilesDatabase getInstance(Context context) {
if (dbInstance == null) {
// Creates the Room persistent database
dbInstance = Room.databaseBuilder(context.getApplicationContext(), FilesDatabase.class, FilesDatabase.DATABASE_NAME)
Another thing is, no matter your choice of Activity or Fragment for your UI, you will be using ViewModelProviders.of to retain your ViewModel while a scope of your Activity or Fragment is alive. If you are implementing different Activities/Fragments, you will have different instances of ViewModel in your application.
If for example, you are implementing your database using Room & you want to allow your user to update your database while using your application, your application may now need the same instance of the ViewModel across your main activity and the updating activity. Though an anti-pattern, ViewModel provides a simple factory with an empty constructor. You can implement it in Room using public class UpdateFileViewModelFactory extends ViewModelProvider.NewInstanceFactory{:
#Override
public <T extends ViewModel> T create(#NotNull Class<T> modelClass) {
return (T) new UpdateFileViewModel(sDb, sFileId);
Above, T is a type parameter of create. In the factory method above, the class T extends ViewModel. The member variable sDb is for FilesDatabase, and sFileId is for the int id that represents each FileEntry.
This article on Persist Data section by Android may be more useful than my comments if you would like to find out more, on performance costs.

Instantiate ViewModels directly, without making use of ViewModelProviders.of method

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.

Understanding scopes in Dagger 2

I have an scope-related error in Dagger 2 and I'm trying to understand how I can solve it.
I have a CompaniesActivity that shows companies. When the user selects an item, selected company's employees are shown in EmployeesActivity. When the user selects an employee, her detail is shown in EmployeeDetailActivity.
class Company {
List<Employee> employees;
}
Class CompaniesViewModel contains the companies and the selected one (or null):
class CompaniesViewModel {
List<Company> companies;
Company selected;
}
CompaniesActivity has a reference to CompaniesViewModel:
class CompaniesActivity extends Activity {
#Inject
CompaniesViewModel viewModel;
#Override
protected void onCreate(Bundle b) {
//more stuff
getComponent().inject(this);
showCompanies(viewModel.companies);
}
//more stuff
private onCompanySelected(Company company) {
viewModel.selected = company;
startActivity(new Intent(this, EmployeesActivity.class));
}
}
Class EmployeesViewModel contains the employees and the selected one (or null):
class EmployeesViewModel {
List<Employee> employees;
Employee selected;
}
EmployeesActivity has a reference to EmployeesViewModel:
class EmployeesActivity extends Activity {
#Inject
EmployeesViewModel viewModel;
#Override
protected void onCreate(Bundle b) {
//more stuff
getComponent().inject(this);
showEmployees(viewModel.employees);
}
//more stuff
private onEmployeeSelected(Employee emp) {
viewModel.selected = emp;
startActivity(new Intent(this, EmployeeDetailActivity.class));
}
}
Finally, in EmployeeDetailActivity, I get selected Employee from view model and show her detail:
class EmployeeDetailActivity extends Activity {
#Inject
EmployeesViewModel viewModel;
#Override
protected void onCreate(Bundle b) {
//more stuff
getComponent().inject(this);
showEmployeeDetail(viewModel.selected); // NullPointerException
}
}
I get NullPointerException because EmployeesViewModel instance in EmployeesActivity is not the same as the EmployeeDetailActivity and, in the second one, viewModel.selected is null.
This is my dagger module:
#Module
class MainModule {
#Provides
#Singleton
public CompaniesViewModel providesCompaniesViewModel() {
CompaniesViewModel cvm = new CompaniesViewModel();
cvm.companies = getCompanies();
return cvm;
}
#Provides
public EmployeesViewModel providesEmployeesViewModel(CompaniesViewModel cvm) {
EmployeesViewModel evm = new EmployeesViewModel();
evm.employees = cvm.selected.employees;
return evm;
}
}
Note that CompaniesViewModel is singleton (#Singleton) but EmployeesViewModel is not, because it has to be recreated each time user selects a company (employees list will contain other items).
I could set the company's employees to EmployeesViewModel each time user selects a company, instead of create a new instance. But I would like CompaniesViewModel to be immutable.
How can I solve this? Any advise will be appreciated.
Unfortunately, I think that you abuse DI framework in this case, and the issues that you encounter are "code smells" - these issues hint that you're doing something wrong.
DI frameworks should be used in order to inject critical dependencies (collaborator objects) into top level components, and the logic that performs these injections should be totally independent of the business logic of your application.
From the first sight everything looks fine - you use Dagger in order to inject CompaniesViewModel and EmployeesViewModel into Activity. This could have been fine (though I wouldn't do it this way) if these were real "Objects". However, in your case, these are "Data Structures" (therefore you want them to be immutable).
This distinction between Objects and Data Structures is not trivial, but very important. This blog post summarizes it pretty well.
Now, if you try to inject Data Structures using DI framework, you ultimately turn the framework into "data provider" of the application, thus delegating part of the business functionality into it. For example: it looks like EmployeesViewModel is independent of CompaniesViewModel, but it is a "lie" - the code in #Provides method ties them together logically, thus "hiding" the dependency. Good "rule of thumb" in this context is that if DI code depends on implementation details of the injected objects (e.g. calls methods, accesses fields, etc.) - it is usually an indication of insufficient separation of concerns.
Two specific recommendations:
Don't mix business logic with DI logic. In your case - don't inject data structures, but inject objects that either provide access to the data (bad), or expose the required functionality while abstracting the data (better).
I think that your attempt of sharing a View-Model between multiple screens is not a very robust design. It would be better to have a separate instance of View-Model for each screen. If you need to "share" state between screens, then, depending on the specific requirements, you could do this with 1) Intent extras 2) Global object 3) Shared prefs 4) SQLite
According to this article about Custom Scopes:
http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/
In short - scopes give us “local singletons” which live as long as scope itself.
Just to be clear - there are no #ActivityScope or #ApplicationScope annotations provided by default in Dagger 2. It’s just most common usage of custom scopes. Only #Singleton scope is available by default (provided by Java itself), and the point is using a scope is not enough(!) and you have to take care of component that contains that scope. This mean keeping a reference to it inside Application class and reuse it when Activity changes.
public class GithubClientApplication extends Application {
private AppComponent appComponent;
private UserComponent userComponent;
//...
public UserComponent createUserComponent(User user) {
userComponent = appComponent.plus(new UserModule(user));
return userComponent;
}
public void releaseUserComponent() {
userComponent = null;
}
//...
}
You can take a look at this sample project:
http://github.com/mmirhoseini/marvel
and this article:
https://hackernoon.com/yet-another-mvp-article-part-1-lets-get-to-know-the-project-d3fd553b3e21
to get more familiar with MVP and learn how dagger scope works.
There are a couple of issues here and that are only obliquely related to Dagger 2 scopes.
Firstly, the fact that you have used the term "ViewModel" suggests you are trying to use MVVM architecture. One of the salient features of MVVM is separation of layers. However, your code has not achieved any separation between model and view-model.
Let's take a look at this definition of model from Eric Evans:
A domain model is a system of abstractions that describes selected aspects of a sphere of knowledge, influence, or activity (a domain).2
Here your sphere of knowledge is a company and its employees. Looking at your EmployeesViewModel, it contains at least one field that is probably better isolated in the model layer.
class EmployeesViewModel {
List<Employee> employees; //model layer
Employee selected;
}
Perhaps it is merely an unfortunate choice of name, but I think your intention is to create proper view-models so any answer to this question should address that. While selection is associated with the view, the class doesn't really qualify as an abstraction of the view. A real view-model would probably somehow match the way the Employee is displayed on the screen. Let's say you have "name" and "date of birth" TextViews. Then a view-model would expose methods that provide the text, visibility, color etc. for those TextViews.
Secondly, what you are proposing is using (singleton) Dagger 2 scopes to communicate between Activities. You want the company selected in the CompaniesActivity to be communicated to the EmployeesActivity and the employee selected in EmployeesActivity to be communicated to the EmployeeDetailActivity.
You are asking how to achieve this by making them all use the same shared global object.
While this may be technically possible using Dagger 2, the correct approach in Android to communicate between Activities is to use intents, rather than shared objects. The answers to this question are a really good explanation of this point.
Here is a proposed solution:
It's not clear what you are doing to actually get the List<Company>. Maybe you are getting from a db, maybe you are getting from a cached web request. Whatever it is, encapsulate this in an object CompaniesRepository. Likewise for EmployeesRepository.
So you will have something like:
public abstract class EmployeesRepository {
List<Employee> getAll();
Employee get(int id);
int getId(Employee employee);
}
Do something similar for a CompaniesRepository class. These two retrieval classes can be singletons and be initialised in your module.
#Module
class MainModule {
#Provides
#Singleton
public CompaniesRepository(Dependency1 dependency1) {
//TODO: code you need to generate the companies retrieval object
}
#Provides
#Singleton
public EmployeesRepository(Dependency2 dependency2) {
//TODO: code you need to generate the employees retrieval object
}
}
Your EmployeesActivity now looks something like this:
class EmployeesActivity extends Activity {
#Inject CompaniesRepository companiesRepository;
#Inject EmployeesRepository employeesRepository;
#Override
protected void onCreate(Bundle b) {
//more stuff
getComponent().inject(this);
//retrieve the id of the company selected in the previous activity
//and use that to get the company model
int selectedCompanyId = b.getIntExtra(BUNDLE_COMPANY_ID, -1);
//TODO: handle case where no company id has been passed into the activity
Company selectedCompany = companiesRepository.get(selectedCompanyId);
showEmployees(selectedCompany.getEmployees);
}
//more stuff
private onEmployeeSelected(Employee emp) {
int selectedEmployeeId = employeesRepository.getId(emp);
Intent employeeDetail = new Intent();
employeeDetail.putExtra(BUNDLE_EMPLOYEE_ID, selectedEmployeeId);
startActivity(employeeDetail));
}
}
Extend this example to your other two activities and you will be approaching the standard architecture for an Android app and you will be using Dagger 2 without mixing your model layer, view layer etc.

Dagger2 Inject a lot of activities/fragments/services (possible get a lot of NPE)

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

Android Dagger 2: Inject versus Provides

I have a question regarding Android Dagger 2 und the usage of #Inject and #Provide annotations. Given are the following two simplified examples:
public class A {
String msg;
public A(String msg){
this.msg = msg;
}
}
public class B {
public A a;
public B(A a){
this.a = a;
}
}
#Module
public class AModule {
#Provides
A providesA(){
return new A("blah");
}
#Provides
B ProvidesB(A a)
{
return new B(a);
}
}
The example is pretty straight forward, I have two methods in my AModule with #Provides annotations. Therefore, Dagger can create an object of B using an instance of A with the string blah.
My second example looks like this:
public class A {
String msg;
public A(String msg){
this.msg = msg;
}
}
public class B {
public A a;
#Inject
public B(A a){
this.a = a;
}
}
#Module
public class AModule {
#Provides
A providesA(){
return new A("blah");
}
}
In this example, Dagger can create an instance of B because an object A can be created using AModule. The instance of B can be created because it's constructor uses the #Inject annotation.
So my question is: What's the difference between those two examples? Both seem to have the same semantics. Does the generated code differ and are there any pitfalls? Or is it just a matter or personal taste or best practices?
They work similarly, and the #Inject style is preferred when you have such an easy choice like in your example. However, this isn't always the case:
If B, which consumes A, is not under your control and not DI-aware, you will have no way to add the #Inject annotation.
If B is an interface, you will need #Provides (or #Binds in newer Dagger versions) to identify which implementation to use.
If you choose not to use your Dagger object graph for every injected parameter, you can call the constructor manually whether it is marked #Inject or not. This might be the case if you want a specific instance or constant as a constructor parameter, but you can't or don't want to set up the binding for the whole object graph.
#Provides allows you to effectively create a factory method, with everything that allows. This is a great way to change which instances are included in your graph, or to effectively add to the class's constructor graph-wide if you can't (or shouldn't) change the class itself.
You can return an existing instance rather than a new one. Note that custom scopes (implemented in Dagger through subcomponents) might be a better fit for common cases, but if you have more complex instance control or use a third-party library that controls instances, you could put that into your factory method.
If you want your binding to return null sometimes that logic can live in a #Provides method. Make sure you annotate the injection sites as #Nullable.
You can choose between implementations with a factory method like a #Provides method, particularly if the choice depends on runtime environment.
You can run post-construction logic, including instance registration or initialization.

Categories

Resources