Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit df7b033

Browse files
Add RXJava3 and Flow implementation with each nav graph
1 parent ae41d05 commit df7b033

File tree

13 files changed

+289
-55
lines changed

13 files changed

+289
-55
lines changed

‎Tutorial2-1FlowBasics/src/main/AndroidManifest.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<activity android:name=".chapter2_network.Activity2Network" />
2222
<activity android:name=".chapter3_database.Activity3Database" />
2323
<activity android:name=".chapter4_single_source_of_truth.Activity4SingleSourceOfTruth" />
24+
<activity android:name=".chapter4_single_source_of_truth.Activity4SingleSourceOfTruthRxJava3" />
2425
</application>
2526

2627
</manifest>

‎Tutorial2-1FlowBasics/src/main/java/com/smarttoolfactory/tutorial2_1flowbasics/MainActivity.kt‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.smarttoolfactory.tutorial2_1flowbasics.adapter.ChapterSelectionAdapte
1212
import com.smarttoolfactory.tutorial2_1flowbasics.chapter2_network.Activity2Network
1313
import com.smarttoolfactory.tutorial2_1flowbasics.chapter3_database.Activity3Database
1414
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth.Activity4SingleSourceOfTruth
15+
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth.Activity4SingleSourceOfTruthRxJava3
1516
import com.smarttoolfactory.tutorial2_1flowbasics.databinding.ActivityMainBinding
1617
import com.smarttoolfactory.tutorial2_1flowbasics.model.ActivityClassModel
1718
import java.util.*
@@ -32,7 +33,18 @@ class MainActivity : AppCompatActivity(), BaseAdapter.OnRecyclerViewItemClickLis
3233
// Add Activities to list to be displayed on RecyclerView
3334
activityClassModels.add(ActivityClassModel(Activity2Network::class.java))
3435
activityClassModels.add(ActivityClassModel(Activity3Database::class.java))
35-
activityClassModels.add(ActivityClassModel(Activity4SingleSourceOfTruth::class.java))
36+
activityClassModels.add(
37+
ActivityClassModel(
38+
Activity4SingleSourceOfTruth::class.java,
39+
"Single Source of Truth with Flow"
40+
)
41+
)
42+
activityClassModels.add(
43+
ActivityClassModel(
44+
Activity4SingleSourceOfTruthRxJava3::class.java,
45+
"Single Source of Truth with RxJava3"
46+
)
47+
)
3648

3749
val recyclerView = activityMainBinding.recyclerView
3850

‎Tutorial2-1FlowBasics/src/main/java/com/smarttoolfactory/tutorial2_1flowbasics/chapter4_single_source_of_truth/Activity4SingleSourceOfTruth.kt‎

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@ package com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_tru
22

33
import android.os.Bundle
44
import androidx.appcompat.app.AppCompatActivity
5+
import androidx.fragment.app.FragmentContainerView
6+
import androidx.navigation.Navigation
7+
import androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment
8+
import androidx.navigation.findNavController
9+
import androidx.navigation.fragment.NavHostFragment
10+
import androidx.navigation.fragment.findNavController
511
import com.smarttoolfactory.tutorial2_1flowbasics.R
6-
import kotlinx.coroutines.*
7-
import kotlin.coroutines.CoroutineContext
812

