Improve coroutines

This commit is contained in:
Ash 2022-03-27 10:30:25 +08:00
parent 435eb67c55
commit 77974c1d8b
12 changed files with 102 additions and 115 deletions

View File

@ -58,7 +58,7 @@ class App : Application(), Configuration.Provider {
override fun onCreate() {
super.onCreate()
applicationScope.launch {
applicationScope.launch(Dispatchers.IO) {
accountInit()
workerInit()
}

View File

@ -21,7 +21,7 @@ class Filter(
companion object {
val Starred = Filter(
index = 0,
important = 13,
important = 666,
icon = Icons.Rounded.StarOutline,
filledIcon = Icons.Rounded.Star,
)

View File

@ -10,7 +10,6 @@ import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import me.ash.reader.currentAccountId
import me.ash.reader.data.account.AccountDao
import me.ash.reader.data.article.Article
@ -104,24 +103,22 @@ abstract class AbstractRssRepository constructor(
}
}
suspend fun pullImportant(
fun pullImportant(
isStarred: Boolean = false,
isUnread: Boolean = false,
): Flow<List<ImportantCount>> {
return withContext(Dispatchers.IO) {
Log.i("RLog", "thread:pullImportant ${Thread.currentThread().name}")
val accountId = context.currentAccountId
Log.i(
"RLog",
"pullImportant: accountId: ${accountId}, isStarred: ${isStarred}, isUnread: ${isUnread}"
)
when {
return when {
isStarred -> articleDao
.queryImportantCountWhenIsStarred(accountId, isStarred)
isUnread -> articleDao
.queryImportantCountWhenIsUnread(accountId, isUnread)
else -> articleDao.queryImportantCountWhenIsAll(accountId)
}
}.flowOn(Dispatchers.IO)
}
@ -154,11 +151,9 @@ abstract class AbstractRssRepository constructor(
}
suspend fun deleteFeed(feed: Feed) {
withContext(Dispatchers.IO) {
articleDao.deleteByFeedId(context.currentAccountId, feed.id)
feedDao.delete(feed)
}
}
companion object {
val mutex = Mutex()

View File

@ -17,7 +17,9 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import me.ash.reader.*
import me.ash.reader.MainActivity
import me.ash.reader.R
import me.ash.reader.currentAccountId
import me.ash.reader.data.account.AccountDao
import me.ash.reader.data.article.Article
import me.ash.reader.data.article.ArticleDao
@ -89,25 +91,28 @@ class LocalRssRepository @Inject constructor(
withContext(Dispatchers.IO) {
val preTime = System.currentTimeMillis()
val accountId = context.currentAccountId
val feeds = async { feedDao.queryAll(accountId) }
val articles = feeds.await().also { feed ->
val articles = mutableListOf<Article>()
feedDao.queryAll(accountId).also { feed ->
updateSyncState {
it.copy(
feedCount = feed.size,
)
}
Log.i("RLog", "thread:sync ${Thread.currentThread().name}")
}.map { feed ->
async {
val articles = syncFeed(accountId, feed)
articles
syncFeed(accountId, feed)
}
}.awaitAll().forEach {
if (it.isNotify) {
notify(it.articles)
}
articles.addAll(it.articles)
}
articles.awaitAll().sumOf { it.size }.let { count ->
articleDao.insertList(articles)
Log.i(
"RlOG",
"[${count}] onCompletion: ${System.currentTimeMillis() - preTime}"
"onCompletion: ${System.currentTimeMillis() - preTime}"
)
accountDao.queryById(accountId)?.let { account ->
accountDao.update(
@ -126,12 +131,16 @@ class LocalRssRepository @Inject constructor(
}
}
}
}
data class ArticleNotify(
val articles: List<Article>,
val isNotify: Boolean,
)
private suspend fun syncFeed(
accountId: Int,
feed: Feed
): List<Article> {
): ArticleNotify {
val latest = articleDao.queryLatestByFeedId(accountId, feed.id)
val articles = rssHelper.queryRssXml(
rssNetworkDataSource,
@ -143,21 +152,16 @@ class LocalRssRepository @Inject constructor(
rssHelper.queryRssIcon(feedDao, feed, it.first().link)
}
}
Log.i("RLog", "thread:syncFeed ${Thread.currentThread().name}")
updateSyncState {
it.copy(
syncedCount = it.syncedCount + 1,
currentFeedName = feed.name
)
}
if (articles.isNotEmpty()) {
articleDao.insertList(articles)
if (feed.isNotification) {
notify(articles)
}
}
return articles
return ArticleNotify(
articles = articles,
isNotify = articles.isNotEmpty() && feed.isNotification
)
}
private fun notify(

View File

@ -1,17 +1,14 @@
package me.ash.reader.ui.page.home
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.PagerState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import me.ash.reader.data.constant.Filter
import me.ash.reader.data.feed.Feed
import me.ash.reader.data.group.Group
@ -36,7 +33,7 @@ class HomeViewModel @Inject constructor(
fun dispatch(action: HomeViewAction) {
when (action) {
is HomeViewAction.Sync -> sync(action.callback)
is HomeViewAction.Sync -> sync()
is HomeViewAction.ChangeFilter -> changeFilter(action.filterState)
is HomeViewAction.ScrollToPage -> scrollToPage(
action.scope,
@ -46,11 +43,8 @@ class HomeViewModel @Inject constructor(
}
}
private fun sync(callback: () -> Unit = {}) {
viewModelScope.launch {
private fun sync() {
rssRepository.get().doSync()
callback()
}
}
private fun changeFilter(filterState: FilterState) {
@ -80,9 +74,7 @@ data class HomeViewState(
)
sealed class HomeViewAction {
data class Sync(
val callback: () -> Unit = {},
) : HomeViewAction()
object Sync : HomeViewAction()
data class ChangeFilter(
val filterState: FilterState

View File

@ -49,7 +49,7 @@ fun DeleteFeedDialog(
confirmButton = {
TextButton(
onClick = {
viewModel.dispatch(FeedOptionViewAction.Delete(){
viewModel.dispatch(FeedOptionViewAction.Delete {
viewModel.dispatch(FeedOptionViewAction.HideDeleteDialog)
viewModel.dispatch(FeedOptionViewAction.Hide(scope))
Toast.makeText(context, deletedTip, Toast.LENGTH_SHORT).show()

View File

@ -8,11 +8,13 @@ import androidx.lifecycle.viewModelScope
import com.google.accompanist.pager.ExperimentalPagerApi
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.ash.reader.data.feed.Feed
import me.ash.reader.data.group.Group
import me.ash.reader.data.repository.RssRepository
@ -30,7 +32,7 @@ class FeedOptionViewModel @Inject constructor(
val viewState: StateFlow<FeedOptionViewState> = _viewState.asStateFlow()
init {
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
rssRepository.get().pullGroups().collect { groups ->
_viewState.update {
it.copy(
@ -88,7 +90,7 @@ class FeedOptionViewModel @Inject constructor(
}
private fun selectedGroup(groupId: String) {
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
_viewState.value.feed?.let {
rssRepository.get().updateFeed(
it.copy(
@ -109,7 +111,7 @@ class FeedOptionViewModel @Inject constructor(
}
private fun changeParseFullContentPreset() {
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
_viewState.value.feed?.let {
rssRepository.get().updateFeed(
it.copy(
@ -122,7 +124,7 @@ class FeedOptionViewModel @Inject constructor(
}
private fun changeAllowNotificationPreset() {
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
_viewState.value.feed?.let {
rssRepository.get().updateFeed(
it.copy(
@ -136,13 +138,14 @@ class FeedOptionViewModel @Inject constructor(
private fun delete(callback: () -> Unit = {}) {
_viewState.value.feed?.let {
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
rssRepository.get().deleteFeed(it)
}.invokeOnCompletion {
withContext(Dispatchers.Main) {
callback()
}
}
}
}
private fun hideDeleteDialog() {
_viewState.update {

View File

@ -61,7 +61,7 @@ fun FeedsPage(
)
LaunchedEffect(Unit) {
viewModel.dispatch(FeedsViewAction.FetchAccount())
viewModel.dispatch(FeedsViewAction.FetchAccount)
}
LaunchedEffect(homeViewModel.filterState) {
@ -89,7 +89,7 @@ fun FeedsPage(
actions = {
IconButton(onClick = {
if (syncState.isSyncing) return@IconButton
homeViewModel.dispatch(HomeViewAction.Sync())
homeViewModel.dispatch(HomeViewAction.Sync)
}) {
Icon(
modifier = Modifier.rotate(if (syncState.isSyncing) angle else 0f),
@ -135,7 +135,7 @@ fun FeedsPage(
Banner(
title = filterState.filter.getName(),
desc = filterState.filter.getDesc(),
icon = viewState.filter.icon,
icon = filterState.filter.icon,
action = {
Icon(
imageVector = Icons.Outlined.KeyboardArrowRight,

View File

@ -29,33 +29,32 @@ class FeedsViewModel @Inject constructor(
fun dispatch(action: FeedsViewAction) {
when (action) {
is FeedsViewAction.FetchAccount -> fetchAccount(action.callback)
is FeedsViewAction.FetchAccount -> fetchAccount()
is FeedsViewAction.FetchData -> fetchData(action.filterState)
is FeedsViewAction.AddFromFile -> addFromFile(action.inputStream)
is FeedsViewAction.ScrollToItem -> scrollToItem(action.index)
}
}
private fun fetchAccount(callback: () -> Unit = {}) {
viewModelScope.launch {
private fun fetchAccount() {
viewModelScope.launch(Dispatchers.IO) {
_viewState.update {
it.copy(
account = accountRepository.getCurrentAccount()
)
}
callback()
}
}
private fun addFromFile(inputStream: InputStream) {
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
opmlRepository.saveToDatabase(inputStream)
rssRepository.get().doSync()
}
}
private fun fetchData(filterState: FilterState) {
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
pullFeeds(
isStarred = filterState.filter.isStarred(),
isUnread = filterState.filter.isUnread(),
@ -144,9 +143,7 @@ sealed class FeedsViewAction {
val filterState: FilterState,
) : FeedsViewAction()
data class FetchAccount(
val callback: () -> Unit = {},
) : FeedsViewAction()
object FetchAccount: FeedsViewAction()
data class AddFromFile(
val inputStream: InputStream

View File

@ -5,11 +5,8 @@ import androidx.lifecycle.viewModelScope
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.PagerState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import me.ash.reader.R
import me.ash.reader.data.article.Article
import me.ash.reader.data.feed.Feed
@ -73,7 +70,7 @@ class SubscribeViewModel @Inject constructor(
private fun subscribe() {
val feed = _viewState.value.feed ?: return
val articles = _viewState.value.articles
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
val groupId = async {
if (
_viewState.value.newGroupSelected &&
@ -129,7 +126,7 @@ class SubscribeViewModel @Inject constructor(
private fun search(scope: CoroutineScope) {
searchJob?.cancel()
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
try {
_viewState.update {
it.copy(

View File

@ -42,7 +42,7 @@ class FlowViewModel @Inject constructor(
}
private fun fetchData(filterState: FilterState) {
viewModelScope.launch {
viewModelScope.launch(Dispatchers.Default) {
rssRepository.get().pullImportant(filterState.filter.isStarred(), true)
.collect { importantList ->
_viewState.update {

View File

@ -4,6 +4,7 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@ -65,46 +66,44 @@ class ReadViewModel @Inject constructor(
}
private fun markUnread(isUnread: Boolean) {
_viewState.value.articleWithFeed?.let {
val articleWithFeed = _viewState.value.articleWithFeed ?: return
viewModelScope.launch(Dispatchers.IO) {
_viewState.update {
it.copy(
articleWithFeed = it.articleWithFeed?.copy(
article = it.articleWithFeed.article.copy(
articleWithFeed = articleWithFeed.copy(
article = articleWithFeed.article.copy(
isUnread = isUnread
)
)
)
}
viewModelScope.launch {
rssRepository.get().updateArticleInfo(
it.article.copy(
articleWithFeed.article.copy(
isUnread = isUnread
)
)
}
}
}
private fun markStarred(isStarred: Boolean) {
_viewState.value.articleWithFeed?.let {
val articleWithFeed = _viewState.value.articleWithFeed ?: return
viewModelScope.launch(Dispatchers.IO) {
_viewState.update {
it.copy(
articleWithFeed = it.articleWithFeed?.copy(
article = it.articleWithFeed.article.copy(
articleWithFeed = articleWithFeed.copy(
article = articleWithFeed.article.copy(
isStarred = isStarred
)
)
)
}
viewModelScope.launch {
rssRepository.get().updateArticleInfo(
it.article.copy(
articleWithFeed.article.copy(
isStarred = isStarred
)
)
}
}
}
private fun scrollToItem(index: Int) {
viewModelScope.launch {