Retrofit2는 안드로이드 REST API 통신 라이브러리입니다. 통신 라이브러리중 Volley와 함께 가장 많이 사용되는 라이브러리입니다.
Retrofit을 사용한 이유는 성능과 간단한 구현, Type-Safe때문입니다.
소개는 여기까지하고 바로 구현으로 들어가겠습니다.
Retrofit을 사용하려면 세 가지 클래스가 필요합니다.
Retrofit, Picasso, Gson의 의존성을 추가합니다.
dependencies{
//의존성 추가
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
}
Manifest에서 Internet도 허용해줍니다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.retrofittest">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.RetrofitTest">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
가져올 JSON은 https://jsonplaceholder.typicode.com/photos 입니다.
[
{
"albumId": 1,
"id": 1,
"title": "accusamus beatae ad facilis cum similique qui sunt",
"url": "https://via.placeholder.com/600/92c952",
"thumbnailUrl": "https://via.placeholder.com/150/92c952"
},
{
"albumId": 1,
"id": 2,
"title": "reprehenderit est deserunt velit ipsam",
"url": "https://via.placeholder.com/600/771796",
"thumbnailUrl": "https://via.placeholder.com/150/771796"
}
...
요청을 위해 dataClass를 model패키지에 정의합니다.
package com.example.retrofittest.model
import com.google.gson.annotations.SerializedName
//데이터 통신을 위한 모델클래스 생성
data class RetroPhoto(
@SerializedName("albumId") var albumId: Int,
@SerializedName("id") var id: Long,
@SerializedName("title") var title: String,
@SerializedName("url") var url: String,
@SerializedName("thumbnailUrl") var thumbnailUrl: String,
){
}
REST API에 요청하기위해 Retrofit.Builder클래스와 baseUrl을 정의한 인스턴스를 사용합니다.
package com.example.retrofittest.network
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.*
//레트로핏 인스턴스 생성
class RetrofitClientInstance {
companion object{
private val BASE_URL: String = "https://jsonplaceholder.typicode.com"
}
fun getRetrofitInstance(): Retrofit{
var retrofit = retrofit2
.Retrofit
.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).build()!!
return retrofit
}
}
service패키지 GetDataService interface를 정의합니다.
package com.example.retrofittest.service
import com.example.retrofittest.model.RetroPhoto
import retrofit2.Call
import retrofit2.http.GET
//엔드포인트 설정
interface GetDataService {
@GET("/photos")
fun getAllPhoto(): Call<List<RetroPhoto>>
}
RecyclerView에 뿌려주기위해 adapter패키지에 CustomAdapter를 정의합니다.
package com.example.retrofittest.adapter
import android.content.Context
import android.text.Layout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.retrofittest.R
import com.example.retrofittest.model.RetroPhoto
import com.jakewharton.picasso.OkHttp3Downloader
import com.squareup.picasso.Picasso
class CustomAdapter(var context: Context, var dataList: List<RetroPhoto>) : RecyclerView.Adapter<CustomAdapter.CustomViewHolder>() {
//뷰홀더 생성
inner class CustomViewHolder(val itemView: View) : RecyclerView.ViewHolder(itemView){
var txtTitle : TextView= itemView.findViewById(R.id.title)
var coverImage : ImageView = itemView.findViewById(R.id.coverImage)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.custom_row, parent, false)
return CustomViewHolder(view)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.txtTitle.setText(dataList[position].title)
//Picasso라이브러리를 이용하여 ImageView에 간단하게 이미지 로딩
val builder = Picasso.Builder(context)
builder.downloader(OkHttp3Downloader(context))
builder.build().load(dataList[position].thumbnailUrl)
.placeholder((R.drawable.ic_launcher_background))
.error(R.drawable.ic_launcher_background)
.into(holder.coverImage)
}
override fun getItemCount(): Int = dataList.size
}
MainActivity에서 요청, 응답수행후 callBack에서 아이템뷰를 만들어줍니다.
package com.example.retrofittest
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.retrofittest.adapter.CustomAdapter
import com.example.retrofittest.model.RetroPhoto
import com.example.retrofittest.service.GetDataService
import com.example.retrofittest.network.RetrofitClientInstance
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//서비스 호출
val service: GetDataService = RetrofitClientInstance().getRetrofitInstance().create(GetDataService::class.java)
//콜백 정의
val call: Call<List<RetroPhoto>> = service.getAllPhoto()
call.enqueue(object: Callback<List<RetroPhoto>>{
override fun onResponse(call: Call<List<RetroPhoto>>, response: Response<List<RetroPhoto>>) {
generateDataList(response.body()!!)
}
override fun onFailure(call: Call<List<RetroPhoto>>, t: Throwable) {
Toast.makeText(applicationContext, "SomeThing went wrong...Plase try later!", Toast.LENGTH_SHORT).show()
}
})
}
private fun generateDataList(photoList: List<RetroPhoto>) {
val recycler: RecyclerView = findViewById(R.id.customRecyclerView)
val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(applicationContext)
recycler.layoutManager = layoutManager
recycler.adapter = CustomAdapter(this, photoList)
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/customRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/coverImage"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentTop="true"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/coverImage"
android:layout_marginLeft="16dp"
android:paddingTop="20dp"
android:lines="2"
android:text="title"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
custom_row.xml
위 사진처럼 정상적으로 뷰가 생성된 것을 확인할 수 있습니다.