May 7 | 7:20 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
May 7 | 7:30 PM |
Jeff | has entered the room |
Mark M. |
hello, Jeff!
|
Mark M. |
how can I help you today?
|
May 7 | 7:35 PM |
Jeff |
mark, nice to see you.
|
roberto | has entered the room |
Mark M. |
Jeff: you can see me? I thought my webcam was covered... :-)
|
Mark M. |
and hello, Roberto!
|
Jeff |
figuratively lol
|
Mark M. |
Jeff: you got here first, so... do you have a question?
|
Jeff |
I want to learn proper mvvm pattern in my app. should i read exploring android or android architecture components. I noticed chapters on viewmodel in both.
|
Mark M. |
I do not cover MVVM specifically, but I do cover the Jetpack ViewModel class and related supporting classes
|
Jeff |
or do you just talk about the viewmodel and not mvvm pattern
|
Mark M. |
*Android's Architecture Components* is a first-generation book, so it uses Java and the older Android Support Library classes
|
Mark M. |
all else being equal, you are better off with *Exploring Android*, as it is newer, shows the Jetpack/AndroidX classes, and shows both Java and Kotlin
|
Mark M. |
but yes, I talk about ViewModel and not MVVM
|
Mark M. |
my samples are more MVI-ish
|
Jeff |
I've been reading AAC, how are your examples different from MVVM? For example, I noticed you also use models persisted in room and activity as a view. Your activity contains a viewmodel. Can you explain why it does not follow mvvm
|
Mark M. |
I would phrase it more as: I am not expert on all the various GUI architecture patterns
|
May 7 | 7:40 PM |
Mark M. |
so I make no claims about whether my code strictly follows any particular pattern
|
May 7 | 7:40 PM |
Jeff |
sure thanks
|
Mark M. |
besides, from what I can tell, different people have different takes on what exactly MVVM really means
|
Mark M. |
so I try to stay out of those fights
|
Mark M. |
I'm here to show you how to use the SDK and its classes to get stuff done :-)
|
Mark M. |
let me take a question from Roberto, and I'll come back to you in a bit
|
Jeff |
ok
|
Mark M. |
Roberto: your turn! how can I help you today?
|
roberto |
Mark, greetings from New Zealand, I'm returning back to the subscription after a couple of years of pause. I've been following your job, big fan here.
|
roberto |
My question is a bit similar :)
|
Mark M. |
thanks for the kind words! and good morning! :-)
|
Mark M. |
so, your question is: where do I cover MVVM?
|
roberto |
I got an app following MVVM, with Pagination Library and the Repository using database as single source of truth
|
roberto |
Just as Google's Pagination Code Labs covers
|
Mark M. |
is this using Room?
|
roberto |
Yes, it is
|
roberto | |
roberto |
And this is the codelab https://codelabs.developers.google.com/codelabs...
|
May 7 | 7:45 PM |
roberto |
Most of project's I've seen implement something similar here
|
roberto |
which covers the basic cases: updates from network and offline support
|
roberto |
my doubt is what happens when updates comes from UI
|
roberto |
and I'm looking for the patterns that I could be following
|
roberto |
This is a tricky question... but I thought the chat would be the way because rather than a question it may be a conversation/discussion
|
Mark M. |
I guess I am uncertain what your concern is -- if it with respect to the Paging support, it should be just a matter of calling the appropriate DAO functions to manipulate the database, and you should get fresh data automatically
|
Mark M. |
and your viewmodel would probably call functions on the repository to cause those database changes
|
roberto |
Even if the data updates comes from UI events?, such as a swipe to delete event on UI?
|
Mark M. |
sure
|
Mark M. |
my take on the classic to-do app takes that basic approach, minus the paging (because if you have that many to-do items, you probably don't have time to read my books...)
|
May 7 | 7:50 PM |
roberto |
Yes... I remember these. Are you using polling in any example?
|
Mark M. |
no, it's all pushed from Room
|
roberto |
Unfortunately, I've to poll the backend to see if data has changed on server side
|
roberto |
And that's creating me race conditions
|
Mark M. |
ah, sorry, I misunderstood your scenario
|
roberto |
Oh nothing to apologise.. it's a bit complex
|
Mark M. |
the big decision, AFAIK, is what is the system of record -- earlier, you said that is the database
|
Mark M. |
that implies that your UI changes still change the database, and you are doing something to arrange to also update your backend, plus find out if the backend has changed data
|
roberto |
Exactly. That's the problem.
|
Mark M. |
by "race conditions", what do you mean? is this something low-level like a deadlock, or something higher-level like two parties updating the same data?
|
roberto |
Yeah, race conditions may be missused there. I'm talking about data inconsistency
|
Mark M. |
well, if the local database is the single source of truth, then that implies that the server loses
|
Mark M. |
if that would be a problem, then the server is really the source of truth, and the local database is a cache
|
May 7 | 7:55 PM |
Mark M. |
you can certainly try to get elaborate, with data-merging algorithms (think git), depending on your needs
|
roberto |
Alright. Makes sense. Any litearature discussing this scenarios? is "synchronization pattern" the key words here?
|
roberto |
you're mentioning data-merging algorithms. I guess my problem is I don't know the name of my problem :)
|
roberto |
then I cannot take advantage of existing literature
|
roberto |
:)
|
Mark M. |
synchronization, in this context, tends to be the data-merging approach, where you're trying to "have your cake and eat it too" and have both copies by systems of record
|
Mark M. |
personally, I haven't had to work with that sort of scenario in years, so my terminology is somewhat dated
|
Mark M. |
and it's a tough nut to crack, unless you are using some system designed around that problem (e.g., Couchbase)
|
Mark M. |
pretty much everything I've had to do in recent years has either had the server as the system of record (with a local cache, perhaps) or considered the server to simply be a place for backups
|
Mark M. |
so "data synchronization" is a fine term, but I can't really point you in the direction of current literature on the topic
|
roberto |
The problem I've to solve is pretty much the same as todo list
|
roberto |
being updates from different source and server is not able to do updates by push
|
Mark M. |
let me take another question from Jeff, and I'll swing back to you shortly
|
roberto |
Sure, thank you very much Mark.
|
Mark M. |
Jeff: back to you! do you have another question?
|
May 7 | 8:00 PM |
Jeff |
yes. which datasource should i use for the new pagination library in jetpack if I have using an api taking in an offset and limit, returning a Single type from RxJava?
|
Mark M. |
I pretty much failed in all my attempts to get Paging to work, other than when Room returns the DataSource.Factory, so my advice is going to be limited
|
Mark M. |
offset and limit sounds like a PositionalDataSource, though
|
Mark M. |
your offset and limit map to loadSize and startPosition in PositionalDataSource.LoadRangeParams
|
Mark M. |
however, I forgot that PositionalDataSource needs a countable data set, and I don't know if you know the data size
|
Mark M. |
in that case, you would need to disable placeholders if you still want to use PositionalDataSource
|
May 7 | 8:05 PM |
Mark M. |
the Paging library and I didn't get along, so I don't have an example of your scenario to show you, unfortunately :-(
|
Mark M. |
but, that was back when Paging first came out, and I just haven't had a need to get back into it beyond Room since then
|
Mark M. |
let me take another question from Roberto, and I will come back to you in a bit
|
Mark M. |
Roberto: back to you! do you have another question?
|
Jeff |
https://medium.com/@antobeslie25/android-recycl... discusses using a PositionalDataSource. It seems you load data using a function returning List<Model>. I have a a Single<List<Model>, so it sounds like I would subscribe to that Single inside the loadData method
|
May 7 | 8:10 PM |
Mark M. |
(Jeff: there are probably recipes for dealing with RxJava for this sort of Web service API and mapping that into Paging -- subscribing in loadData() would make me nervous, in terms of when you would dispose of the subscription)
|
Jeff |
yes, I am just trying to find a proper example
|
Mark M. | |
Mark M. |
here, they have their DataSource.Factory take a CompositeDisposable in its constructor, which would be one way of dealing with my concerns over when to clean up the subscription
|
Mark M. |
Roberto: if you have another question, chime in!
|
roberto |
Mark, no questions atm, I'm on the same issue as before. I don't know if there's anything else to talk about that.
|
Mark M. |
"being updates from different source and server is not able to do updates by push" -- unless the server can tell you what data changed by some means, I don't see how you can consider the local database to be a source of truth
|
May 7 | 8:15 PM |
Mark M. |
if either of you have another question, go right ahead!
|
Jeff |
suppose I use an endless scroll listener instead. What is considered good practice for restoring a paginated list? If I am on page 20, do I persist pages 1-20 and restore them from a database? Or do I simply make a network requestagain?
|
May 7 | 8:20 PM |
Mark M. |
that probably depends on the network cost (big bandwidth vs. small) and the expected usage pattern (lots of scrolling vs. not much)
|
Mark M. |
not much scrolling suggests just an LRU memory cache to me
|
Mark M. |
lots of scrolling and small bandwidth would lead me to just making the network request again, perhaps with some in-memory caching
|
Mark M. |
lots of scrolling and large bandwidth suggests some amount of on-disk caching
|
roberto |
"unless the server can tell you what data changed by some means, I don't see how you can consider the local database to be a source of truth" Alright, then you'd use database just as cache and Network would be the only source of truth then?
|
Jeff |
why use memory instead of persisting? I read we should not use bundle memory in this case because bundles are designed to store small amounts of information, not pages
|
Mark M. |
Roberto: yes, because I don't know how you could keep the local database up to date other than by clobbering it with what the server has
|
Mark M. |
Jeff: I wasn't referring to bundles, but rather caching in the repository layer
|
Jeff |
ok. how do I know when it is ok to save and restore a list using a bundle. I tried it using long lists and it was never an issue
|
roberto |
Mark: thank you very much. This was a very insightful conversation, I may return back with more questions on this subject.
|
Mark M. |
technically, you don't know it is a problem until you crash
|
Mark M. |
Roberto: sounds good!
|
May 7 | 8:25 PM |
Mark M. |
Jeff: my general rule of thumb is that that saved instance state and Intents should be small
|
Mark M. |
a few KB, fine
|
Mark M. |
tens of KB, maybe
|
Mark M. |
100s of KB, you're eventually going to crash
|
Jeff |
ok cool
|
Jeff |
I appreciate it; talk to you later mark
|
Mark M. |
sure!
|
Mark M. |
BTW, both of you might be interested in caching solutions like Store: https://github.com/dropbox/Store
|
Mark M. |
(though it's now Kotlin-centric, and I'm not sure whether you are using Kotlin yet)
|
May 7 | 8:30 PM |
Mark M. |
and that's a wrap for today's chat
|
Mark M. |
the transcript will go up on https://commonsware.com/office-hours/ shortly
|
Mark M. |
the next chat is Thursday at 4pm US Eastern (sorry Roberto, not very NZ-friendly!)
|
Mark M. |
have a pleasant day, and stay healthy!
|
Jeff | has left the room |
roberto | has left the room |
Mark M. | turned off guest access |