Mark M. | has entered the room |
Mark M. | turned on guest access |
May 26 | 7:30 PM |
Kai H. | has entered the room |
Kai H. |
hi there
|
Mark M. |
hello, Kai!
|
Mark M. |
how can I help you today?
|
Kai H. |
I have a rather extensive architecture question about (contextual) action bars
|
Mark M. |
if you mean "action modes", I haven't played with those in a few years, so I may be a bit rusty, but go ahead!
|
Kai H. |
I have a list with item of different types
|
Kai H. |
Like pictures, pdfs, word documents etc.
|
Kai H. |
I can long click the items to select and I can select multiple items
|
Kai H. |
Depending on which types of items are selected, I wanna offer some actions in the ActionBar.
|
Kai H. |
I wonder how to design and organize the classes
|
May 26 | 7:35 PM |
Mark M. |
I don't really have enough to go on to really help with that -- for example, what classes are you referring to?
|
Eric | has entered the room |
Kai H. |
The classes needed to do something like that
|
Mark M. |
(BTW, hello Eric! I'll be with you shortly!)
|
Eric |
ok mark
|
Mark M. |
Kai: um, well, that's kinda broad
|
Mark M. |
your first challenge is in deciding how to track the multiple selections
|
Kai H. |
I feared so
|
Mark M. |
Google has a recyclerview-selection library
|
Mark M. |
I tried it once and didn't have a lot of luck with it
|
Mark M. |
I show how to roll your own in *The Busy Coder's Guide to Android Development*, in the Advanced RecyclerView chapter
|
Kai H. |
I think the usual way is to have a list of selected items that are just added and removed on the callback in the adapter.
|
Mark M. |
and I'm sure there are third-party libraries for it, though I haven't had a need for this recently so I can't name any
|
Mark M. |
well, your ViewHolder is going to need to know whether an item is selected or not, so it can render it appropriately (e.g., alternative background color)
|
Mark M. |
what recyclerview-selection, my book samples, and stuff try to do is decouple the "track the selections" somewhat separately from the model data
|
Mark M. |
that way, the selection logic can be reusable
|
Mark M. |
but that's tricky to get right, and I don't think my book examples necessary handle all the edge cases
|
Mark M. |
(er, necessarily handle)
|
May 26 | 7:40 PM |
Mark M. |
your activity/fragment that shows the toolbar will also need to know about the number and type of selections, so it can make your choices about what to show in that toolbar for options
|
Mark M. |
which is why figuring out how you are going to track what is and is not selected is your first order of business, IMHO
|
Mark M. |
now, a lot of the early RecyclerView work predated the Jetpack ViewModel system
|
Mark M. |
(such as my aforementioned book examples)
|
Mark M. |
so there may be some cleaner options nowadays than what we had, though RecyclerView itself makes some aspects messy
|
Mark M. |
but, figuring out how to handle multiple selection in a RecyclerView with a modern architecture alone would be a thing I'd probably spend a few hours researching
|
Mark M. |
let alone what the current approach is for a contextual action bar
|
Kai H. |
Ok
|
Mark M. |
(cause the system action modes never played well with Toolbar -- I'm assuming you'd use two Toolbar widgets and animate between them)
|
Mark M. |
so, I'm sorry, but I don't have an "out of the box" solution to offer you on that subjec
|
Mark M. |
let me take a question from Eric, and I'll swing back to you in a bit
|
Mark M. |
Eric: your turn! how can I help you today?
|
Eric |
I am reading "Elements of Room", "Relations in Room". I have a data class doubling as a Retrofit model and Room entity. My api returns a List<Category>. My Category data class contains a List<Book> for retrofit. I marked it Ignore.
|
Mark M. |
"I have a data class doubling as a Retrofit model and Room entity" -- that's unlikely to turn out well, unless you have complete control over the server
|
May 26 | 7:45 PM |
Mark M. |
if you do control everything, then you can force yourself to keep things consistent
|
Eric |
I reuse it because my function gets data from an api and persists it into room. I am confused about any alternative using separate classes for retrofit/room
|
Mark M. |
if you do not control everything, I strongly recommend that you treat your Retrofit classes as separate DTOs (data transfer objects) from your Room entities
|
Eric |
my question is actually my associated Books do not get persisted
|
Kai H. | has left the room |
Eric |
I followed the example you gave by creating a Books entity. I created a CategoryAndBooks data class
|
Mark M. |
well, properties with @Ignore get ignored :-)
|
Mark M. |
OK, that sounds fine
|
Eric |
I see. Is there a way to persist the associated books using the pattern you demonstrated
|
Mark M. |
but you need to persist the Book objects yourself
|
Mark M. |
is this a one-to-many or a many-to-many relation?
|
May 26 | 7:50 PM |
Eric |
in CategoryDao I only insert categories. Are you saying I need a BooksDao to inisert books?
|
Mark M. |
you need DAO functions for your books, yes
|
Eric |
I think it is one to many. my api returns a List<Category>. One Category is related to many Books
|
Mark M. |
and a Book has only one Category?
|
Eric |
yep
|
Mark M. |
then yes, that is a one-to-many relation
|
Mark M. |
if you look at my chapter on "Room Relations", my "One-to-Many Relations" section shows a Bookstore DAO
|
Eric |
I ignore my List<Book> in my Category because as you write in your book it cannot persist similarly to retrofit deserializing nested model
|
Eric |
yes I follow it. It does not contain a Dao for books iirc
|
Mark M. |
Bookstore handles Category and Book
|
Mark M. |
a DAO is not limited to a single entity type
|
Mark M. |
so, my Bookstore offers save() functions for Category and Book
|
Eric |
can you explain suspend?
|
Mark M. |
and my test code shows saving a Category and saving Book objects in separate calls
|
Mark M. |
the suspend keyword is part of Kotlin coroutines
|
May 26 | 7:55 PM |
Mark M. |
I happen to be using Kotlin and coroutines in this book, as it is the long-term direction that Android is going in terms of thread management, but Room is not tied to coroutines
|
Mark M. |
as part of the Warescription, you also have access to *Elements of Kotlin Coroutines*
|
Mark M. |
you might want to read the first couple of chapters there, to get familiarized with the basics, like the role of the suspend keyword
|
Mark M. |
also, *Elements of Kotlin Coroutines* has an appendix on migrating an app from using RxJava to coroutines
|
May 26 | 8:00 PM |
Eric |
ok great
|
Mark M. |
while RxJava may not matter to you, the resulting app demonstrates using Room and Retrofit together, with separate classes
|
Mark M. |
since Kai departed, if you have other questions, go right ahead!
|
Eric |
thank you. I am rereading your example and applying it.
|
May 26 | 8:10 PM |
Eric |
it looks like if you want to persist a categorie's associated books you execute 2 queries, 1 to insert the category followed by 1 to insert related books
|
Mark M. |
yes
|
Mark M. |
remember: a Category does not have any List<Book>
|
Mark M. |
that is one of the reasons why I do not like using the same classes for Retrofit and Room
|
Mark M. |
in Retrofit, you might very well be in a situation where Category has a List<Book>
|
Eric |
So I have a completable insertCategories and a completable insertBooks. For each category, I want to persists category and its related books
|
Mark M. |
you would need to set up a DAO function that takes those books (List<Book>, vararg Book, Array<Book>, whatever) and arrange to call that function
|
May 26 | 8:15 PM |
Eric |
yes. if my Category contains a List<Book> I ignore it and created a Book entity. Are you saing it doesnt work? After I get a List<Category> I want to save my category and its List<Book>
|
Eric |
ok. so I got 2 Completables
|
Mark M. |
"Are you saing it doesnt work?" -- I am saying that Room is going to ignore that List<Book>
|
Mark M. |
and I fear that it will confuse you a fair bit going forward
|
Mark M. |
if you want to persist a Category and its List<Book>, the safest thing to do is to have a single @Transaction function on your DAO that turns around and calls the DAO functions for those individual persistence operations
|
Mark M. |
that way, the entire thing succeeds or fails as a whole
|
Mark M. |
I have a section on @Transaction in "The DAO of Entities" chapter: https://wares.commonsware.com/app/internal/book...
|
May 26 | 8:20 PM |
Eric |
ok, so it first persists book, then iterates its List<Book> to persist every associated book?
|
Mark M. |
well, a DAO function can take a List<Book>
|
Mark M. |
so, you would have one DAO function that takes a Category and a separate DAO function that takes a List<Book>
|
Mark M. |
and, ideally, you would wrap those two calls in a single function with the @Transaction annotation, and that function is basically the "public API" that you call to save a Category
|
Eric |
ok great. You mentioned retrofit and room using distinct models. I would need to convert between a retrofit data class and room data class?
|
May 26 | 8:25 PM |
Mark M. |
probably
|
May 26 | 8:25 PM |
Mark M. |
I say "probably" because it depends a lot on what your app is doing
|
Mark M. |
I typically think of three separate data representations:
|
Mark M. |
1. the real model objects that I want to use in the UI
|
Mark M. |
2. the Room entities
|
Mark M. |
3. the Retrofit responses
|
Eric |
I appreciate it; you gave me lots to consider today. take it easy
|
Mark M. |
these will have similar pieces of data but will have them organized differently, to take into account Room's restrictions and your server team's Web service API design
|
Mark M. |
I wish you luck with your project, and I hope to see you in a future chat!
|
Eric | has left the room |
May 26 | 8:30 PM |
Mark M. | turned off guest access |