Skip to content

Commit

Permalink
Feed取得処理にコルーチンを導入 (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
jageishi committed Apr 14, 2020
1 parent ee2267f commit 6912629
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 305 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ dependencies {
testImplementation 'junit:junit:4.12'
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
testImplementation 'org.assertj:assertj-core:3.14.0'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.5'

androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ageage.eggplant.common.repository

import io.reactivex.Single
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
Expand All @@ -18,18 +19,18 @@ private const val BASE_URL = "https://b.hatena.ne.jp"

class FeedRepository {

fun fetchRss(mode: Mode, category: Category): Single<List<Item>> {
suspend fun fetchRss(mode: Mode, category: Category): List<Item> {
val url = "${BASE_URL}${mode.url}${category.url}.rss"
.toHttpUrl()
.newBuilder().build()
return fetchRss(url)
}

fun search(
suspend fun search(
keyword: String,
searchFilterOption: SearchFilterOption,
page: Int
): Single<List<Item>> {
): List<Item> {
val url = when (searchFilterOption.target) {
SearchTarget.TEXT -> "${BASE_URL}/search${SearchTarget.TEXT.url}"
SearchTarget.TAG -> "${BASE_URL}/search${SearchTarget.TAG.url}"
Expand Down Expand Up @@ -67,8 +68,8 @@ class FeedRepository {
return fetchRss(httpUrl)
}

private fun fetchRss(url: HttpUrl): Single<List<Item>> {
return Single.create {
private suspend fun fetchRss(url: HttpUrl): List<Item> {
return withContext(Dispatchers.IO) {
val response = Client.getInstance().newCall(
Request.Builder()
.url(url)
Expand All @@ -77,9 +78,9 @@ class FeedRepository {
).execute()

if (response.isSuccessful) {
it.onSuccess(parse(response))
parse(response)
} else {
it.onError(IllegalStateException("Request failed with status code ${response.code}"))
throw IllegalStateException("Request failed with status code ${response.code}")
}
}
}
Expand Down
43 changes: 16 additions & 27 deletions app/src/main/java/org/ageage/eggplant/feed/FeedItemsViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@ package org.ageage.eggplant.feed
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.ageage.eggplant.common.api.response.Item
import org.ageage.eggplant.common.enums.Category
import org.ageage.eggplant.common.enums.Mode
import org.ageage.eggplant.common.repository.FeedRepository
import org.ageage.eggplant.common.schedulerprovider.BaseSchedulerProvider

class FeedItemsViewModel(
private val repository: FeedRepository,
private val schedulerProvider: BaseSchedulerProvider
private val repository: FeedRepository
) : ViewModel() {

private val disposable = CompositeDisposable()

private val _items = MutableLiveData<List<Item>>()
val items: LiveData<List<Item>>
get() = _items
Expand All @@ -28,30 +24,23 @@ class FeedItemsViewModel(

private var isAlreadyLoaded = false

override fun onCleared() {
super.onCleared()
disposable.clear()
}

fun loadRss(mode: Mode, category: Category, forceLoad: Boolean = false) {
if (isAlreadyLoaded && !forceLoad) {
return
}

_isLoading.postValue(true)
repository
.fetchRss(mode, category)
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe(
{ itemList ->
_items.postValue(itemList)
_isLoading.postValue(false)
isAlreadyLoaded = true
}, {
_isLoading.postValue(false)
}
)
.addTo(disposable)
viewModelScope.launch {
_isLoading.value = true
try {
val feedItems = repository.fetchRss(mode, category)
_items.value = feedItems
isAlreadyLoaded = true
} catch (e: Throwable) {
println(e.message)
} finally {
_isLoading.value = false

}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.ageage.eggplant.feed
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.ageage.eggplant.common.repository.FeedRepository
import org.ageage.eggplant.common.schedulerprovider.SchedulerProvider

class FeedItemsViewModelFactory : ViewModelProvider.Factory {

Expand All @@ -14,8 +13,7 @@ class FeedItemsViewModelFactory : ViewModelProvider.Factory {
}

return FeedItemsViewModel(
FeedRepository(),
SchedulerProvider()
FeedRepository()
) as T
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
package org.ageage.eggplant.search

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import androidx.lifecycle.*
import kotlinx.coroutines.launch
import org.ageage.eggplant.common.api.response.Item
import org.ageage.eggplant.common.enums.MinimumBookmarkCount
import org.ageage.eggplant.common.enums.SearchTarget
import org.ageage.eggplant.common.enums.SortType
import org.ageage.eggplant.common.model.SearchFilterOption
import org.ageage.eggplant.common.repository.FeedRepository
import org.ageage.eggplant.common.schedulerprovider.BaseSchedulerProvider

class SearchResultViewModel(
private val repository: FeedRepository,
private val schedulerProvider: BaseSchedulerProvider
private val repository: FeedRepository
) : ViewModel() {

private val disposable = CompositeDisposable()

private val _items = MutableLiveData<List<Item>>()
val items: LiveData<List<Item>>
get() = _items
Expand Down Expand Up @@ -54,68 +46,48 @@ class SearchResultViewModel(
true
)

override fun onCleared() {
super.onCleared()
disposable.clear()
}

fun search(forceLoad: Boolean = false) {
if (isAlreadyLoaded && !forceLoad) {
return
}

repository
.search(keyword, searchFilterOption, 1)
.doOnSubscribe {
_status.postValue(Status.Loading)
}
.doOnSuccess {
_status.postValue(Status.Success)
viewModelScope.launch {
_status.value = Status.Loading
try {
val feedItems = repository.search(keyword, searchFilterOption, 1)
_status.value = Status.Success
currentPageIndex = 1
_items.value = feedItems
isAlreadyLoaded = true
} catch (e: Throwable) {
_status.value = Status.Error(e)
}
.doOnError {
_status.postValue(Status.Error(it))
}
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe(
{ itemList ->
currentPageIndex = 1
_items.postValue(itemList)
isAlreadyLoaded = true
},
{
}
)
.addTo(disposable)
}
}

fun loadNextPage() {
repository
.search(keyword, searchFilterOption, currentPageIndex + 1)
.doOnSubscribe {
_status.postValue(Status.LoadingNextPage)
}
.doOnSuccess {
_status.postValue(Status.Success)
}
.doOnError {
_status.postValue(Status.ErrorLoadNextPage(it))
}
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe(
{ itemList ->
if (itemList.isEmpty()) {
_status.postValue(Status.ReachedLastPage)
} else {
currentPageIndex += 1
_items.postValue(items.value?.union(itemList)?.toList() ?: itemList)
}
},
{
viewModelScope.launch {
_status.value = Status.LoadingNextPage
try {
val feedItems = repository.search(
keyword,
searchFilterOption,
currentPageIndex + 1
)

_status.value = Status.Success

if (feedItems.isEmpty()) {
_status.value = Status.ReachedLastPage
} else {
currentPageIndex += 1
_items.value = items.value?.union(feedItems)?.toList() ?: feedItems
}
)
.addTo(disposable)

} catch (e: Throwable) {
_status.value = Status.ErrorLoadNextPage(e)
}
}
}

sealed class Status {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.ageage.eggplant.search
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.ageage.eggplant.common.repository.FeedRepository
import org.ageage.eggplant.common.schedulerprovider.SchedulerProvider

class SearchResultViewModelFactory : ViewModelProvider.Factory {

Expand All @@ -14,8 +13,7 @@ class SearchResultViewModelFactory : ViewModelProvider.Factory {
}

return SearchResultViewModel(
FeedRepository(),
SchedulerProvider()
FeedRepository()
) as T
}
}

0 comments on commit 6912629

Please sign in to comment.