11

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.

shagi
  • 629
  • 8
  • 25

2 Answers2

4

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.

Brian
  • 2,130
  • 22
  • 29
  • But isn't it a bad practice to use Repository from Adapter without Presenter? I've implemented my cache in DAO class and called it from Adapter but "I've got a bad feeling about this". P.S. My key not a simple position, it just depends on position. f(position)=key – shagi Apr 25 '17 at 06:26
  • You're correct. That's why I suggested to use Loaders: (1) your presenter deals with the Loader (initialization, result callback, etc); (2) the Loader loads data from the repository; (3) the presenter takes the Loader's result callback and hands over the data to the adapter – Brian Apr 25 '17 at 12:43
  • Ok instead of Loadres I use Rx. So I've got the idea of initialization, but what about actions from ViewHolders? Should I create interface in my adapter and then implement in Fragment\Activity or Presenter oO? Or there is the better way to communicate? RxBus(EventBus)? – shagi Apr 25 '17 at 13:09
  • Now you're getting quite into details here ;-) Sure, you'll need to propagate UI events to your presenter somehow. If you're using Rx already, it sounds reasonable to also leverage it for that. But how to handle UI events using Rx is probably worth a new question (I bet there are already some good ones). – Brian Apr 25 '17 at 14:30
1

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 :

enter image description here

compte14031879
  • 1,531
  • 14
  • 27
  • yes, I've understood the idea of caching data) The last question that is not clear for me is how to connect Adapter and Presenter? The comment to previous answer – shagi Apr 26 '17 at 06:32
  • @shagi IMHO, in your presenter you call your repository or your business logic to get data (you can use Rx here). The presenter call a method from fragment to transmit data. Fragment implements the adapter and it's logic. The adapter/fragment listen UI events (you can use Rx here) and call a method of presenter. The presenter contains logic to do something after UI events. – compte14031879 Apr 26 '17 at 07:19
  • 1
    Details from the same example with Rx. Fragment and adapter (no Rx used here ! If you use rx here, it's for UI events) : https://github.com/googlesamples/android-architecture/blob/todo-mvp-rxjava/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksFragment.java - Presenter (Rx used here to subscribe to observer of repository) : https://github.com/googlesamples/android-architecture/blob/todo-mvp-rxjava/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksPresenter.java – compte14031879 Apr 26 '17 at 07:27