안녕하세요, 🐥 오늘은 DI 1탄에 이어서 2탄으로 돌아왔습니다Rest API(Retrofit)와 Dagger2
를 함께 사용하는 예제로 진행하겠습니다
이번 포스팅에서 진행할 3가지 기능입니다
📌 mocky API
📌 Retrofit을 이용해 REST API 연동
📌 Dagger2로 Retrofit 사용
Mocky 사용법
1. https://designer.mocky.io/에서 NEW MOCK을 클릭하여 이동
2. 출력하고자 하는 값을 JSON 형태로 Body에 넣어줍니다
3. GENERATE MY HTTP RESPONSE 클릭
- 이 경우에 BASE_URL은 https://run.mocky.io/v3/ 입니다
💬 본격적으로 DI를 시작하기 전, Retrofit과 dagger2 사용을 위해서 library를 추가합니다
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
//dagger
def dagger_version = '2.40.5'
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
//rxjava
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
REST API 연동
1. Response에 맞는 data class 생성
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class TestData(
var date: String? = "",
var blog: String? = "",
var title: String? = "",
var summary: String? = ""
) : Parcelable
2. Uri를 명시, Response를 반환하는 interface 생성 (mocky에선 io/뒤에 붙는 주소가 uri)
interface mainAPI {
@GET("v3/345bfd6f-97e3-4e41-8113-b9584f571efc")
fun getTestData(): Call<TestData>
}
우선, REST API는 위의 두가지만 선언해두고, 아래의 Module에 생성하는 함수를 사용하겠습니다
Module
객체 반환
Module
은 객체를 반환하는 함수를 통해 객체를 공급해주는 클래스입니다.
클래스에만 @Module을 붙일 수 있고, Module class내에서는 객체를 반환하는 함수가 필요합니다.
객체를 반환하는 함수엔 @Provides
혹은 @Binds
annotation이 붙습니다.
반환하는 함수명에는 주로 prefix로 provide를 붙입니다.
위의 annotation은 @Module이 선언된 곳에만 사용가능합니다.
Retrofit의 Builder는 한번 생성 후 동일한 객체를 이용하므로, Singleton을 annotation으로 선언합니다
import com.di.retrofitdagger.retro.mainAPI
import dagger.Module
import dagger.Provides
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@Module
class NetworkModule {
@Provides
@Singleton
fun provideAPI(): mainAPI {
val okHttpClient = OkHttpClient.Builder()
okHttpClient.connectTimeout(3, TimeUnit.MINUTES)
return Retrofit.Builder()
.baseUrl("https://run.mocky.io/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient.build())
.build()
.create(mainAPI::class.java)
}
}
Component
객체 생성, 제공
Component는 interface
, abstract class
에서만 사용 가능
연결된 Module을 이용해 객체를 생성하고, inject로 요청받은 instance에 객체를 제공합니다
즉, DI의 주된 역할을 수행한다고 볼 수 있습니다
그리고 중요한 것은, Component는 modules을 활용해 객체를 생성하기 때문에
@Component를 붙일 때 반드시 modules를 함께 선언해줘야 합니다
Component class를 생성한 후 앱을 빌드하게 되면, Dagger + Component 명칭의 네이밍을 가진 클래스가
자동으로 생성됩니다. (ex. DaggerNetworkComponent)
import com.di.retrofitdagger.MainActivity
import dagger.Component
import javax.inject.Singleton
@Component(modules = [NetworkModule::class])
@Singleton
interface NetworkComponent {
fun inject(activity: MainActivity)
}
Component 클래스 안에서 Method는 크게 2가지 유형으로 나눠집니다
1. Provision
매개 변수가 없고 Module이 제공하는 객체타입을 반환형으로 가지는 유형
fun makeTest(): Test
2. Member-injection Method
인자로 받은 Component 내부에 @inject가 붙은 필드에 DI
fun inject(activity: MainActivity)
해당 예제에선 MainActivity 내부에 @inject가 붙은 필드에 직접 주입하는 2번 방식을 사용합니다
MainActivity
fun inject(activity: MainActivity)
Component의 해당 함수를 통해 MainActivity 속 @Inject가 선언된 필드에 DI를 확인합니다
Mocky에서 가져온 response를 아래의 화면과 같이 나타내려고 합니다
🔍
package com.di.retrofitdagger
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import com.di.retrofitdagger.di.DaggerNetworkComponent
import com.di.retrofitdagger.di.NetworkComponent
import com.di.retrofitdagger.retro.mainAPI
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
@Inject
lateinit var api: mainAPI // 1) @Inject를 가진 variable 선언
lateinit var component: NetworkComponent
private val tvTitle: TextView by lazy {
findViewById(R.id.tvTitle)
}
private val tvBlog: TextView by lazy {
findViewById(R.id.tvBlog)
}
private val tvDate: TextView by lazy {
findViewById(R.id.tvDate)
}
private val tvSum: TextView by lazy {
findViewById(R.id.tvSum)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//di inject network
component = DaggerNetworkComponent.builder().build()
component.inject(this)
// 2)생성된 Component의 명칭으로 builder 연결 -> inject
// NetworkComponent에 선언된 inject 함수 사용
testStart()
}
private fun testStart() {
api.run {
// 3) inject한 api를 통해 View에 setdata
getTestData().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
tvTitle.text = it.title ?: "Empty Value"
tvBlog.text = it.blog ?: "Empty Value"
tvDate.text = it.date ?: "Empty Value"
tvSum.text = it.summary ?: "Empty Value"
}, { e ->
})
}
}
}
dagger2를 통해 의존성을 주입하고, retrofit으로 간단한 데이터를 가져오는 예제를 만들어보았습니다
🙇🏻♀️
도움이 되셨다면 공감하기 부탁드려요 🙋🏻♀️
'📱 Android' 카테고리의 다른 글
[Android] Clean Architecture in Android (0) | 2022.05.29 |
---|---|
[Firebase SDK 추가 이슈] build.gradle in Bumblebee (0) | 2022.05.22 |
[Dagger2] #1 DI 기본개념 (0) | 2022.03.02 |
[Android] 앱 설치여부 확인 및 플레이스토어 이동 (0) | 2021.12.27 |
[Android] RecyclerView (1) - Multi ViewHolder (0) | 2021.05.15 |