Android의 권장 앱 아키텍처
출처:https://developer.android.com/training/dependency-injection/manual?hl=ko
Android의 권장 앱 아키텍처는 코드를 클래스로 분할하여 관심사 분리의 이점을 누리길 권장한다.
지금까지 예제에서는 Repository를 거치지 않고 ViewModel에서 바로 Retrofit으로 데이터를 불러왔었다. 이번 예제에서는 권장 아키텍쳐를 사용하며 Repository를 생성해서 여기서 데이터를 받아오고 ViewModel과 데이터를 연결해서 사용할 것이다.
예제 소개
https://raw.githubusercontent.com/googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/plants.json
Retrofit으로 위 url의 식물 데이터들(텍스트, 이미지)을 불러와서 RecyclerView에 넣어보자
이번 예제의 패키지 구조는 다음과 같다.
코드로 구현하기
Glide 라이브러리 사용하기: https://github.com/bumptech/glide
dependencies {
//retrofit 사용
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
//코루틴 사용
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
//ViewModelScope 사용
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
//Glide 사용
implementation 'com.github.bumptech.glide:glide:4.14.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
}
<uses-permission android:name="android.permission.INTERNET" />
package com.woonyum.jetpack_ex2.model
data class Plant(
val plantId: String,
val name: String,
val description: String,
val growZoneNumber: Int,
val wateringInterval: Int,
val imageUrl: String
)
package com.woonyum.jetpack_ex2.api
object RetrofitInstance {
val BASE_URL = "https://raw.githubusercontent.com/"
val client = Retrofit
.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun getInstance(): Retrofit {
return client
}
}
package com.woonyum.jetpack_ex2.api
interface MyApi {
@GET("googlecodelabs/kotlin-coroutines/master/advanced-coroutines-codelab/sunflower/src/main/assets/plants.json")
suspend fun getAllPlants(): List<Plant>
}
Repository에서 API를 호출해서 서버로부터 데이터를 받아온다.
package com.woonyum.jetpack_ex2.repository
//서버에서 데이터 가져오는 역할
class Repository {
private val client = RetrofitInstance.getInstance().create(MyApi::class.java)
suspend fun getAllData() = client.getAllPlants()
}
package com.woonyum.jetpack_ex2.viewModel
class MainViewModel : ViewModel() {
private val repository = Repository()
private val _result = MutableLiveData<List<Plant>>()
val result: LiveData<List<Plant>>
get() = _result
fun getAllData() = viewModelScope.launch {
_result.value =repository.getAllData()
}
}
package com.woonyum.jetpack_ex2
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
viewModel.getAllData()
val rv = findViewById<RecyclerView>(R.id.rv)
viewModel.result.observe(this, Observer {
val customAdapter = CustomAdapter(this, it)
rv.adapter = customAdapter
rv.layoutManager = LinearLayoutManager(this)
})
}
}
Glide 라이브러리를 사용해 ImageView에 해당 url 이미지를 넣어준다.
package com.woonyum.jetpack_ex2.adapter
class CustomAdapter(val context: Context, val dataSet: List<Plant>) :
RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val textView: TextView = view.findViewById(R.id.textArea)
val imageView: ImageView = view.findViewById(R.id.imageArea)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.text_row_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) {
holder.textView.text = dataSet[position].name
Glide.with(context)
.load(dataSet[position].imageUrl)
.into(holder.imageView)
}
override fun getItemCount(): Int {
return dataSet.size
}
}
RecyclerView Adapter 각 함수의 작동방식과 매개변수의 의미를 잘 모르겠다. 특히 상속받을 때 제네릭타입으로 어댑터의 ViewHolder가 넘어가는 방식이 잘 이해가 안된다.
공부해서 정리해야지