Skip to content

Задача по Coroutines #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Coroutines Homework

## Задача

### Перейти с коллбеков на саспенд функции и корутины

1. Поменять возвращаемый тип в `CatsService` и добавить модификатор `suspend`
Expand All @@ -19,3 +21,17 @@
1. Реализовать наследника `ViewModel` и продублировать в нем логику из `CatsPresenter`, с необходимыми изменениями. Используйте `viewModelScope` в качестве скоупа.
2. Добавить логирование ошибок через CoroutineExceptionHanlder. Используйте класс CrashMonitor в качестве фейкового CrashMonitor инструмента
3. Создать sealed класс `Result`. Унаследовать от него классы `Success<T>`, `Error`. Использовать эти классы как стейт необходимый для рендеринга/отображени ошибки


## Решение

Приложение переведено на coroutines.

В качестве сервиса получения случайных картинок используется [TheCat](https://developers.thecatapi.com/view-account/ylX4blBYT9FaoVd6OhvR?report=bOoHBz-8t), позволяющий получить список случайных изображений.

Представлены три варианта реализации задачи, находящиеся в ```app/src/main/java/otus/homework/coroutines/presentation```

1. ```/mvp``` - реализация *custom view* на основе паттеран `MVP`
2. ```/mvvm/parent``` - реализация *custom view* на основе паттерна ```MVVM``` и `findViewTreeViewModelStoreOwner`
3. ```/mvvm/owners``` - реализация *custom view* на основе паттерна ```MVVM``` и *custom* ```ViewModelStoreOwner, LifecycleOwner```
4. ```/mvi``` - реализация *custom view* на основе паттерна ```MVI``` и библиотеки ```MVICore```
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Добавил реализацию на MVI, но скорее в качестве эксперимента, там coroutine толком нет

29 changes: 27 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ plugins {
}

android {
compileSdkVersion 30
compileSdkVersion 33
buildToolsVersion "30.0.3"

defaultConfig {
applicationId "otus.homework.coroutines"
minSdkVersion 23
targetSdkVersion 30
targetSdkVersion 33
versionCode 1
versionName "1.0"

Expand Down Expand Up @@ -42,4 +42,29 @@ dependencies {
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.squareup.picasso:picasso:2.71828'


// coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'

// lifecycle
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'

// appcompat
implementation 'androidx.appcompat:appcompat:1.3.1'

// mvi core
implementation 'com.github.badoo.mvicore:mvicore:1.4.0'
implementation 'com.github.badoo.mvicore:mvicore-android:1.4.0'
implementation 'com.github.badoo.mvicore:binder:1.4.0'
implementation 'com.github.badoo.mvicore:mvicore-diff:1.4.0'

// rxjava2
implementation "io.reactivex.rxjava2:rxjava:2.2.21"
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

// coroutines-rx2
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.7.2'
}
17 changes: 16 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,29 @@
package="otus.homework.coroutines">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".utils.CustomApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Coroutines">
<activity android:name=".MainActivity"
<activity
android:name=".presentation.mvi.CatsActivity"
android:exported="false" />
<activity
android:name=".presentation.mvp.CatsActivity"
android:exported="false" />
<activity
android:name=".presentation.mvvm.parent.CatsActivity"
android:exported="false" />
<activity
android:name=".presentation.mvvm.owners.CatsActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
35 changes: 0 additions & 35 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt

This file was deleted.

10 changes: 0 additions & 10 deletions app/src/main/java/otus/homework/coroutines/CatsService.kt

This file was deleted.

32 changes: 0 additions & 32 deletions app/src/main/java/otus/homework/coroutines/CatsView.kt

This file was deleted.

10 changes: 0 additions & 10 deletions app/src/main/java/otus/homework/coroutines/CrashMonitor.kt

This file was deleted.

16 changes: 0 additions & 16 deletions app/src/main/java/otus/homework/coroutines/DiContainer.kt

This file was deleted.

24 changes: 0 additions & 24 deletions app/src/main/java/otus/homework/coroutines/Fact.kt

This file was deleted.

54 changes: 39 additions & 15 deletions app/src/main/java/otus/homework/coroutines/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,30 +1,54 @@
package otus.homework.coroutines

import androidx.appcompat.app.AppCompatActivity
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity

/**
* Базовая `activity`
*/
class MainActivity : AppCompatActivity() {

lateinit var catsPresenter: CatsPresenter

private val diContainer = DiContainer()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView
setContentView(view)
setContentView(R.layout.activity_main)

catsPresenter = CatsPresenter(diContainer.service)
view.presenter = catsPresenter
catsPresenter.attachView(view)
catsPresenter.onInitComplete()
initView()
}

override fun onStop() {
if (isFinishing) {
catsPresenter.detachView()
private fun initView() {
findViewById<Button>(R.id.view_with_presenter_button).setOnClickListener {
startActivity(
Intent(
this, otus.homework.coroutines.presentation.mvp.CatsActivity::class.java
)
)
}

findViewById<Button>(R.id.view_with_view_model_button).setOnClickListener {
startActivity(
Intent(
this, otus.homework.coroutines.presentation.mvvm.parent.CatsActivity::class.java
)
)
}

findViewById<Button>(R.id.view_with_custom_owners_button).setOnClickListener {
startActivity(
Intent(
this, otus.homework.coroutines.presentation.mvvm.owners.CatsActivity::class.java
)
)
Copy link
Author

@SergeyKozhukhov SergeyKozhukhov Jul 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Activity ведет на другие три activity с тремя вариантами решения: с презентером, ViewModel и ViewModel с кастомными owаner-ами

}

findViewById<Button>(R.id.view_with_mvi_button).setOnClickListener {
startActivity(
Intent(
this, otus.homework.coroutines.presentation.mvi.CatsActivity::class.java
)
)
}
super.onStop()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package otus.homework.coroutines.data

import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import otus.homework.coroutines.data.converter.CatConverter
import otus.homework.coroutines.data.models.Fact
import otus.homework.coroutines.data.models.Image
import otus.homework.coroutines.domain.CatRepository
import otus.homework.coroutines.domain.models.Cat

/**
* Репозиторий информации о кошке [CatRepository]
*
* @param factService сервис получения случайного факта
* @param imageService сервис получения случайных изображений
* @param converter конвертер данных из [Fact] и списка [Image] в данные с информацией о кошке [Cat]
*/
class CatRepositoryImpl(
private val factService: FactService,
private val imageService: ImagesService,
private val converter: CatConverter,
) : CatRepository {

override suspend fun getCatInfo(): Cat = coroutineScope {
val factDeferred = async { factService.getCatFact() }
val imagesDeferred = async { imageService.getCatImages() }
converter.convert(factDeferred.await(), imagesDeferred.await())
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/otus/homework/coroutines/data/FactService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package otus.homework.coroutines.data

import otus.homework.coroutines.data.models.Fact
import retrofit2.http.GET

/**
* Сервис получения случайного факта
*/
interface FactService {

/** Получить случайных факт о кошке */
@GET("fact")
suspend fun getCatFact(): Fact
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Какие ошибки может выбрасывать Retrofit здесь?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зависит от статус кода который придет, ну точно может выбросить Таймаут, в целом все равно все статус коды которые не входят в 200-299 будут расценены колладаптером как исключение

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если нужно подробнее глянь реализацию колладаптеров

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Спасибо! Пока кажется, что с ретрофитом мы не можем четко прописать бросаемые исключения, так как они внутри него самого прописаны и их могут менять

}
14 changes: 14 additions & 0 deletions app/src/main/java/otus/homework/coroutines/data/ImagesService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package otus.homework.coroutines.data

import otus.homework.coroutines.data.models.Image
import retrofit2.http.GET

/**
* Сервис получения случайных изображений
*/
interface ImagesService {

/** Получить список случайных изображений */
@GET("search")
suspend fun getCatImages(): List<Image>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package otus.homework.coroutines.data.converter

import otus.homework.coroutines.data.models.Fact
import otus.homework.coroutines.data.models.Image
import otus.homework.coroutines.domain.models.Cat

/**
* Конвертер данных из [Fact] и списка [Image] в данные с информацией о кошке [Cat]
*/
class CatConverter {

/** Сконвертировать факт [Fact] и список изобрежений [Image] в информацию о кошке [Cat] */
fun convert(fact: Fact, images: List<Image>) =
Cat(fact.fact, images.firstOrNull()?.url)
}
Loading