How to build a data layer
HTML-код
- Опубликовано: 20 авг 2024
- In this workshop, you'll learn about the data layer and how it fits into your overall app architecture. Build the data layer for a TODO app which creates, stores, and updates tasks. Explore repositories, data sources, and data models. Last, discover how to expose data using streams, and how to test that your data layer is behaving as it should.
Resources:
Build an offline-first app → goo.gle/offlin...
About the data layer → goo.gle/androi...
Build a Data Layer Codelab → goo.gle/androi...
Speaker: Don Turner
Watch more:
Watch all Android and Play Sessions → goo.gle/IO23_a...
Watch all the workshops from Google I/O 2023 → goo.gle/IO23_w...
Watch more Mobile Sessions → goo.gle/IO23_m...
All Google I/O 2023 Sessions → goo.gle/IO23_all
Subscribe to Android Developers → goo.gle/Androi...
#GoogleIO #Featured #AndroidDevelopers #tools
Have a burning question? Leave it in the comments below for a chance to get it answered by the Android team. 👇👇🏻👇🏿👇🏽 👇🏾👇🏼
Do we have any video on UI layer and domain layer like this...
Please enable Dark Mode in your codelabs, difficult to read on white screens
Please pay attention to the screen recording settings or the final video rendering settings.
There are annoying video encoding artefacts when showing the studio.
You could work on colour scheme entire video also(tone and brightness).
Many of us work all with screens and our eyes become too sensitive.
Great!! tutorial
Lets say we have also Groups. Each Task assigned to one or more groups. It could be separate table task_id | group_id. Now we need display at one screen all tasks with info about all their groups. So we need to receive list of tasks, list of groups and list of task-groups relations. How our Data layer should looks like? Do we need introduce Domsin layer(UseCase) here?
In this case, I'd suggest updating the Task model to include a List, then update your SQL query inside TaskDao to perform a join on your Groups table to return the groups which each Task is associated with. I wouldn't introduce the domain layer (UseCase) for this simple addition, however, if you have significant business logic for filtering and/or sorting those tasks then it may be worth introducing one or more UseCases to contain that logic.
In the Now in Android app, we have a similar one-to-many relationship where NewsResources can have up to 5 Topics. In this case, we introduced a TopicsRepository because we have a screen which displays all the Topics independently of the NewsResources which are associated with them github.com/android/nowinandroid/blob/d20910595522867067e7a7d1c542e95fc5f5155d/core/data/src/main/java/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt#L23.
Can anyone help me understand at 16:22, why observeAll() is not a suspend function? Is that because it returns a flow? Can this function to be suspend?
Nice code lab.
in the repository -> createTask, why do you create a Task first, then convert to LocalTask? Why not LocalTask directly?
The Task model encapsulates the logic of creating a task (e.g. with isCompleted = false default value). If you were to create LocalTask directly then you would bypass that logic. To be fair, this isn't ideal since you _always_ need to create a Task before you can create a LocalTask so it would probably be better to encapsulate this creation logic inside a separate function inside LocalTask e.g. newLocalTask which includes those defaults.
About the refresh() method, fetching remote data, and deleting the local one could be done parallel, in two separate coroutines.
The `refresh` method could be implemented in a number of ways, however, your proposed way is even more risky than the one implemented here, namely because the remote job could fail whilst the local data is deleted, leaving your user with no data at all. For any production app, I'd suggest following the guidance here: developer.android.com/topic/architecture/data-layer/offline-first
@@donturner1928 You are right about that, I was thinking about performance only.
While the refresh method is not the main point about this video, it is important to know that in production apps this refresh method needs to run a transaction in room to avoid race conditions, specially if run in viewModelScope which detaches according to its lifecycle. This type of conflict can be categorized as WAW (write after write) if we consider that the first delete is a write. The actual description of this conflict happens if exactly after we delete all locals tasks we detach from viewModelScope so we missed the insertion and then we no longer have local tasks. The right mitigation would be to run a @Transaction in room.
Thank you for the good content. The code font is very pretty. May I know the name of the font?
Google Sans Mono. It's a proprietary font.
google doesn't understand what the repository pattern is
How do you handle API Failures? how do you notify the tasks observers that our api failed?
That's the neat part... It doesn't fail. No more code to worry abou👓🤏
Why didnt we use taskrepository interface which is usefull for creating fake repository? Also used for decoupling. Is there any reason?
can your team reset Write WebRtc for android document?
Thanks for the video, is there a repository with the code shown in the video? Regards.
It's in the video description but here you go: github.com/android/architecture-samples
Anyone get turn off syntax high light when access file has @Dao like me :((
Totally unrelated questions. What is the new font that Google is using in their slides? And in this tutorial that shown in Android Studio?😃 Looks nice and clean.
Google Sans Mono. It's a proprietary font.
Please use dark mode for the presentation slides
Is there any video to test retrofit network data?
You'd usually do this in manual QA testing to avoid making your automated tests slow and/or flaky
@@donturner1928 You mean we shouldn't write any test and mock for network?
@@saeednoshadi3922 I mean you should use a fake network class when testing subjects which rely on the network.
Here: 15:12 I’m thinking the functions to map local to domain/repository data types should belong in the repository/domain, so instead of implementing the `LocalTask.toExternal` extension function rather do it with the `DefaultTaskRepository().observeAll()` function:
```
…
return localDataSource.observeAll().map { Task(it…) }
```
No? 🤔
By abstracting the mapping logic into its own function you guarantee that any changes to `Task`, `LocalTask` or the mapping logic can be done in a single place. It also makes this logic easier to test than if it's hidden inside a repository function.
@@donturner1928why are they extension functions and not actual members of LocalTask?
Video is flickering too much...😵💫😵💫😵💫
@Android Developers How to pass errors/exceptions from Data layer to UI?
Return a result type?
In the UI, when you collect from the flow use `catch` to catch any exceptions. Throwing/handling exceptions is preferable to using a Result wrapper because it avoids the overhead of wrapping and unwrapping the actual result.
@@donturner1928 I really wouldn't recommend returning exceptions, especially for known errors from your data layer 🤔
How much do you want to stand behind this suggestion here? Does Now In Android do the same at any part of the code base so we could take a look to see what you mean?
Plus then you got the situation where the exception isn't specified anywhere, and you can very well forget to catch bringing issues to your project. A return type forces you to handle it, since you never want to ignore it in the first place. I don't know, I could be wrong here but I can't imagine myself preferring that approach.
Yeah, that's a really good point, thanks. My initial answer wasn't comprehensive. I should have added that Result is a preferable choice in situations where you want to force the UI to handle known errors, and this is indeed what we do in Now in Android. In fact, we convert any exceptions in a flow into a Result.Error type which contains the exception: github.com/android/nowinandroid/blob/d20910595522867067e7a7d1c542e95fc5f5155d/core/common/src/main/java/com/google/samples/apps/nowinandroid/core/result/Result.kt#L24