Understanding scopes in Dagger 2 - android

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.

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.

Applying MVVM between ViewModel and Fragment/Activity interactions and communication

I am investing a lot of time into properly understanding of architecture components and the how everything fits into MVVM pattern. So far here's how I see things (without doing Dagger injection as I haven't got that far yet):
a) UserEntity is a class with #Entity annotation to handle Room's table creation
#Entity(tableName="users")
public class Users{
private long id;
private String name;
}
b) User pojo class in the model to use it around the app and has supplemental fields as needed.
public class User{
private long id;
private String name;
private List<Role> roles;
private Preferences preferences;
}
beside this there can be different pojos depending on what I need from db, for instance, UserWithRoles
c) UserDao takes care of getting or inserting/updating info in the room database. In here, for #Insert, #Update I can user the UserEntity but for #Query I can also use the pojo classes
#Dao
public abstract class UserDao{
#Insert
public abstract long insertUser(User user)
#Query("Select someFields from inner joined tables")
public abstract LiveData<List<UserRoles> getUsersWithRoles();
}
d) Have RepositoryUser as the repository between ViewModel and Dao
public class RepositoryUser{
private UserDao userDao;
public RepositoryUser(Application app){
Database db = Databaase.getDatabase(app.getApplicationContext);
userDao = db.userDao();
}
public LiveData<List<UserWithRoles>> getUsersWithRoles(){
return userDao.getUsersWithRoles()
}
}
e) UserWithRolesViewModel to be available for the fragment that shows the list with users and their roles
public class UserWithRolesViewModel extends AndroidViewModel{
private RepositoryUser repositoryUser;
public UserWithRolesViewModel(Application app){
super(app);
repositoryUser = new RepositoryUser(app);
}
public LiveData<List<UserWithRoles>> getUsersWithRoles(){
return repositoryUser.getUsersWithRoles()
}
}
f) In my fragment I can do something like:
public void onCreate(...){
viewModel = ViewModelProviders.of(this).get(UserWithRolesViewModel.class);
}
public View onCreateView(...){
viewModel.getUsersWithRoles().observe(...)
public void onChanged(...){
adapter.setData(...);
}
}
However, there are some pieces that are missing. From my understanding according to MVVM the view should only be responsible for showing info, so no actual logic or even handling to be made inside the fragment or activity. At this point I have 2 questions:
On the regular way, I would create an interface, for instance onFragmentAction and implement it in activity. Then on fragment when I wanted to inform the activity to do something, I would do callback.onFragmentAction(params) and the onFragmentAction in the activity would fire and act accordingly. How is this scenario handled in MVVM? How does a fragment talk to it's parent activity?
On the regular way I would have inside the fragment's onCreateView, inflate the layout, use findViewById to get the views and use, for instance textView.setText() or button.setOnClickListener(). How can this be done in MVVM? Use DataBinding?
On the regular way, I would create an interface, for instance
onFragmentAction and implement it in activity. Then on fragment when I
wanted to inform the activity to do something, I would do
callback.onFragmentAction(params) and the onFragmentAction in the
activity would fire and act accordingly. How is this scenario handled
in MVVM? How does a fragment talk to it's parent activity?
For interaction, you can create ViewModel that is shared between Fragment and Activity. In that case you have an abstraction, where you push some data in ViewModel LiveData where it gets an event whoever listens to same ViewModel.
For example this method is recommended for Fragment to Fragment communications, but I think it also fits Fragment to Activity.
On the regular way I would have inside the fragment's onCreateView,
inflate the layout, use findViewById to get the views and use, for
instance textView.setText() or button.setOnClickListener(). How can
this be done in MVVM? Use DataBinding?
You can use either DataBinding or Kotlin Android Extension, both should be fine with MVVM.
Through DataBinding should be better, since it will reduce boilerplate.
But personally I find Kotlin Android Extensions also very clean.

Where to put appending of required data (to be passed on API) globally?

Clean Architecture Question
I have many form activities that has 1 common data that must be appended during submission, my question is, where will the logic must be placed?
Domain or Presentation Layer?
For Presentation:
I'll create a BaseFormActivity that has a method of inserting the needed data on a form that is child of BaseForm which contains the needed data globally.
BaseForm: (to be extended by all forms)
public class BaseForm {
private String globalData;
//getter setters...
}
BaseFormPresenter:
public class BaseFormPresenter extends BaseFormMvpView {
private final GetGlobalDataInteractor mGetData; //to be injected, this is a use case
public void getGlobalData() {
mGetData.execute()
.subscribe(data -> {
getMvpView().showGlobalData(data);
}); //just for the sake of simplicity
}
}
BaseFormActivity: (which is extended by all activity that handles form)
public abstract class BaseFormActivity implements BaseFormMvpView {
#Inject
BaseFormPresenter mPresenter;
//onCreate(), etc
}
SpecificFormActivity: (extends BaseFormActivity)
public class SpecificFormActivity extends BaseFormActivity {
private SpecificForm mForm; //extends BaseForm
//onCreate(), etc
#Override
public void showGlobalData(String data) {
mForm.setGlobalData(data);
}
//then ill just call the presenter to get the global data before submitting
}
For Domain:
SubmitSpecificFormInteractor: (Sorry for the coding, it is just a representation on what I'm thinking to do)
public class SubmitSpecificFormInteractor extends SingleUseCase<Return, Param> {
//to be injected
GlobalRepository mGlobalRepository;
SpecificFormRepository mFormRepository;
//some initialization
public Single<SomeResponse> buildObservable(#NonNull String specificFormData, String anotherSpecificFormData) {
return mGlobalRepository.getGlobalData()
.map(globalData -> SpecificFormDto.create(
specificFormData, anotherSpecificFormData, globalData)) //create the dto then append global data
.flatMap(specificFormDto -> mFormRepository.submit(specificFormDto)) //then submit data
}
}
I'm thinking of placing it on the domain layer (you can see that it is much isolated, but I'll have to do it on all form submissions, which is redundancy), but still I just want to make my decision solid. Refactoring is time consuming. Hope you understand my point here, specially on my pseudocode-like coding. Feel free to comment if there's something hard to understand. Thank you.
In Clean Architecture all business rules go to use case interactors. The main goal is to keep the business rules independent from any details - from any framework - that includes android as well.
The Clean Architecture then uses "interface adapters" (in UI part called "controllers" and "presenters") to map between data most convenient for the inner circles and data convenient for the frameworks.
So in ur case u should go for ur second proposal - even if that means that u have to call the interactor from multiple places and have to map some data types. it is worth the benefits (business rules free from details).
For a more detailed discussion about use case interactors, controllers and presenters pls refer to my posts here: https://plainionist.github.io/Implementing-Clean-Architecture-UseCases/
and here https://plainionist.github.io/Implementing-Clean-Architecture-Controller-Presenter/

Android - keep webservice results in memory

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

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