I'm developing project following MVP architecture. Usually, when I use recyclerView, my presenter controls Adapter data. But now I need to make recycler adapter with data from cache (or something like cache), recycler's size dosen't depends on cache size, so I make cache via HashMap where the key is - position of recycler, if there is an item in map then the data shows, else empty row with something like "add events" btn. And I can't realize where the place for that cache in such structure - Model (Dao or something like CacheManager) or in Adapter.
The cache idea is following: I have some types of events which store in database, every event modifying changes it in db - so the cache has to be updated either.
The main questions are: where to keep this cache and load it to adapter, how can i keep it sync with database changes.
P.S. Also I tries to use RX, so if it can be solved with it - would be very interesting to try.
P.P.S If it the Repository pattern is the way to solve - welcome. Read about it sometime ago.
Your problem doesn't sound like it's related to RecyclerView.Adapter -- and actually you shouldn't try to solve it within your Adapter: the responsibility of the adapter is to act as a bridge (or "adapter" ;-) ) between your data and view component. Giving it more responsibility would turn it into something that is not interchangeable with other Adapter implementations (you don't want this!).
You should probably find a clean way to abstract your data persistence. The in-memory cache should go into that abstraction. You mentioned the repository pattern, this would be good choice IMHO.
Your architecture should roughly look like this:
adapter -> repository -> |-> cache
|-> database
The repository combines the logic for data access (your DAO) and the cache handling (your CacheManager). The repository would always first check the cache and then fetch data from the database. It also updates the cache if non-cached data is fetched. Furthermore, it registers for updates on the database. As soon as the database notifies for changed data, the repository has the chance to update the cache and/or propagate the notification to the view. The important part is that the interface of the repository hides all this logic; it only offers access to data.
You then need to find a way to make your adapter work with your repository. I'd suggest Android's Loader mechanics. By this you get asynchronous loading and correct lifecycle handling for free. Also this nicely decouples adapter and repository.
If you need some inspirations on how to apply the repository pattern, have a look into the googlesamples/android-architecture Github. The Clean Architecture branch could be a good fit for you.
On a side note: try to find real (unique) keys of your data. Using the position within the data list is usually a bad idea and depending on the structure of your data will result in strange side effects in the view.
I think it's an architecture subject. Rx is out of subject. For example for a repository, Rx is just a way to implement it, and by extension, its cache.
A Google repository provide some samples of architecture. One of them contains an example of architecture with a basic mvp, and a repository who manage cache :
googlesamples/android-architecture/todo-mvp
Another example in the same repository with Rx :
googlesamples/android-architecture/todo-mvp-rxjava
We can be seen here that these two examples share the same scheme of architecture :
Related
I don't understand few things about repository in mvvm, also have seen multiple blogs and code templates. Every one of them doesn't match with other. So
Why there is needs to have multiple Repositories classes? Why single repository shouldn't handle all the data IN OUT for the app?
Why Repository shouldn't be a SINGLETON class in project?
What a repository('s method) should provide to viewmodel Result(Sealed class) or actual response from a api like list(or error)?
SharedPrefrences should be handled by Repository as well or not? if no why
I believe Repository should be that part of your code which should process all the data from multiple source whether be database or network or sharedprefs. So what do you think?
Purpose of repository is not only to manage requests/response but also provide a layer to keep responsibilities separated. If you have 2 modules (let's say Login and Registration), it is much better to keep repositories separated to keep things clean and simple rather than having a single repo with spaghetti code.
This point is opinionated. IMO you can have singleton repositories because they are stateless i.e they do not hold any kind of data which may cause conflict between multiple ViewModels/Modules.
Depends on you but it is much better to return Result. With Result you can handle Success Failure and Error Messages easily and return different Error Messages directly from repository rather than deciding in ViewModel what message to show. Messages can be either from server or internal exception messages.
SharedPreferences is a datasource. Yes you can manage preferences using repository pattern.
I have read a couple articles about clean architecture concepts here and here and others.
I think I understand what is going on but they didn't solve the question that I have.
I wonder if a use case can be called from data layer. if so, how? if not why?
UPDATE:
what I am trying to do is to track what is happening during each usecase with a event usecase. Sometimes, I need to collect those info in data layer as well.
Let's say I have
CollectEventUseCase
for that event usecase. I want to track if a person data lookup from network was fail but successful from database during the LookupPersonUsecase and I want to collect that information that lookup was failed from network but successful from database, is this a bad idea to call CollectEventUseCase in data layer? is this okay solution to this case? or whatelse should be the right approach?
Short Answer - No, in a CLEAN architecture the data layer should not be calling through to a use case as it would be breaking the separation of concerns. Data layer should only be concerned with data, not business logic which lives in a use case.
Long Answer
One main reason for implementing a CLEAN architecture is for implementing separation of concerns, meaning each layer addresses a specific concern in your app. Expanding on the photo in domain layer Android documentation, an app architecture might look like the picture below.
Where the domain layer would contain all of the business logic for presentation and use cases which can communicate with different sources in the data layer.
The data layer exists to serve as an abstraction over where data is coming from and that should be its only responsibility. Most apps data layers are fetching data from the network and then caching that data in a local database. So to best separate the concerns, a use case should only be concerned about interacting with different sources from the data layer and any business logic.
In Android paging library, how do I invalidate the subset of the dataset in a PagedList or a DataSource? I write PagedList or DataSource because I don't know which one I should call.
For example, I'm making a QA App called BufferOverflow.
I have a PageKeyedDataSource that load data over the network. It support pagination by the index on the API. In that App, I have a Top Question screen that has "infinite" list of top questions. Of course, the top question is updated periodically.
How to:
Update the dataset at page 1?
Update the dataset at page 10?
I understand that the benefit of using PagedList is it will do a calculation on which item is new/old, using DiffUtil. But, how do I tell it the dataset on certain key or position has changed?
Is what I'm asking impossible? Should I implement custom paging behaviour on my own?
Note:
I don't want to invalidate all the data.
adapter.getCurrentList().getDataSource().invalidate();
This will invalidate the whole data on data source. While I just want to invalidate certain part of the data.
I think API that I'm looking for is similar to RecyclerView.Adapter's
list.adapter.notifyItemChanged(0);
But of course, its not Adapter's responsibility to update the dataset. It's either on the PagedList, DataSource, or Dao classes..
The only way to solve this issue is to introduce a persistence layer with Room.
https://github.com/googlesamples/android-architecture-components/tree/master/PagingWithNetworkSample#paging-with-database-and-network
As per docs here: https://developer.android.com/reference/androidx/paging/DataSource#updating-paged-data
If you have more granular update signals, such as a network API
signaling an update to a single item in the list, it's recommended to
load data from the network into memory. Then present that data to the
PagedList via a DataSource that wraps an in-memory snapshot. Each time
the in-memory copy changes, invalidate the previous DataSource, and a
new one wrapping the new state of the snapshot can be created.
https://developer.android.com/topic/libraries/architecture/paging/data#consider-content-updates
As you construct observable PagedList objects, consider how content
updates work. If you're loading data directly from a Room database
updates get pushed to your app's UI automatically.
I'm building an app using MVP and I'm using a repository pattern.
I have a local data source, which is a DB where I store certain information.
I have a remote data source, in which using Retrofit, I make an API request. This request has a #Query, and that is a String that is stored in the SharedPreferences.
I thought of adding the SharedPreferences to the Repository, as a data source, but since the remote data source should make use of these SharedPreferences -which would be a different data source than the remote- I don't see this architecture so clear anymore.
Thanks a lot for the help in advance.
I think many people over complicate the idea of repositories. The Repository pattern is really just a specific type of the Facade pattern. It is just an abstraction layer which sits between the true data sources (Models) and the consumers.
Let's say we have a system that deals with weather data. In this case I see nothing wrong with having three Modal classes:
WeatherHttp
WeatherDb
WeatherPrefs
These can all be members of your repository class injected through the constructor. All three of these are hidden from the consumers (UI) behind a single repository class:
WeatherRepository
which might have a single public method:
public void getWeatherForecasts(Callback<List<Forecast> forecastCallback);
or Rx:
public Observable<List<Forecast>> getWeatherForecasts();
Behind the scenes of that method you might well make an http call to fetch the latest data, a database call to save the majority of the details and a shared prefs edit to save the timestamp of the last time the data was fetched. The idea is you are free to change the implementation at any time and the consumers don't care.
The single most important thing about implementing a repository is you must not leak the implementation details. No network classes, database DAOs or SharedPreference keys should be exposed by the public API of the repository.
My app will pull some json data when it is started and realistically, once the data has been pulled, I really won't need to pull it again for the duration of the user experience. The backend data may update a few times a day at most, so I think I would like to just grab the data upon app start and then use that same data for the duration and give the user an option to manually refresh the data. So, my question is, where/how should I store that data? I've got all of my data structures set up (classes and sub-classes). And there may be 200 or so instances of some of the classes. If I store everything as member variables in my activity_main class, it won't be available to other activities once the other activities are started. Storing them all in databases could be an option, but it sort of feels like overkill. I really don't need the data to persist between sessions. Is there a way to easily store it in memory and still have it easily accessible to all activities?
You should think about OS killing your app process on low-memory, so backing your data on disk is a good thing to do. Doing so you have an ability to show user data from disk cache while refreshing it in background from server.
Choosing the tool for data storage depends on the way you need to work with data.
Of course, there is an option to use Realm, but you should consider the fact that it is not the relational database. So if you have complex domain model with joins and other relational stuff for your business logic, I'd go with something other. It is thread-safe, also has migrations (but, as for me, migrarations are always pain, you just can not do anythig about it). Realm is supposed to be RxJava-friendly now (support added in v0.87) There are some disadvantages(part of them may already be fixed), but you should consider it before using.
As for more relational approach, there is SQLBrite library:
A lightweight wrapper around SQLiteOpenHelper which introduces reactive stream semantics to SQL operations.
It is not an ORM (but you can add some kind of it on top of SQLBrite if you wish: see this for more info). In fact, being alone this library is doing one thing (and doing it good) - it provides a mechanism for coordinating and composing the notification of updates to tables(Realm also has such ability) such that you can update queries(in SQL fashion) as soon as data changes. And it respects RxJava!
As an alternative to SQLBrite you can look at StorIO.
There are also lots of different ORM solutions, like GreenDAO, ORMLite etc.
But I'm pretty sure, one of the first two libraries (Realm or SQLBrite) will likely help you. So analyze your app, these libs and decide what fits better.
P.S. Great article on how RxJava would help you to work with data from different data sources (in-memory cache + disk cache + network) easily. Might be helpful!
I would still recommend a SQLite Databse, you can easily declare it as a 'in-memory' database, if that is what you want.
However.... I would be rather upset as a user of your application if it downloaded redundant data over and over. I would just recommend making a content provider and being done with it. This gives you access to a SyncAdapter, and defines clear boundaries between where code should go.
The 'trick' with making a good ContentProvider is to make good POJOs, that have methods to convert from POJO -> ContentValues and Cursor -> POJO(s).
Easiest thing is to do is store the json file in Apps data storage and parse the json every time you need.
But this is not recommended as it is costly to parse data every time.
Best option is to implement Realm (Replacement for Sqlite) which is very easy to implement and its amazingly fast.