Retrofit2
https://square.github.io/retrofit/
- 스퀘어 사의 오픈소스 라이브러리
- A type-safe HTTP client for Android and Java
type-safe = "타입에 안정적이다/컴파일 시 타입체크를 할 수 있어 런타임 전에 타입에 대한 문제를 미리 잡는다/네트워크로 오가는 데이터의 타입을 필요한 형태로 주고받을 수 있도록 보장한다. "
사용계기
- HttpURLConnection이 싫은데 이제 더이상 피할 수 없어서
- 서버 DB를 구축했는데 모처럼 클라이언트에서 써보고 싶어서
- 사실 안드로이드에서 서버DB에 접근할 방법이 없어,,
프로젝트에 적용
1. 목표
클라이언트(안드로이드)에서 서버로 검색어를 request로 주고, 원하는 객체로 response를 받는다.
건강기능식품에 대한 정보를 가진 객체로
① : m_name(건강기능식품 이름)을 주면
② : SupplementVO로 돌려받는다. (type-safe한 특징)
2. 사전 준비
- 당연히 네트워크 사용할 것이니 AndroidManifest.xml에 추가한다.
<uses-permission android:name="android.permission.INTERNET" />
- build.gradle (Module:app)에 다음을 추가한다. Sync한 후 최신이 아니라면 노란줄 그어주니까 버전 숫자를 바꿔주자.
// Gson implementation 'com.google.code.gson:gson:2.8.5' // Retrofit2 implementation 'com.squareup.retrofit2:retrofit:2.7.0' // Retrofit2-Gson converter implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
3. package hierarchy
- 조금 딴 이야기지만 안드로이드 한번 하고 말것도 아니고,, 프로젝트가 매번 커지다보니 코드를 왠만하면 깔끔하게 짜서 반복을 줄이고, 효율적인 프로젝트 디렉토리를 구성하고 싶은 욕심이 커졌다.
- 여러 아키텍쳐도 접하면서 아무래도 패키지를 잘 짜는게 중요함을 알게되었다.
- 이전에는 액티비티 클래스를 그냥 생성대는 대로 두었다면 지금은 모듈별로 나누어 "View"Package 아래에 액티비티를 두고,
- 지금 생성할 RetrofitClient, RetrofitService는 "Network"Package에 둘 예정이다.
4. RetrofitClient
- 싱글톤 패턴 사용, 인스턴스 재생성 방지
import com.google.gson.GsonBuilder
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
private var instance: Retrofit? = null
private val gson = GsonBuilder().setLenient().create()
// 서버 주소
private const val BASE_URL = "http://서버.주소.xx.xx/"
// SingleTon
fun getInstance(): Retrofit {
if (instance == null) {
instance = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
return instance!!
}
}
5. RetrofitService
import com.example.dyno.VO.SupplementVO
import retrofit2.Call
import retrofit2.http.*
// 서버에서 호출할 메서드를 선언하는 인터페이스
// POST 방식으로 데이터를 주고 받을 때 넘기는 변수는 Field라고 해야한다.
interface RetrofitService {
@FormUrlEncoded
@POST("Supplement/List")
fun requestList(
@Field("s_name") s_name: String
) : Call<ArrayList<SupplementVO>>
@FormUrlEncoded
@POST("Supplement/Single")
fun requestSingle(
@Field("s_name") s_name: String
) : Call<SupplementVO>
}
6. RegistSupplementActivity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.dyno.OCR.CameraActivity
import com.example.dyno.View.MyPage.Detail.DetailSupplementActivity
import com.example.dyno.R
import com.example.dyno.Network.RetrofitService
import com.example.dyno.Network.RetrofitClient
import com.example.dyno.VO.SupplementVO
import kotlinx.android.synthetic.main.activity_regist_supplement.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
// process
// 0. 이름으로 검색.
// 1. RDS에 해당 건강기능 식품이 존재하는지 검사
// 2. 1.에서 있다면 바로 등록.
// 3. 1.에서 없다면 OCR 수행.
class RegistSupplementActivity : AppCompatActivity() {
private val TAG = this::class.java.simpleName
private lateinit var retrofit : Retrofit
private lateinit var supplementService : RetrofitService
private lateinit var supplementAdapter : SupplementAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_regist_supplement)
/* (어댑터 달기 등 중략) */
// 서버 연결
initRetrofit()
// 검색 버튼 클릭 시
btn_search.setOnClickListener {
// 검색어
val keyword = input_search.text.toString()
// 연결
if(keyword!="")
getSearchList(supplementService, keyword)
}
}
private fun initRetrofit(){
retrofit = RetrofitClient.getInstance()
supplementService = retrofit.create(RetrofitService::class.java)
}
private fun getSearchList(service : RetrofitService, keyword : String){
// 키워드로 검색
service.requestList(keyword).enqueue(object : Callback<ArrayList<SupplementVO>>{
override fun onFailure(call: Call<ArrayList<SupplementVO>>, t: Throwable) {
Log.d(TAG,"실패 : {$t}")
}
override fun onResponse(call: Call<ArrayList<SupplementVO>>, response: Response<ArrayList<SupplementVO>>) {
Log.d(TAG,"성공^^")
// 결과가 없을 경우
if(response.body()!!.size==0){
yes_result.visibility = View.GONE // 결과 있음 GONE
no_result.visibility = View.VISIBLE // 결과 없음 VISIBLE
}
// 결과가 있을 경우
else {
//Log.d(TAG,"사이즈 : ${response.body()!!.size}, 첫번째 인자 이름 : ${response.body()!![0].m_name}")
supplementAdapter.setNewData(response.body()!!) // 어댑터에 데이터 업데이트
yes_result.visibility = View.VISIBLE // 결과 있음 VISIBLE
no_result.visibility = View.GONE // 결과 없음 GONE
}
}
})
}
private fun getSelectedSingle(service : RetrofitService, keyword : String) {
service.requestSingle(keyword).enqueue(object : Callback<SupplementVO>{
override fun onFailure(call: Call<SupplementVO>, t: Throwable) {
Log.d(TAG,"실패 : {$t}")
}
override fun onResponse(call: Call<SupplementVO>, response: Response<SupplementVO>) {
Log.d(TAG,"성공^^ 22")
Log.d(TAG,"${response.body()!!.m_name},${response.body()!!.m_company}")
// 현재 액티비티 종료하고, 건강기능식품 디테일 액티비티로 넘어감.
val intent = Intent(applicationContext,DetailSupplementActivity::class.java)
intent.putExtra("DATA2",response.body()!!)
startActivity(intent)
finish()
}
})
}
}
반응형
'Android' 카테고리의 다른 글
안드로이드에서의 Parcelable vs Serializable (0) | 2021.01.28 |
---|---|
안드로이드 DI 라이브러리 Koin 에 대해 (0) | 2021.01.24 |
안드로이드 MVVM 패턴 - 개요 (0) | 2021.01.24 |
마법같은 애니메이션 Lottie 에 대해서 (0) | 2021.01.19 |
안드로이드 - service 정리 (0) | 2020.11.14 |
안드로이드 이중스크롤뷰 (0) | 2020.08.24 |
안드로이드 spinner 테두리와 모서리 둥글게 만들기 (0) | 2020.07.30 |
안드로이드의 Context 개념에 대한 개인적인 정리 (0) | 2020.07.14 |