9-
class Activity4SingleSourceOfTruth : AppCompatActivity(), CoroutineScope {
10-
11-
private lateinit var job: Job
12-
13-
override val coroutineContext: CoroutineContext
14-
get() = job + Dispatchers.Main + CoroutineName("🙄 Activity Scope") + CoroutineExceptionHandler { coroutineContext, throwable ->
15-
println("🤬 Exception $throwable in context:$coroutineContext")
16-
}
13+
class Activity4SingleSourceOfTruth : AppCompatActivity() {
1714

1815
override fun onCreate(savedInstanceState: Bundle?) {
1916
super.onCreate(savedInstanceState)
2017
setContentView(R.layout.activity4_single_source_of_truth)
21-
job = Job()
22-
}
2318

19+
title = "Single Source of Truth Coroutines Flow"
20+
21+
val navHostFragment: NavHostFragment = supportFragmentManager
22+
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
23+
24+
navHostFragment.findNavController().setGraph(R.navigation.nav_graph_post_list)
25+
}
2426
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth
2+
3+
import android.os.Bundle
4+
import androidx.appcompat.app.AppCompatActivity
5+
import androidx.navigation.findNavController
6+
import androidx.navigation.fragment.NavHostFragment
7+
import androidx.navigation.fragment.findNavController
8+
import com.smarttoolfactory.tutorial2_1flowbasics.R
9+
import com.smarttoolfactory.tutorial2_1flowbasics.data.model.PostEntity
10+
import com.smarttoolfactory.tutorial2_1flowbasics.di.ServiceLocator
11+
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
12+
import io.reactivex.rxjava3.core.Single
13+
import io.reactivex.rxjava3.schedulers.Schedulers
14+
15+
class Activity4SingleSourceOfTruthRxJava3 : AppCompatActivity() {
16+
17+
override fun onCreate(savedInstanceState: Bundle?) {
18+
super.onCreate(savedInstanceState)
19+
setContentView(R.layout.activity4_single_source_of_truth)
20+
21+
title = "Single Source of Truth RxJava3"
22+
23+
val navHostFragment: NavHostFragment = supportFragmentManager
24+
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
25+
26+
navHostFragment.findNavController().setGraph(R.navigation.nav_graph_post_list_rxjava3)
27+
28+
// examineDaoWithRxJava()
29+
30+
}
31+
32+
/**
33+
* Function to test what happens when there is no post in DB with Single, Maybe and Observable
34+
* classes of RxJava2
35+
*/
36+
private fun examineDaoWithRxJava() {
37+
38+
val serviceLocator = ServiceLocator(application)
39+
40+
val postDaoRxJava = serviceLocator.providePostDaoRxJava()
41+
42+
// SINGLE
43+
val disposableSingle = postDaoRxJava.getPostByIdSingle(1)
44+
.subscribeOn(Schedulers.io())
45+
.observeOn(AndroidSchedulers.mainThread())
46+
// .onErrorResumeNext {
47+
// _ -> Single.error(AssertionError("ERROR"))
48+
// }
49+
.onErrorResumeNext {
50+
Single.just(PostEntity(1, 1, "Title", "Body"))
51+
}
52+
.doOnError { throwable ->
53+
println("🔥 MainActivity onCreate() doOnError() throwable: $throwable")
54+
}
55+
.subscribe(
56+
{ postEntity ->
57+
println("🍎 MainActivity onCreate() getPostByIdSingle() onNext(): $postEntity")
58+
},
59+
{
60+
println("⏰ MainActivity onCreate() getPostByIdSingle() onError: $it")
61+
}
62+
)
63+
64+
// MAYBE
65+
// val disposableMaybe = postDaoRxJava.getPostListByIdMaybe(1)
66+
// .subscribeOn(Schedulers.io())
67+
// .observeOn(AndroidSchedulers.mainThread())
68+
// .doOnError { throwable ->
69+
// println("🔥 MainActivity onCreate() doOnError() throwable: $throwable")
70+
// }
71+
// .doOnComplete {
72+
// println("🔥 MainActivity onCreate() doOnComplete()")
73+
//
74+
// }
75+
// .subscribe(
76+
// { postEntity ->
77+
// println("🍎 MainActivity onCreate() getPostListByIdMaybe() onNext(): $postEntity")
78+
// },
79+
// {
80+
// println("🍏 MainActivity onCreate() getPostListByIdMaybe() onError: $it")
81+
// },
82+
// {
83+
// println("⏰ MainActivity onCreate() getPostListByIdMaybe() onComplete")
84+
// }
85+
// )
86+
87+
// OBSERVABLE
88+
// val disposableObservable = postDaoRxJava.getPostListById(1)
89+
// .subscribeOn(Schedulers.io())
90+
// .observeOn(AndroidSchedulers.mainThread())
91+
// .doOnError { throwable ->
92+
// println("🔥 MainActivity onCreate() doOnError() throwable: $throwable")
93+
// }
94+
// .doOnComplete {
95+
// println("🔥 MainActivity onCreate() doOnComplete()")
96+
//
97+
// }
98+
// .subscribe(
99+
// { postEntity ->
100+
// println("🍎 MainActivity onCreate() getPostListById() onNext(): $postEntity")
101+
// },
102+
// {
103+
// println("🍏 MainActivity onCreate() getPostListById() onError: $it")
104+
// },
105+
// {
106+
// println("⏰ MainActivity onCreate() getPostListById() onComplete")
107+
// }
108+
// )
109+
}
110+
}

‎Tutorial2-1FlowBasics/src/main/java/com/smarttoolfactory/tutorial2_1flowbasics/chapter4_single_source_of_truth/data/repository/PostRepositoryImpl.kt‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth.data.repository
22

33
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth.data.source.*
4-
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth_rxjava3.data.source.Cache
5-
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth_rxjava3.data.source.LocalPostDataSourceRxJava3
6-
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth_rxjava3.data.source.RemotePostDataSourceRxJava3
74
import com.smarttoolfactory.tutorial2_1flowbasics.data.mapper.DTOtoEntityMapper
85
import com.smarttoolfactory.tutorial2_1flowbasics.data.model.PostEntity
96
import io.reactivex.rxjava3.core.Completable

‎Tutorial2-1FlowBasics/src/main/java/com/smarttoolfactory/tutorial2_1flowbasics/chapter4_single_source_of_truth/data/source/DataSources.kt‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth.data.source
22

3-
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth_rxjava3.data.source.LocalPostDataSourceRxJava3
4-
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth_rxjava3.data.source.PostDataSource
5-
import com.smarttoolfactory.tutorial2_1flowbasics.chapter4_single_source_of_truth_rxjava3.data.source.RemotePostDataSourceRxJava3
63
import com.smarttoolfactory.tutorial2_1flowbasics.data.api.PostApi
74
import com.smarttoolfactory.tutorial2_1flowbasics.data.api.PostApiRxJava
85
import com.smarttoolfactory.tutorial2_1flowbasics.data.db.PostDao

‎Tutorial2-1FlowBasics/src/main/java/com/smarttoolfactory/tutorial2_1flowbasics/chapter4_single_source_of_truth/domain/GetPostsUseCaseFlow.kt‎

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,32 @@ class GetPostsUseCaseFlow(
3737
* * if both network and db didn't have any data throws empty set exception error
3838
*/
3939
fun getPostFlowOfflineLast(): Flow<ViewState<List<Post>>> {
40+
41+
// *** START This section was in repo before moving to here ***
4042
return flow { emit(repository.fetchEntitiesFromRemote()) }
4143
.map {
4244
println("🍏 getPostFlowOfflineLast() First map in thread: ${Thread.currentThread().name}")
4345

4446
if (it.isNullOrEmpty()) {
45-
throw EmptyDataException("No data is available!")
47+
throw EmptyDataException("Data is available in neither in remote nor local source!")
4648
} else {
4749
repository.deletePostEntities()
4850
repository.savePostEntity(it)
4951
repository.getPostEntitiesFromLocal()
5052
}
5153

5254
}
55+
// *** END This section was in repo before moving to here ***
5356
.flowOn(dispatcherProvider.ioDispatcher)
5457
.catch { cause ->
5558
println("❌ getPostFlowOfflineLast() FIRST catch with error: $cause, in thread: ${Thread.currentThread().name}")
56-
emitAll(flow { emit(repository.getPostEntitiesFromLocal()) })
59+
emitAll(flowOf(repository.getPostEntitiesFromLocal()))
5760
}
5861
.map {
5962
if (!it.isNullOrEmpty()) {
6063
entityToPostMapper.map(it)
6164
} else {
62-
throw EmptyDataException("No data is available!")
65+
throw EmptyDataException("No data is available in both remote and local source!")
6366
}
6467
}
6568
.map { postList ->
@@ -68,13 +71,11 @@ class GetPostsUseCaseFlow(
6871
}
6972
.catch { cause: Throwable ->
7073
println("❌ getPostFlowOfflineLast() SECOND catch with error: $cause, in thread: ${Thread.currentThread().name}")
71-
emitAll(flow<ViewState<List<Post>>> { emit(ViewState(Status.ERROR, error = cause)) })
74+
emitAll(flowOf(ViewState(Status.ERROR, error = cause)))
7275
}
7376
.flowOn(dispatcherProvider.defaultDispatcher)
74-
7577
}
7678

77-
7879
/**
7980
* Flow to get data from cache if it's not expired, if it's expired check remote data source.
8081
*
@@ -92,14 +93,12 @@ class GetPostsUseCaseFlow(
9293
}
9394
}
9495

95-
9696
fun getPostFlowOfflineFirst(): Flow<ViewState<List<Post>>> {
9797

9898
return flow { emit(repository.getPostEntitiesFromLocal()) }
9999
.map {
100100

101101
println("🍏 getPostFlowOfflineFirst() First map in thread: ${Thread.currentThread().name}")
102-
103102
if (it.isEmpty()) {
104103
repository.run {
105104
repository.deletePostEntities()
@@ -109,7 +108,6 @@ class GetPostsUseCaseFlow(
109108
} else {
110109
it
111110
}
112-
113111
}
114112
.flowOn(dispatcherProvider.ioDispatcher)
115113
.map {
@@ -119,21 +117,18 @@ class GetPostsUseCaseFlow(
119117
if (!it.isNullOrEmpty()) {
120118
entityToPostMapper.map(it)
121119
} else {
122-
throw EmptyDataException("No data is available!")
120+
throw EmptyDataException("Data is available in neither in remote nor local source!")
123121
}
124122
}
125123
.map { postList ->
126124

127125
println("🍎 getPostFlowOfflineFirst() Third map in thread: ${Thread.currentThread().name}")
128126

129-
ViewState<List<Post>>(
130-
status = Status.SUCCESS,
131-
data = postList
132-
)
127+
ViewState(status = Status.SUCCESS, data = postList)
133128
}
134129
.catch { cause: Throwable ->
135130
println("❌ getPostFlowOfflineFirst() SECOND catch with error: $cause, in thread: ${Thread.currentThread().name}")
136-
emitAll(flow<ViewState<List<Post>>> { emit(ViewState(Status.ERROR, error = cause)) })
131+
emitAll(flowOf(ViewState(Status.ERROR, error = cause)))
137132
}
138133
.flowOn(dispatcherProvider.defaultDispatcher)
139134
}

‎Tutorial2-1FlowBasics/src/main/java/com/smarttoolfactory/tutorial2_1flowbasics/chapter4_single_source_of_truth/domain/GetPostsUseCaseRxJava3.kt‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import com.smarttoolfactory.tutorial2_1flowbasics.data.model.Status
88
import com.smarttoolfactory.tutorial2_1flowbasics.data.model.ViewState
99
import com.smarttoolfactory.tutorial2_1flowbasics.util.EmptyDataException
1010
import io.reactivex.rxjava3.core.Single
11-
import io.reactivex.rxjava3.core.SingleSource
1211
import io.reactivex.rxjava3.schedulers.Schedulers
1312

1413
class GetPostsUseCaseRxJava3(
@@ -33,7 +32,7 @@ class GetPostsUseCaseRxJava3(
3332

3433

3534
/**
36-
* This function always looks for new data from remote source first
35+
* This function always looks for new data from REMOTE source first
3736
*
3837
* * if data is fetched from remote source: deletes old data, saves new data and returns new data
3938
* * if error occurred while fetching data from remote: it tries to fetch data from database
@@ -57,7 +56,6 @@ class GetPostsUseCaseRxJava3(
5756
}
5857
.onErrorResumeNext { cause ->
5958
println("❌ getPostFlowOfflineLast() FIRST onErrorResumeNext() with error: $cause, in thread: ${Thread.currentThread().name}")
60-
6159
repository.getPostEntitiesFromLocal()
6260
}
6361
// Alternative 1
@@ -68,26 +66,28 @@ class GetPostsUseCaseRxJava3(
6866
// } else {
6967
// entityToPostMapper.map(it)
7068
// }
71-
// Alternative 2
69+
// Alternative 2 START
70+
// This filter does not emit anything if list is empty. Empty list is still an emission
7271
.filter {
7372
println("🤓 getPostFlowOfflineLast() filter: ${it.isNotEmpty()}")
7473
it.isNotEmpty()
7574
}
76-
.switchIfEmpty(
77-
Single.error(EmptyDataException("Empty set"))
78-
)
7975
.map {
8076
entityToPostMapper.map(it)
8177
}
78+
.switchIfEmpty(
79+
Single.error(EmptyDataException("Data is available in neither in remote nor local source!"))
80+
)
81+
// Alternative 2 END
82+
8283
.map { postList ->
8384
println("🎃 getPostFlowOfflineLast() Third map in thread: ${Thread.currentThread().name}")
8485
ViewState(status = Status.SUCCESS, data = postList)
8586
}
8687
.onErrorResumeNext { cause ->
8788
println("❌ getPostFlowOfflineLast() SECOND catch with error: $cause, in thread: ${Thread.currentThread().name}")
88-
SingleSource {
89-
ViewState<List<PostEntity>>(status = Status.ERROR, error = cause)
90-
}
89+
// Single.error(cause)
90+
Single.just(ViewState(status = Status.ERROR, error = cause))
9191
}
9292
}
9393

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /