Skip to content

Commit a2f5207

Browse files
author
morz
committed
refactor: restructure network module using DI and separate interfaces per entity API
This refactor is based on using the NIA project as a base project. Improves code structure without changing functionality, and makes it easier to extend when working with multiple API methods.
1 parent 8092c60 commit a2f5207

File tree

5 files changed

+142
-53
lines changed

5 files changed

+142
-53
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.samples.apps.nowinandroid.core.network.api
18+
19+
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
20+
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
21+
import com.google.samples.apps.nowinandroid.core.network.retrofit.NetworkResponse
22+
import retrofit2.http.GET
23+
import retrofit2.http.Query
24+
25+
/**
26+
* Retrofit API declaration for News Resource API
27+
*/
28+
internal interface NewsResourceApi {
29+
@GET(value = "newsresources")
30+
suspend fun getNewsResources(
31+
@Query("id") ids: List<String>?,
32+
): NetworkResponse<List<NetworkNewsResource>>
33+
34+
@GET(value = "changelists/newsresources")
35+
suspend fun getNewsResourcesChangeList(
36+
@Query("after") after: Int?,
37+
): List<NetworkChangeList>
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.samples.apps.nowinandroid.core.network.api
18+
19+
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
20+
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
21+
import com.google.samples.apps.nowinandroid.core.network.retrofit.NetworkResponse
22+
import retrofit2.http.GET
23+
import retrofit2.http.Query
24+
25+
/**
26+
* Retrofit API declaration for Topic API
27+
*/
28+
internal interface TopicApi {
29+
@GET(value = "topics")
30+
suspend fun getTopics(
31+
@Query("id") ids: List<String>?,
32+
): NetworkResponse<List<NetworkTopic>>
33+
34+
@GET(value = "changelists/topics")
35+
suspend fun getTopicChangeList(
36+
@Query("after") after: Int?,
37+
): List<NetworkChangeList>
38+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.google.samples.apps.nowinandroid.core.network.di
2+
3+
import androidx.tracing.trace
4+
import com.google.samples.apps.nowinandroid.core.network.api.NewsResourceApi
5+
import com.google.samples.apps.nowinandroid.core.network.api.TopicApi
6+
import dagger.Module
7+
import dagger.Provides
8+
import dagger.hilt.InstallIn
9+
import dagger.hilt.components.SingletonComponent
10+
import retrofit2.Retrofit
11+
import javax.inject.Singleton
12+
import kotlin.jvm.java
13+
14+
@Module
15+
@InstallIn(SingletonComponent::class)
16+
internal object ApiModule {
17+
18+
@Provides
19+
@Singleton
20+
internal fun providesTopicApi(retrofit: Retrofit): TopicApi {
21+
trace("RetrofitNiaNetwork") {
22+
return retrofit.create(TopicApi::class.java)
23+
}
24+
}
25+
26+
@Provides
27+
@Singleton
28+
internal fun providesNewsResourceApi(retrofit: Retrofit): NewsResourceApi {
29+
trace("RetrofitNiaNetwork") {
30+
return retrofit.create(NewsResourceApi::class.java)
31+
}
32+
}
33+
}

core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,19 @@ import coil.decode.SvgDecoder
2323
import coil.util.DebugLogger
2424
import com.google.samples.apps.nowinandroid.core.network.BuildConfig
2525
import com.google.samples.apps.nowinandroid.core.network.demo.DemoAssetManager
26+
import com.google.samples.apps.nowinandroid.core.network.retrofit.NIA_BASE_URL
2627
import dagger.Module
2728
import dagger.Provides
2829
import dagger.hilt.InstallIn
2930
import dagger.hilt.android.qualifiers.ApplicationContext
3031
import dagger.hilt.components.SingletonComponent
3132
import kotlinx.serialization.json.Json
3233
import okhttp3.Call
34+
import okhttp3.MediaType.Companion.toMediaType
3335
import okhttp3.OkHttpClient
3436
import okhttp3.logging.HttpLoggingInterceptor
37+
import retrofit2.Retrofit
38+
import retrofit2.converter.kotlinx.serialization.asConverterFactory
3539
import javax.inject.Singleton
3640

3741
@Module
@@ -92,4 +96,23 @@ internal object NetworkModule {
9296
}
9397
.build()
9498
}
99+
100+
@Provides
101+
@Singleton
102+
fun provideRetrofit(
103+
networkJson: Json,
104+
okhttpCallFactory: dagger.Lazy<Call.Factory>,
105+
): Retrofit {
106+
trace("RetrofitNiaNetwork") {
107+
return Retrofit.Builder()
108+
.baseUrl(NIA_BASE_URL)
109+
// We use callFactory lambda here with dagger.Lazy<Call.Factory>
110+
// to prevent initializing OkHttp on the main thread.
111+
.callFactory { okhttpCallFactory.get().newCall(it) }
112+
.addConverterFactory(
113+
networkJson.asConverterFactory("application/json".toMediaType()),
114+
)
115+
.build()
116+
}
117+
}
95118
}

