๐ SeSAC์ 'JetPack๊ณผ Kotlin์ ํ์ฉํ Android App ๊ฐ๋ฐ' ๊ฐ์ข๋ฅผ ์ ๋ฆฌํ ๊ธ ์ ๋๋ค.
<uses-permission android:name="android.permission.INTERNET"/>
์๋ฒ์ http ํต์ ์ ํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์๋ค.
java SE ์์ ์ ๊ณต๋๋ API.
ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ์ด๊ธฐ ๋ฒ์ ๋ถํฐ ์ ๊ณต.
์ฌ์ฉํ ์๋ ์์ง๋ง ์ฝ๋ ์์ฑ ์ ํ๋ก๊ทธ๋จ ์ฝ๋๊ฐ ๊ธธ์ด์ง๊ฑฐ๋ ๋ณต์ก, ์ค๋ณต๋๋ ์ธก๋ฉด์ด ์๋ค.
๊ทธ๋์ ์ฌ์ฉํ๋ค๋ฉด HttpURLConnection ๋ฅผ ์ถ์ํ ์์ผ ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก์ ๊ฐ๋ฐ.
apache http ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
์๋๋ก์ด๋ ์ด๊ธฐ HttpURLConnection ๋ถ๋ถ์ ๋ด๋ถ์ ์ธ ๋ฌธ์ ๊ฐ ์์ด ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ๋์ด ๋ง์ด ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
Android 6.0 ์์๋ Apache HTTP ํด๋ผ์ด์ธํธ์ ๋ํ ์ง์์ด ์ ๊ฑฐ.
Android 9 ๋ถํฐ๋ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ bootclasspath ์์ ์ ๊ฑฐ๋๊ณ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฑ์์ ์ฌ์ฉํ ์ ์๋ค.
Android 9 ์ด์์ ๋์์ด๋ก ํ๋ ์ฑ์ด Apache HTTP ํด๋ผ์ด์ธํธ๋ฅผ ๊ณ์ ์ฌ์ฉํ๋ ค๋ฉด AndroidManifest.xml ์ ์ถ๊ฐํด์ผ ํ๋ค.
์ด๊ธฐ์ HttpURLConnection ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฌธ์ ๊ฐ ์์ด Apache ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ ๋ถ๊ตฌํ๊ณ ์๋๋ก์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ ๋์๋ค. ๊ทธ๋ฌ๋ค HttpURLConnection ์ ๋ด๋ถ์ ๋ฌธ์ ํด๊ฒฐ ๋ฐ ๋ง์ http ๋คํธ์ํน ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฑ์ฅ์ผ๋ก ์ธ๊ธฐ๊ฐ ์ฌ๊ทธ๋ผ๋ค์๋ค.
๊ทธ๋์ ํ์ฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฑ์์ ์ด์ฉํ ์ ์๋ ์ํ์ด๋ค. ๋ง์ฝ ์ฌ์ฉํ๋ค๋ฉด Manifest ์ <uses-library>
๋ก ์ ์ธํด์ผ ํ๋ค. ๊ทธ ์ธ์๋ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ ๋คํธ์ํน ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ๋ด๋ถ์ ์ผ๋ก HttpClient ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์๋ ์๊ธฐ ๋๋ฌธ์ ์ ์ธํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์ข
์ข
์๋ค.
2013๋ Google I/O ์์ ๋ฐํํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
http ํต์ ์ ์ํ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
1.0.0 ๋ฒ์ ์์๋ ๋ด๋ถ์ ์ผ๋ก apache http ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ผ๋ฉฐ 1.1.1 ๋ฒ์ ์์๋ apache http ์ข ์์ฑ์ด ์ ๊ฑฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
dependency ๋ฅผ ์ถ๊ฐํด ์ฃผ์ด์ผ ํ๋ค.
์ด์ฉ ๋น์จ์ด ๋์ง๋ ์์ง๋ง ๊ฐ๋ volley ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๊ฐ๋ฐํ๋ ๊ฐ๋ฐ์๋ ์๋ค.
square ๋ผ๋ ํ์ฌ์์ ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
์์ฃผ ์ ๋ช ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
์ด๊ฒ๋ square ์์ ๋ง๋ค์๋ค.
2013๋ 1.0 ๋ฐํ ๋์๊ณ 2016๋ 2.0 ๋ฐํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
๋ด๋ถ์ ์ผ๋ก okHttp ๋ฅผ ์ด์ฉํ๋ค.
ํ์ฌ ๊ฐ์ฅ ์ ๋ช
ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ. ํ ์์ ๊ฐ์ฅ ์ด์ฉ ๋น์จ์ด ๋๊ณ ์ ๋ช
ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
2.0 ๋ฒ์ ์ด ๋์ค๋ฉด์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ด ์๊ณ retrofit ์ด๋ผ๊ณ ํ๋ฉด ํํ 2.0 ๋ฒ์ ์ ์ง์นญํ๋ค.
<application
android:usesCleartextTraffic="true">
api level 28 ๋ถํฐ ๋คํธ์ํฌ ๋ณด์ ์ ์ฑ ๋ณ๊ฒฝ.
https ๋ ๋ฌธ์ ์์ผ๋ฉฐ http ์ธ ๊ฒฝ์ฐ๋ ์ค์ ์ด ํ์
useCleartextTraffic
์ค์
์ด์ ์๋ ์์๋ ์์ฑ. api level 28 ๋ถํฐ๋ ๊ธฐ๋ณธ๊ฐ์ด false ๋ก ๋ณ๊ฒฝ. ๋ช ์์ ์ผ๋ก true ๋ก ์ค์ ํด ์ฃผ์ด์ผ ํ๋ค.
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">192.168.1.2</domain>
</domain-config>
</network-security-config>
<application
android:networkSecurityConfig="@xml/network_security_config">
xml ๋๋ ํ ๋ฆฌ์ ๊ฐ๋ฐ์ ์์์ xml ํ์ผ์ ์์ฑํ๋ค. <network-security-config>
๋ฅผ ๋ฃจํธ ํ๊ทธ๋ก ์์ฑํ๊ณ ์ ์ชฝ์ <domain>
์ http ํต์ ์ ํ์ฉํ๋ ๋๋ฉ์ธ ํน์ IP ์ฃผ์๋ฅผ ๋ฑ๋กํ๋ค.
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
ConnectivityManager ์ ์ด์ฉํด ๋คํธ์ํฌ ์ํ๋ฅผ ํ์
์์คํ ์๋น์ค์ด๋ค.
๋คํธ์ํฌ ์ํฉ ํ์ ์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํด๋์ค. ๋คํธ์ํฌ ์ ๋ณด ํ์ ์ ์ํด์๋ ํผ๋ฏธ์ ์ด ์๊ตฌ๋๋ค.
val nw = connectivityManager.activeNetwork
val acNw = connectivityManager.getNetworkCapabilities(nw)
ConnectivityManager ์ getActiveNetwork()
์ ์ด์ฉํด Network ๊ฐ์ฒด๋ฅผ ํ๋
Network ๊ฐ์ฒด๋ฅผ getNetworkCapabilities() ํจ์์ ๋งค๊ฐ๋ณ์๋ก ์ง์ ํ๋ฉด ํ์ฌ ์ ์๋ ๋คํธ์ํฌ ๋ง ์ ๋ณด๋ฅผ ํ๋ ๊ฐ๋ฅ.
์ค์ ๋คํธ์ํฌ๋ฅผ ํ๋ ๊ฒ์ด ์๋๋ผ ๋คํธ์ํฌ ์ ๋ณด๊ฐ ๋ด๊ธด ๊ฐ์ฒด๋ฅผ ํ๋ํด getter ํจ์๋ก ํ์ฌ ๋คํธ์ํฌ ์ํ๋ฅผ ํ์ ํ ์ ์๋ค.
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
return "wifi online"
}
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
return "cellular available"
}
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/resultView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.kotdev99.android.c85
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val resultView = findViewById<TextView>(R.id.resultView)
resultView.text = isNetworkAvailable()
}
private fun isNetworkAvailable(): String {
val manager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val nw = manager.activeNetwork ?: return "offline"
val actNw = manager.getNetworkCapabilities((nw)) ?: return "offline"
return when {
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
return "wifi online"
}
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
return "cellular online"
}
else -> "offline"
}
} else {
if (manager.activeNetworkInfo?.isConnected ?: false) {
return "online"
} else {
return "offline"
}
}
}
}
Retrofit2 ๋ Square ์ฌ์์ ๋ง๋ HTTP ํต์ ์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํน์ ํ๋ ์์ํฌ
Retrofit
์ ๊ฐ๋
์ ๋คํธ์ํน ํ๋ก๊ทธ๋จ ์ฝ๋๋ฅผ ์๋์ผ๋ก ์์ฑํด ์ฃผ๋ ๊ฒ์ด๋ค.
๋คํธ์ํน์ ํ๊ธฐ ์ํด์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋คํธ์ํฌ ๊ด๋ จ ์ฝ๋ ์์ฑํด์ผ ํ๋ค. ํ์ง๋ง Retrofit
์ ์ด๋ฌํ ๊ธฐ๋ณธ ๋คํธ์ํน ์ฝ๋๋ฅผ ์๋์ผ๋ก ์์ฑ ํด์ค๋ค. ๊ฐ๋ฐ์๋ ๋คํธ์ํฌ์ ํ์ํ ์ ๋ณด (URL/request/response ๋ฑ) ๋ง Retrofit
์ ๋๊ฒจ์ฃผ๋ฉด ๋๋ ๊ฒ์ด๋ค.
๋คํธ์ํฌ ์ ๋ณด๋ ์ด๋
ธํ
์ด์
์ผ๋ก ์ ๋ฌํ๋ค. ๊ทธ๋์ ๊ฐ๋ฐ์๋ ์ธํฐํ์ด์ค๋ง ๋ง๋ค์ด์ Retrofit
์ ๋ฑ๋ก์์ผ ๋ฒ๋ฆฌ๋ฉด ๋์ด๋ค. ๊ทธ๋ฌ๋ฉด Retrofit
์์ ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด ์ค๋ค.
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
// Retrofit ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
implementation ("com.google.code.gson:gson:2.10.1")
// JSON ํน์ XML ํ์ฑ์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (optional)
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
// ํ์ฑํ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ธ(VO ํด๋์ค) ๊ฐ์ฒด๋ก ๋ณํํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (optional)
์๋๋ก์ด๋ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ XML parser ํน์ JSON parser ๋ฅผ ์ฌ์ฉํด๋ ๋๋ค. ๊ทธ๋ด ๊ฒฝ์ฐ ํ์ฑํ ๋ฐ์ดํฐ๋ฅผ VO ํด๋์ค ๊ฐ์ฒด๋ก ๋ง๋ค์ด์ฃผ๋ ์ฝ๋๋ฅผ ์ง์ ์์ฑํด ์ฃผ์ด์ผ ํ๋ค.
์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ฑ๊ณผ VO ํด๋์ค๋ก ๋ณํํด์ฃผ๋ ์์ ์ ๊ฐ์ํํ ์ ์๋ค. ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ฐ๋ฐ์ ์ทจํฅ๋๋ก ์ฌ์ฉํ๋ฉด ๋๋ค. ์์์๋ gson ์ ์๋ก ๋ค์๋ค.
Gson : com.squareup.retrofit2:converter-gson
Jackson : com.squareup.retrofit2:converter-jackson
Moshi : com.squareup.retrofit2:converter-moshi
Protobuf : com.squareup.retrofit2:converter-protobuf
Wire : com.squareup.retrofit2:converter-wire
Simple XML : com.squareup.retrofit2:converter-simplexml
JAXB : com.squareup.retrofit2:converter-jaxb
Scalars (primitives, boxed, and String) : com.squareup.retrofit2:converter-scalars
// ์๋ฒ ์ฌ์ด๋์์ ๋์ด์ค๋ JSON
{
"id": 3,
"email": "kotdev.coder@reqres.in",
"first_name": "kotdev",
"last_name": "Lawson",
"avatar": "https://reqres.in/img/faces/3-image.jpg"
}
// ๋ชจ๋ธ ํด๋์ค
data class UserModel(
var id: String, // JSON ์ Key์ ๋์ผํ๊ฒ ๋ณ์๋ฅผ ์ ์ธํด์ผ ํ๋ค
@SerializedName("first_name") // ๋ง์ฝ ์์์ ๋ณ์๋ช
์ ์ฌ์ฉํ๋ค๋ฉด annotation ์์ฑ
var firstName: String,
@SerializedName("last_name")
var lastName: String,
var avatar: String,
var avatarBitmap: Bitmap
}
๋ชจ๋ธ ํด๋์ค๋ ์๋ฒ์ ์ฃผ๊ณ ๋ฐ๋ ๋ฐ์ดํฐ๋ฅผ ํํํ๋ ํด๋์ค
(๋ชจ๋ธ/VO/DTO ํด๋์ค = ๋ญ๋ผ ๋ถ๋ฅธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ด๊ธฐ ์ํ ํด๋์ค๋ฅผ ์๋ฏธ)
JSON, XML ๋ฐ์ดํฐ๋ฅผ ํ์ฑํด ๋ชจ๋ธ ํด๋์ค ๊ฐ์ฒด์ ๋ด์์ฃผ๋ ๊ฒ์ ์๋ํ
interface INetworkService {
@GET("api/users") // http ํ๋กํ ์ฝ๊ณผ ์ฐ๋ํ URL ํน์ path ๋ฅผ ์ด๋
ธํ
์ด์
์ผ๋ก ์์ฑ
fun doGetUserList(@Query("page") page: String): Call<UserListModel>
// ์๋ฒ์ ๋๊ฒจ์ฌํ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด ๋งค๊ฐ๋ณ์์ Query ์ด๋
ธํ
์ด์
์ผ๋ก ๋๊ฒจ์ฃผ๋ฉด ๋๋ค
// "page" ๊ฐ Key ๊ฐ, page: String ๊ฐ Value ๊ฐ
}
๋คํธ์ํฌ ํต์ ์ ์ด์ฉ๋ ์ธํฐํ์ด์ค
๋คํธ์ํฌ ํต์ ์ด ํ์ํ ์๊ฐ ํธ์ถํ ํจ์๋ฅผ ์ ์ธ
์ค์ ๋คํธ์ํน์ด ๊ฐ๋ฅํ ๊ฐ์ฒด. ์ด Call ๊ฐ์ฒด๋ฅผ ๋ฐ์ enqueue() ํจ์๋ก ๋คํธ์ํน์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ ๋ค๋ฆญ์ผ๋ก ๋ฆฌํด ํ์ ์ผ๋ก์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ DTO ๊ฐ์ฒด๋ฅผ ๋ช ์ํด ์ค๋ค. ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋ฒ์์ ๋์ด์จ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก parsing ๋ฐ converting ํ์ฌ DTO ๊ฐ์ฒด๋ก ์์ฑ ํด์ฃผ๋ฉด, ์ฐ๋ฆฌ๋ ์ด๋ค DTO ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ๊ฒ์ธ์ง๋ง ์ ๋ค๋ฆญ ํ์ ์ผ๋ก ์๋ ค์ฃผ๋ฉด ๋๋ค.
val retrofit: Retrofit
get() = Retrofit.Builder()
.baseUrl("https://reqres.in/") // ์๋น์ค ์ธํฐํ์ด์ค์ ์ด๋
ธํ
์ด์
์ ์์ฑํ URL ์ ๋ฌธ์์ด์ ์ง์
.addConverterFactory(GsonConverterFactory.create())
.build()
Retrofit ์ ์ด๊ธฐ ์ค์ ์ ๋ชฉ์
baseUrl() ํจ์๋ฅผ ์ด์ฉํด ์๋ฒ ์ฐ๋์ ์ํ URL ์ ์ค์
์ฆ, ์๋น์ค ์ธํฐํ์ด์ค์ ์ด๋
ธํ
์ด์
์ฃผ์๋ฅผ path ๋ก ์ฌ์ฉํ ์ ์๋ค.
๋งค๋ฒ ํ ๋๋ฉ์ธ ์ฐ๋ ๊ฑด ๊ท์ฐฎ์ผ๋๊น!
baseUrl + @GET = "https://reqres.in/" + "api/users" = "https://reqres.in/api/users"
addConverterFactory() ํจ์๋ฅผ ์ด์ฉํด converter ์ง์
์ด๊ธฐ ์ค์ ์ด๋ ์ฑ์ด ์คํ๋๋ฉด์ ๋ฑ ํ ๋ฒ๋ง ์คํ๋๋ ๊ณณ์ ์์ฑํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ค.
var networkService: INetworkService = retrofit.create(INetworkService::class.java)
์ด์ ์ค์ ๋คํธ์ํน์ ํด์ผ ํ๋ค. ๋คํธ์ํน์ ์ํด์๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๊ฐ์ฒด๊ฐ ํ์ํ๋ค.
Retrofit์ create()
ํจ์์ ์ธํฐํ์ด์ค๋ฅผ ๋ฃ์ด์ค๋ค. ๋ถ์ฌ์ง ์ด๋
ธํ
์ด์
์ ๋ฐ๋ผ ๋คํธ์ํน ์ฝ๋๊ฐ ์์ฑ๋ ๊ฐ์ฒด๋ฅผ ์ป์ ์ ์๋ค. ์ด์ ์ฐ๋ฆฐ ์ธํฐํ์ด์ค์ ์์ฑํ ํจ์๋ง ํธ์ถํ๋ฉด ๋๋ค.
val userListCall = networkService.doGetUserList("1") // ๊ตฌํ์ฒด์ ํจ์๋ฅผ ํธ์ถํด์ Call ๊ฐ์ฒด ์์ฑ
// Call ๊ฐ์ฒด.enqueue ํ๋ ์๊ฐ ๋คํธ์ํน์ด ๋๋ค
// ๋งค๊ฐ๋ณ์์ ์ฝ๋ฐฑ์ ์ ๋ฌ
userListCall.enqueue(object : Callback<UserListModel> {
// ๋คํธ์ํน์ด ์ฑ๊ณตํด ๋ฐ์ดํฐ๋ฅผ ์ ์์ ์ผ๋ก ๋ฐ์ ์๊ฐ ์๋ ์ฝ
// ๋งค๊ฐ๋ณ์ response ๋ก ์๋ฒ ๋ฐ์ดํฐ๊ฐ ๋์ด์จ๋ค
override fun onResponse(call: Call<UserListModel>, response: Response<UserListModel>) {
val userList = response.body()
}
// ๋คํธ์ํน์ด ์คํจํ์ ๊ฒฝ์ฐ ์๋ ์ฝ
// ์ค๋ฅ๊ฐ ๋ฌ๋ ๊ฐ์ฒด๋ ๋งค๊ฐ๋ณ์ t ๋ก ์ ๋ฌ์ ํ๊ธด ํ๋ค
override fun onFailure(call: Call<UserListModel>, t: Throwable) {
call.cancel()
}
})
๋ฆฌ์คํธ๋ทฐ์ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ์์ ๊ฐ์ ธ์ค์!
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
<uses-permission android:name="android.permission.INTERNET" />
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
ํ๋์ ์ ์ ์ ๋ณด๋ฅผ ๋ด๋ DTO ํด๋์ค
package com.kotdev99.android.c87
data class UserModel(
@SerializedName("first_name")
var firstName: String,
@SerializedName("last_name")
var lastName: String
)
JSON ์์ ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ VO ํด๋์ค
package com.kotdev99.android.c87
data class UserListModel(
var data: List<UserModel>?
// total ํ์ด์ง ์ ๋ฑ์ ์ ๋ณด๋ฅผ ๋ฐ์ ์ ์๋ค
)
๋คํธ์ํน ์ ํธ์ถํ ํจ์๋ฅผ ๊ฐ๋ ์ธํฐํ์ด์ค
package com.kotdev99.android.c87
interface INetworkService {
@GET("api/users")
fun doGetUserList(@Query("page") page: String): Call<UserListModel>
}
package com.kotdev99.android.c87
class MainActivity : AppCompatActivity() {
// Retrofit ์ด๊ธฐํ
private val retrofit: Retrofit
get() = Retrofit.Builder()
.baseUrl("https://reqres.in/")
.addConverterFactory(GsonConverterFactory.create())
.build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listView = findViewById<ListView>(R.id.listView)
// Retrofit ์ ์ธํฐํ์ด์ค ๋ฑ๋ก
var networkService = retrofit.create(INetworkService::class.java)
// Call ๊ฐ์ฒด ํ๋
val call = networkService.doGetUserList("1")
// ์ฝ๋ฐฑ์ผ๋ก ๊ฒฐ๊ณผ๊ฐ์ ๋ฐ๋๋ค
call.enqueue(object : Callback<UserListModel> {
override fun onResponse(call: Call<UserListModel>, response: Response<UserListModel>) {
val userList = response.body()
val mutableList = mutableListOf<Map<String, String>>()
userList?.data?.forEach {
val map = mapOf("firstName" to it.firstName, "lastName" to it.lastName)
mutableList.add(map)
}
val adapter = SimpleAdapter(
this@MainActivity,
mutableList,
android.R.layout.simple_expandable_list_item_2,
arrayOf("firstName", "lastName"),
intArrayOf(android.R.id.text1, android.R.id.text2)
)
listView.adapter = adapter
}
override fun onFailure(call: Call<UserListModel>, t: Throwable) {
call.cancel()
}
})
}
}
์๋ฒ์์ JSON ํน์ XML ๋งํผ ๋ง์ด ๋์ด์ค๋ ๊ฒ์ด ์ด๋ฏธ์ง์ด๋ค.
์ด๋ฏธ์ง๋ ๋ฐ์ดํธ ๋ฐ์ดํฐ์ด๋ค. ๊ทธ๋์ ํํ ์ด๋ฏธ์ง ๋ค์ด๋ก๋๋ผ๊ณ ์ด์ผ๊ธฐ ํ๋ค.
Java ์ ์ฝ์ด API ๋ก ๊ฐ๋ฅํ๋ค. Retrofit ์ผ๋ก๋ ๊ฐ๋ฅํ๋ค. ํ์ง๋ง ์ด๋ฏธ์ง๋ง์ ์ํ ์ ๋ฌธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์กด์ฌํ๋ค. ๋ง์ฝ ์ด๋ฏธ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด ์ํฉ์ ๋ฐ๋ฅธ ์ฒ๋ฆฌ๋ฅผ ์ผ์ผ์ด ์์ฑํด ์ฃผ์ด์ผ ํ๋ค.
์ด๋ฏธ์ง๊ฐ ํ์ผ or ๋ฆฌ์์ค or ๋ค์ด๋ก๋?
์ฌ์ด์ฆ๋ก ์ธํ OOM.
์ด๋ฏธ์ง ๋ก๋ฉ ์๊ฐ์ด ๊ธธ ๊ฒฝ์ฐ placeholder, ๊ทธ๋ฆฌ๊ณ ๋ก๋ฉ์ด ๋๋ฌ์ ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ.
์ด๋ฏธ์ง ๋ก๋ฉ ์คํจ ์ ์๋ฌ ์ฒ๋ฆฌ.
etc...
๊ทธ๋ฅ ์ด๋ฏธ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉํ์.
implementation ("com.github.bumptech.glide:glide:4.16.0")
๊ตฌ๊ธ์์ ๋ง๋ ์ด๋ฏธ์ง ํธ๋ค๋ง์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋ฆฌ์์ค ์ด๋ฏธ์ง, ํ์ผ ์ด๋ฏธ์ง, ๋คํธ์ํฌ ์ด๋ฏธ์ง ํ๋
์ด๋ฏธ์ง์ ํฌ๊ธฐ ์กฐ์
๋ก๋ฉ ์ด๋ฏธ์ง ํ์
์๋ฌ ์ด๋ฏธ์ง ํ์
// ์ด๋ฏธ์ง๋ฅผ ImageView ์ ์ถ๋ ฅ
Glide.with(this)
.load(R.drawable.seoul) // ๋ฆฌ์์ค ์ด๋ฏธ์ง
.into(resultView) // ์ด๋ฏธ์ง๋ฅผ ์ถ๋ ฅํ๋ View
Glide.with(this)
.load(url) // ๋คํธ์ํฌ ์ด๋ฏธ์ง
.into(resultView) // ์ด๋ฏธ์ง๋ฅผ ์ถ๋ ฅํ๋ View
// File์ ๊ฒฝ์ฐ์๋ .load ์ ํ์ผ ๊ฒฝ๋ก๋ฅผ ์ฃผ๋ฉด ๋๋ค.
// ํน์ ํฌ๊ธฐ๋ก ์ด๋ฏธ์ง ๋ก๋ฉ (OOM)
Glide.with(this)
.load(R.drawable.seoul)
.override(200, 200) // ์ด๋ฏธ์ง ์ฌ์ด์ฆ
.into(resultView)
// ํน์ ํฌ๊ธฐ๋ก ์ด๋ฏธ์ง ๋ก๋ฉ (OOM)
Glide.with(this)
.load(R.drawable.seoul)
.override(200, 200) // ์ด๋ฏธ์ง ์ฌ์ด์ฆ
.into(resultView)
// ๋ก๋ฉ, ์๋ฌ ์ด๋ฏธ์ง ์ถ๋ ฅ
Glide.with(this)
.load(R.drawable.seoul)
.override(200, 200)
.placeholder(R.drawable.loading) // ๋ก๋ฉ ์ด๋ฏธ์ง (์ฃผ๋ก GIF ์ฌ์ฉ)
.error(R.drawable.error) // ์๋ฌ ์ด๋ฏธ์ง
.into(resultView)
implementation ("com.github.bumptech.glide:glide:4.16.0")
<uses-permission android:name="android.permission.INTERNET" />
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/resultView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.kotdev99.android.c88
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView = findViewById<ImageView>(R.id.resultView)
Glide.with(this)
.load("https://www.google.co.kr/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png")
.override(200, 200)
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.into(imageView)
}
}