https://www.youtube.com/watch?v=VrFnJvcBXm4&list=PLYlZbv3fX7WvaWMB9zRgbO7Hzf3MRgrIf&index=18
๋ค์ ์์๊ณผ ๊ต์ฌ ์ฐธ๊ณ ํ์ฌ ์์ฑํ์ต๋๋ค.
์ ๋ชจ๋ Network๋ฅผ ๋ง๋ค์ด์ค๋ค.
build.gradle (Module. ์์ฑํ ๋ชจ๋๋ช
) ์์
1) viewBinding ์ค์ ํด์ค๋ค.
viewBinding{
enabled = true
}
2) ์ฌ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฑ๋กํด์ค๋ค.
implementation 'com.android.volley:volley:1.2.1'
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.google.code.gson:gson:2.8.9'
//parser ์ง์
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'
์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๊ถ๊ธํด์ ์๋๋ก์ด๋ ๋๋ฒจ๋กํผ ๊ณต์ ์ฌ์ดํธ๋ฅผ ์ฐพ์๋ดค๋ค.
https://developer.android.com/training/volley?hl=ko
๐ Volley
Volley๋ Android ์ฑ์ ๋คํธ์ํน์ ๋ ์ฝ๊ณ , ๋ฌด์๋ณด๋ค๋ ๋ ๋น ๋ฅด๊ฒ ํ๋ HTTP ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. Volley๋ GitHub์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Volley๋ UI๋ฅผ ์ฑ์ฐ๊ธฐ ์ํด ์ฌ์ฉ๋๋ RPC ์ ํ ์์ (์: ๊ฒ์๊ฒฐ๊ณผ ํ์ด์ง๋ฅผ ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ๋ก ๊ฐ์ ธ์ค๊ธฐ)์ ํ์ํฉ๋๋ค. ๋ชจ๋ ํ๋กํ ์ฝ๊ณผ ์ฝ๊ฒ ํตํฉ๋๋ฉฐ ์์ ๋ฌธ์์ด, ์ด๋ฏธ์ง ๋ฐ JSON ์ง์์ ์ฆ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ํ์ํ ๊ธฐ๋ฅ์ ๋ํ ๊ธฐ๋ณธ ์ง์์ ์ ๊ณตํ์ฌ ์ฌ์ฉ์์ ์ฑ์ ๋ง๋ ๋ก์ง์ ์ง์คํ ์ ์์ต๋๋ค.Volley๋ ํ์ฑํ๋ ๋์ ๋ชจ๋ ์๋ต์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์งํ๋ฏ๋ก ๋๊ท๋ชจ ๋ค์ด๋ก๋ ๋๋ ์คํธ๋ฆฌ๋ฐ ์์ ์๋ ์ ํฉํ์ง ์์ต๋๋ค. ๋๊ท๋ชจ ๋ค์ด๋ก๋ ์์ ์ DownloadManager์ ๊ฐ์ ๋์์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ณผ์
- ๊ฐ๋จํ ์์ฒญ ๋ณด๋ด๊ธฐ
Volley์ ๊ธฐ๋ณธ ๋์์ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ ๋ฐ ์์ฒญ์ ์ทจ์ํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค.- RequestQueue ์ค์
RequestQueue ์ค์ ๋ฐฉ๋ฒ๊ณผ ์ฑ๊ธํค ํจํด์ ๊ตฌํํ์ฌ ์ฑ์ ์ ์ฒด ๊ธฐ๊ฐ ๋์ ์ง์ํ๋ RequestQueue๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค.- ํ์ค ์์ฒญ ์คํ
Volley์ ๊ธฐ๋ณธ ์์ฒญ ์ ํ(์์ ๋ฌธ์์ด, ์ด๋ฏธ์ง, JSON) ์ค ํ๋๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค.- ๋ง์ถค ์์ฒญ ๊ตฌํ
๋ง์ถค ์์ฒญ ๊ตฌํ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค.
๐ฉ๐ป => ๊ฐ๋จํ ์์ฒญ ๋ฐ ์๋ต์ผ๋ก HTTP ํต์ ์ ์ฝ๊ฒ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก API ํธ์ถ, ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ณ ํ๋๋ณด๋ค.
3) manifest ํ์ผ์ ์ธํฐ๋ท ํผ๋ฏธ์ ๋ฑ๋ก
<uses-permission android:name="android.permission.INTERNET"/>
4) ๊นก์ค ์๋๋ก์ด๋ ๊ณต์ ์ฌ์ดํธ์์ ๊ตฌ๊ธ ๋ฌธ์๋ก ์ค์ต ํ์ผ์ ๊ณต์ ๋ฐ์์ ๋ถ์ฌ์ฃผ์๋ค.
์ฌ๊ธฐ์ activityํ์ผ์ด ํ๋ ์๊ธฐ ๋๋ฌธ์ <application ์์
android:name="com.example.ch18_network.MyApplication"
๋ฑ๋กํด์ค๋ค.
๐ถ com/example/newsapp/model/ItemModel.kt
ItemModel class์ property๋ฅผ ์์ฑํด์ค๋ค.
=> ๋ฐ์ดํฐ๋ฅผ ํํํ๋ ๋ชจ๋ธ
์ด ๋ฐ์ดํฐ๋ volley, retrofit์ผ๋ก ๋ถํฐ ๋ฐ์ ๋ฐ์ดํฐ์ธ๋ฐ,
volley๋ก๋ถํฐ ๋ฐ์ ๋คํธ์ํฌ ๋ฐ์ดํฐ๋ ๊ฐ๋ณ property์ ๋์
์์ผ์ฃผ๋ฉด ๋๋ค.
retrofit์ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ๊ฐ์ฒด ์์ฑํด์ property์ ๋ด์์ค๋ค. ์๋์ผ๋ก ๋๋ค๋ณด๋, ์๋ฒ๋ก๋ถํฐ ๋ฐ๋ key์ property์ ์ด๋ฆ์ด ์ค์ํ๋ค.
๐ถ com/example/ch18_network/model/PageListModel.kt
๋ชฉ๋ก ๋ฐ์ดํฐ => ์ด ์ค ํ๋๊ฐ ItemModel
์ ๋ฌธ๊ธฐ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ๊ฑฐ๋ค.
๐ถ com/example/ch18_network/MyApplication.kt
package com.example.ch18_network
import android.app.Application
import com.android.volley.toolbox.Volley
import com.example.ch18_network.retrofit.NetworkService
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class MyApplication: Application() {
companion object {
val QUERY = "travel"
val API_KEY = "๋ฐ๊ธ๋ฐ์ ํค"
val BASE_URL = "https://newsapi.org"
val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"
}
}
https://newsapi.org/register
์ ๋งํฌ์์ api key๋ฅผ ๋ฐ๊ธ๋ฐ๋๋ค. ๊ธ๋ฐฉ ๋๋ค.
์ด๋์์ ๊ฐ ๋ฑ ํ๋ฒ๋ง ํด์ฃผ๋ฉด ๋๋ค.
๐ถ com/example/ch18_network/MyApplication.kt
var networkService: NetworkService
//retrofit ๊ฐ์ฒด ์ด๊ธฐํ
val retrofit: Retrofit
get() = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
init{
networkService = retrofit.create(NetworkService::class.java)
}
์ถ๊ฐ์ค๋ช
:
GsonConverterFactory ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ๋ฐ์ดํฐ converting ํ ๊ฒ์
์ธํฐํ์ด์ค ํ์
์ผ๋ก ์ค์ ๋คํธ์ํน ๊ฐ๋ฅํ๊ฒ ํด๋์ค๋ฅผ ์ด์ฉ
๐ถ com/example/ch18_network/recycler/MyAdapter.kt
package com.example.ch18_network.recycler
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.ch18_network.databinding.ItemMainBinding
import com.example.ch18_network.model.ItemModel
class MyViewHolder(val binding: ItemMainBinding): RecyclerView.ViewHolder(binding.root)
class MyAdapter(val context: Context, val datas: MutableList<ItemModel>?): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
override fun getItemCount(): Int{
return datas?.size ?: 0
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder
= MyViewHolder(ItemMainBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val binding=(holder as MyViewHolder).binding
}
}
์ ์ฒด ๋ฐ์ดํฐ๊ฐ recycler view์ ์ํด ๋์ฌ๊ฑฐ๋ค.
ํญ๋ชฉ์ ๊ตฌ์ฑํด์ -> ์ด๋ฏธ์ง(์๋ฒ์์ ๋ค์ด๋ฐ์) -> glide ํ์ฉ
=> onBindViewHolder ํจ์์ ์์ฑ
์ด๋ฏธ์ง ํธ๋ค๋ง์ ๊ฐ์ฅ ํธํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ glide์.
val model = datas!![position]
binding.itemTitle.text = model.title
binding.itemDesc.text = model.description
binding.itemTime.text = "${model.author} At ${model.publishedAt}"
Glide.with(context)
.load(model.urlToImage)
.into(binding.itemImage)
Glide๋ก ์ด๋ฏธ์ง ๋ก๋ํ๋ฉด ์์ฒญ ๊ฐ๋จํ๋ค!
๐ถ com/example/ch18_network/retrofit/NetworkService.kt
package com.example.ch18_network.retrofit
import retrofit2.http.GET
import retrofit2.http.Query
interface NetworkService {
@GET("/v2/everything")
fun getList(
@Query("q") q:String?,
@Query("apiKey") apiKey:String?,
@Query("page") page:Long,
@Query("pageSize") pageSize:Int
): Call<PageListModel>
}
"/v2/everything" => ์ด URL๋ก ์๋ฒ ์์ฒญ์ ๋ณด๋ด๋ฉด์, ๋งค๊ฐ๋ณ์๊ฐ์ผ๋ก apiKey, page, pageSize ์๋ฒ๋ก ์ ๋ฌ
๋ฆฌํดํ์ ์ ๋คํธ์ํนํ ์ ์๋ Call๊ฐ์ฒด => ๊ฒฐ๊ณผ ํ์ ์ PageListModel.
๐ถ com/example/ch18_network/VolleyFragment.kt
//retroft์ ์๋์ด์ง๋ง volley๋ ์๋์ฒ๋ผ ์ง์ ์์ฑ
val url = MyApplication.BASE_URL+"/v2/everything?q="+
"{${MyApplication.QUERY}&apiKey=${MyApplication.API_KEY}&page=1&pageSize=5"
QUERY=๊ฒ์์ด, ๋๋จธ์ง ๋ณ์๋ค => ์๋ฒ์ ๋์ด๊ฐ
val queue = Volley.newRequestQueue(activity)
: Volley ์ชฝ ๋คํธ์ํฌ ์์ฒญ์
์ด ๊ฐ์ฒด๋ก request addํ๋ฉด ์๋ฒ์ ์ฐ๋๋จ!!!!
val jsonRequest = object: JsonObjectRequest (
Request.Method.GET,
url,
null,
Response.Listener<JSONObject>{ response ->
val jsonArray = response.getJSONArray("articles")
val mutableList = mutableListOf<ItemModel>()
for(i in 0 until jsonArray.len()){
ItemModel().run{
val article = jsonArray.getJSONObject(i)
author = article.getString("author")
title = article.getString("title")
description = article.getString("description")
urlToImage = article.getString("urlToImage")
publishedAt = article.getString("publishedAt")
mutableList.add(this)
}
}
binding.volleyRecyclerView.layoutManager = LinearLayoutManager(activity)
binding.volleyRecyclerView.adapter = MyAdapter(activity as Context, mutableList)
},
Response.ErrorListener{
Log.d("error", "error...$it")
}
)
์์ฒญ์์๊ฒ ์ ๋ณด๋ฅผ ์ค์ผํจ. ๋์ด์จ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ํธ๋ค๋ง ํ๋์ง
=> request ๊ฐ์ฒด
์ถ๊ฐ์ค๋ช
val jsonRequest = object: JsonObjectRequest (
Request.Method.GET,
url,
null,
Response.Listener< JSONObject >
=> volley ์ชฝ
์ต์ํ์ ์ ๋ณด: GET ๋ฐฉ์์ผ๋ก, ์๋ฒ์์ฒญ url, ์๋ฌ ๋ฆฌ์ค๋:null, ๋ฐ์ดํฐ๊ฐ ๋์ด์์๋์ ๋ฐ์ดํฐ ํ = callback = Response.Listener< JSONObjet >
Response.Listener< JSONObject >{ response ->
val jsonArray = response.getJSONArray("articles")
val mutableList = mutableListOf< ItemModel >()
=> ์ ์์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ ๋ ์คํ๋์์ ๋์ ๊ฒฐ๊ณผ๊ฐ
json ํค ๊ฐ = articles, ์ฌ๋ฌ๊ฐ๋๊น array type
๊ฐ๊ฐ ์ ๋ฌธ๊ธฐ์ฌ ํ๋๋ฅผ ItemModel์ ๋ด๊ธฐ ์ํด mutableList
for(i in 0 until jsonArray.len()){
ItemModel().run{
val article = jsonArray.getJSONObject(i)
author = article.getString("author")
title = article.getString("title")
description = article.getString("description")
urlToImage = article.getString("urlToImage")
publishedAt = article.getString("publishedAt")
mutableList.add(this)
}
}
=> for๋ฌธ์ด ๋๋ฉด์, ์ ๋ฌธ๊ธฐ์ฌ ํ๋์ ๋ํ json object๋ฅผ ์ป์ด๋ด์,
๊ทธ json object์ ํค ๊ฐ์ด "author"์ธ ๋ฐ์ดํฐ๋ฅผ ItemMoodel์ ๋ณ์ author์ ๋ด๋๋ค.
๋นจ๊ฐ์ค์ด ๋จ๋๊ฑด ๋งค๊ฐ๋ณ์๊ฐ ํ๋ ๋ ์์ด์ ์์ง ๋ค ์์ฑํ์ง ์์์ ๊ทธ์ด์ง ๊ฒ์ผ๊ฑฐ๋ค..
binding.volleyRecyclerView.layoutManager = LinearLayoutManager(activity)
binding.volleyRecyclerView.adapter = MyAdapter(activity as Context, mutableList)
},
Response.ErrorListener{
Log.d("error", "error...$it")
}
)
=> ์๋ฒ๋ก ๋ถํฐ ๋ฐ์ ๋ฐ์ดํฐ๊ฐ for๋ฌธ์ ํตํด ๋ค ์ค๋น๊ฐ ๋์๋ค๊ณ ํ๋ฉด,
recyclerView๋ก ๊ตฌ์ฑํด์ค์ผ๋๋๊น ๊ทธ ์ค๋น์์
์.
Response.ErrorListener: ์คํจ ์์ callback
๐ฉ๐ป => ๊ทธ๋ฌ๋๊น,
์ ์ฝ๋๋ค์ ๊ฒฐ๊ตญ ์์ฑ์์ 4๋ฒ์งธ ๋งค๊ฐ๋ณ์์ด๋ฉด์,
์๋ฒ๋ก๋ถํฐ ์ ์์ ์ผ๋ก ๊ฒฐ๊ณผ๊ฐ์ด ๋์ด์์๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊ฑฐ๋,๋ฅผ ๋ํ๋ธ๊ฑฐ๋ค!
{
override fun getHeaders(): MutableMap<String, String> {
val map = mutableMapOf<String, String>(
"User-agent" to MyApplication.USER_AGENT
)
return map
}
}
return binding.root
}
=> ์์ฒญ ์์ ์ค์ ! ํค๋๊ฐ ์ค์
val map = mutableMapOf<String, String>
=> map ๊ฐ์ฒด๋ฅผ ์ด ํจ์์์ returnํจ => ์๋์ผ๋ก map ๊ฐ์ฒด๊ฐ request header ๊ฐ์ ์ง์ ์ด ๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํญ์ ํค๋๊ฐ๋ key, value๋ก ํํ๋๋ค.
=> return ์ํจ map์ด request header ๊ฐ์ผ๋ก ๋ค์ด๊ฐ๋ค.
๐ฉ๐ป => ๊ฒฐ๊ตญ ๋คํธ์ํนํ ์ ์๋ queue๊ฐ์ฒด, ๋คํธ์ํฌ ์ ์ด๋ค ์ ๋ณด ๋ฐ ์ฝ๋ฐฑ๋ค์ ๋ฑ๋กํ request๋ ์ค๋นํ๋ค.
**์ด์ ์ค์ ๋คํธ์ํฌ ์๋๋ queue ๊ฐ์ฒด์ request๋ฅผ ๋ด์ผ๋ฉด ๋๋ค.
queue.add(jsonRequest)
=> ์ด ์๊ฐ ๋คํธ์ํน์ด ๋๋ค. ๊ฒฐ๊ณผ์ ๋ฐ๋ผ response ์ฝ๋ฐฑ ํจ์ ํน์ error์ ์ฝ๋ฐฑ ํจ์๊ฐ ์คํ๋๋ค.
๐ถ com/example/ch18_network/RetrofitFragment.kt
๋คํธ์ํน์ ํ๊ธฐ ์ํด์๋ call ๊ฐ์ฒด๊ฐ ์์ด์ผ ํ๋ค.
=> ์ธํฐํ์ด์ค ํจ์๋ก ์ป์ด๋ผ ์ ์์
val call: Call<PageListModel> = MyApplication.networkService.getList(
MyApplication.QUERY,
MyApplication.API_KEY,
1,
10
)
์ฝ ๊ฐ์ฒด๋ ๋คํธ์ํน์ด ๊ฐ๋ฅํ ๋ฟ, ๋คํธ์ํน์ ์คํํ๋ ค๋ฉด ๋ช
๋ น์ ๋ฐ๋ก ๋ด๋ ค์ค์ผ๋๋ค.
๋คํฌ์ํน ์คํ ๋ช
๋ น: enqueue
=> call?.enqueue()
์๋ฒ์์ ๋์ด์ค๊ฑฐ๋, ์คํจํ๊ฑฐ๋ -> ์๋ call back ๋ฑ๋กํด์ผ๋จ
call?.enqueue(object: Callback<PageListModel>{
override fun onResponse(call: Call<PageListModel>, response: Response<PageListModel>){
}
override fun onFailure(call: Call<PageListModel>, t: Throwable) {
}
})
=> callback ์์ ์ฑ๊ณต์, ์คํจ์ ์ผ์ด์ค 2๊ฐ
์ฑ๊ณต ์:
override fun onResponse(call: Call<PageListModel>, response: Response<PageListModel>){
if(response.isSuccessful){
binding.retrofitRecyclerView.layoutManager = LinearLayoutManager(activity)
binding.retrofitRecyclerView.adapter = MyAdapter(activity as Context,
response.body()?.articles
)
}
}
if(response.isSuccessful): ์ฑ๊ณตํ์ผ๋ฉด,
๋๊ฐ์ด ํ๋ฉด๊ตฌ์ฑ!
์คํจํ์๊ฒฝ์ฐ ๊ฐ๋จํ Log๋ง ์ฐ์ด์ค.
=> retrofit: ๋ชฉ์ ๊ณผ ๋ด์ฉ์ด ๊ฐ์๋ฐ volley์ ๋นํด ๊ฐ๋จํ๋ค. ์ด์ ๋ volley๋ ๋ชจ๋ธ ํด๋์ค์ ์ผ์ผ์ด ๋ด์์ค์ผ๋๋๋ฐ, ๋ค ์๋์ผ๋ก ํด์ฃผ๋๊ฒ converter.
์ด๋์ ๋ด์์ฃผ๋ฉด ๋๋์ง๋ง ์๋ ค์ฃผ๋ฉด ๋จ.
์ค..์ volley๋ ์๋ฐ๊น
ใ ใ ์์ ์คํ๊ฐ ์์๋ค. ์์ ํ๋ ์ ์๋!!
์คํฌ๋กค๋ ์๋๋ค!