Improve coroutines
This commit is contained in:
parent
435eb67c55
commit
77974c1d8b
|
@ -58,7 +58,7 @@ class App : Application(), Configuration.Provider {
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
applicationScope.launch {
|
||||
applicationScope.launch(Dispatchers.IO) {
|
||||
accountInit()
|
||||
workerInit()
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user