core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt

Lines changed: 10 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,55 +16,25 @@
1616

1717
package com.google.samples.apps.nowinandroid.core.network.retrofit
1818

19-
import androidx.tracing.trace
2019
import com.google.samples.apps.nowinandroid.core.network.BuildConfig
2120
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
21+
import com.google.samples.apps.nowinandroid.core.network.api.NewsResourceApi
22+
import com.google.samples.apps.nowinandroid.core.network.api.TopicApi
2223
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
2324
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
2425
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
2526
import kotlinx.serialization.Serializable
26-
import kotlinx.serialization.json.Json
27-
import okhttp3.Call
28-
import okhttp3.MediaType.Companion.toMediaType
2927
import retrofit2.Retrofit
30-
import retrofit2.converter.kotlinx.serialization.asConverterFactory
31-
import retrofit2.http.GET
32-
import retrofit2.http.Query
3328
import javax.inject.Inject
3429
import javax.inject.Singleton
3530

36-
/**
37-
* Retrofit API declaration for NIA Network API
38-
*/
39-
private interface RetrofitNiaNetworkApi {
40-
@GET(value = "topics")
41-
suspend fun getTopics(
42-
@Query("id") ids: List<String>?,
43-
): NetworkResponse<List<NetworkTopic>>
44-
45-
@GET(value = "newsresources")
46-
suspend fun getNewsResources(
47-
@Query("id") ids: List<String>?,
48-
): NetworkResponse<List<NetworkNewsResource>>
49-
50-
@GET(value = "changelists/topics")
51-
suspend fun getTopicChangeList(
52-
@Query("after") after: Int?,
53-
): List<NetworkChangeList>
54-
55-
@GET(value = "changelists/newsresources")
56-
suspend fun getNewsResourcesChangeList(
57-
@Query("after") after: Int?,
58-
): List<NetworkChangeList>
59-
}
60-
61-
private const val NIA_BASE_URL = BuildConfig.BACKEND_URL
31+
internal const val NIA_BASE_URL = BuildConfig.BACKEND_URL
6232

6333
/**
6434
* Wrapper for data provided from the [NIA_BASE_URL]
6535
*/
6636
@Serializable
67-
private data class NetworkResponse<T>(
37+
internal data class NetworkResponse<T>(
6838
val data: T,
6939
)
7040

@@ -73,32 +43,19 @@ private data class NetworkResponse<T>(
7343
*/
7444
@Singleton
7545
internal class RetrofitNiaNetwork @Inject constructor(
76-
networkJson: Json,
77-
okhttpCallFactory: dagger.Lazy<Call.Factory>,
46+
val newsResourceApi: NewsResourceApi,
47+
val topicApi: TopicApi
7848
) : NiaNetworkDataSource {
7949

80-
private val networkApi = trace("RetrofitNiaNetwork") {
81-
Retrofit.Builder()
82-
.baseUrl(NIA_BASE_URL)
83-
// We use callFactory lambda here with dagger.Lazy<Call.Factory>
84-
// to prevent initializing OkHttp on the main thread.
85-
.callFactory { okhttpCallFactory.get().newCall(it) }
86-
.addConverterFactory(
87-
networkJson.asConverterFactory("application/json".toMediaType()),
88-
)
89-
.build()
90-
.create(RetrofitNiaNetworkApi::class.java)
91-
}
92-
9350
override suspend fun getTopics(ids: List<String>?): List<NetworkTopic> =
94-
networkApi.getTopics(ids = ids).data
51+
topicApi.getTopics(ids = ids).data
9552

9653
override suspend fun getNewsResources(ids: List<String>?): List<NetworkNewsResource> =
97-
networkApi.getNewsResources(ids = ids).data
54+
newsResourceApi.getNewsResources(ids = ids).data
9855

9956
override suspend fun getTopicChangeList(after: Int?): List<NetworkChangeList> =
100-
networkApi.getTopicChangeList(after = after)
57+
topicApi.getTopicChangeList(after = after)
10158

10259
override suspend fun getNewsResourceChangeList(after: Int?): List<NetworkChangeList> =
103-
networkApi.getNewsResourcesChangeList(after = after)
60+
newsResourceApi.getNewsResourcesChangeList(after = after)
10461
}

0 commit comments

Comments
 (0)