Android

코틀린 retrofit2 공부

최데브 2020. 11. 16. 12:18

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()
            }

        })

    }


}
반응형