안드로이드 Http & Retrofit

이영준·2023년 4월 25일
0

📌 HTTP Recap

기본적으로 요청 응답 구조로 구성된다.
HTTP Request -> HTTP Response
HTTP Stateless : HTTP는 state를 저장하지 않는다. 요청/응답끼리 연결되어 있는 것이 아니라 독립적으로 요청/ 응답한다. 여러 요청 응답의 진행과정이나 데이터가 필요할 때는 쿠키나 세션을 사용한다.

🔑 Rest API

Representational State Transfer
기존기술과 HTTP를 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍쳐
네트워크 상에서 클라이언트와 서버 사이의 통신 방식 중 하나

  • GET, POST, PUT, PATCH, DELETE
    / 구분자로 계층관계를 나타내면 Is-a 관계가 아닌 Has-a 관계이다.

    GET : example.com/animals/mammals/whales

📌 Retrofit

Square에서 만든 HTTP 라이브러리.
안드로이드에서 RESTful한 통신을 할 수 있도록도와줌.
기존의 httpConnection을 통한 방법보다 호출이나 비동기 처리 이용이 쉬움

네트워크 request 처리 위해 OkHttp를 사용하고,
gson, jackson, moshi 등의 json->자바 변환 라이브러리를 사용한다.

📌 Retrofit 사용 (without 코루틴)

  • internet 퍼미션 허용
<uses-permission android:name="android.permission.INTERNET" />
  • retrofit, gson 라이브러리 추가
    // 레트로핏
    // https://github.com/square/retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'

    // https://github.com/square/retrofit/tree/master/retrofit-converters/gson
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
  • Application Class에서 Retrofit 인스턴스 생성

Retrofit은 앱이 실행될 때 한번만 호출되면 된다.
따라서 Application 클래스를 만들어 이 안에 구현한다.

// 앱이 실행될 때 1번만 실행이 됨
class ApplicationClass : Application() {

    val WEATHER_URL = "https://api.openweathermap.org/data/2.5/"

    companion object {

        // 전역변수 문법을 통해 Retrofit 인스턴스를 앱 실행 시 1번만 생성하여 사용 (싱글톤 객체)
        lateinit var wRetrofit : Retrofit
    }

    override fun onCreate() {
        super.onCreate()

        // 앱이 처음 생성되는 순간, retrofit 인스턴스를 생성
        wRetrofit = Retrofit.Builder()
                .baseUrl(WEATHER_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()

    }
}
<application
        android:name="com.example.network_2.ApplicationClass"

기존의 manifest에도 application의 name을 내가 만든 application class 명으로 바꿔준다.

  • Retrofit 사용
    • 호출할 api 인터페이스 구현
    • create사용하여 Retrofit에서 구현부를 생성
    • enque로 비동기 호출하고 적절한 callback method 구현. (동기호출 = execute)
    • 성공시 onResponse() - 400,500번대도 포함하여, 응답이 오면 이 method 불림
    • 실패 : onFailure() http 통신 오류 등에 호출


  • API 구현
interface WeatherInterface {

    @GET("weather")
    fun getWeather(
        @Query("q") q: String,
        @Query("appid") appid: String
    ) : Call<WeatherResponse>
}

  • enque로 비동기 호출
    Retrofit 가져와 Call 하기
    fun getWeatherData(city : String, key : String) {
        val weatherInterface = ApplicationClass.wRetrofit.create(WeatherInterface::class.java)
        weatherInterface.getWeather(city, key).enqueue(object: Callback<WeatherResponse>{
            override fun onResponse(
                call: retrofit2.Call<WeatherResponse>,
                response: Response<WeatherResponse>
            ) {
                if(response.isSuccessful){
                    Log.d(TAG, "onResponse: ${response.body()}")
//                    c= (℉ − 32) × 5/9
                    binding.tempTv.text = response.body()?.main?.temp.toString()
//                    binding.tempTv.text = ((response.body()?.main?.temp?.minus(32))?.times((5/9))).toString()
                    binding.textView.text = response.body().toString()

                }
            }

            override fun onFailure(call: retrofit2.Call<WeatherResponse>, t: Throwable) {
                Log.d(TAG, "onFailure: 통신오료 : ${t.message}")
            }

        })
        
    }

위 방식은 enque방식으로 비동기를 호출한다. 따라서 callback을 오버라이딩 해주어야 한다.

📌 코루틴 사용하여 retrofit 사용

suspend 함수로 API서비스를 바꾸고

interface WeatherInterface {
    @GET("weather")
    suspend fun getWeather(
        @Query("q") q: String,
        @Query("appid") appid: String
    ) : Response<WeatherResponse>
    // : WeatherResponse 응답 필요없고 isSuccessful 호출 안하려면
}

아래와 같이 수정해준다.

    fun getWeatherData(city: String, key: String) {
        val weatherInterface = ApplicationClass.wRetrofit.create(WeatherInterface::class.java)

        CoroutineScope(Dispatchers.IO).launch {
            val response = weatherInterface.getWeather(city, key)

            if (response.isSuccessful) {
                Log.d(TAG, "onResponse: ${response.body()}")
                val temp = response.body()?.main?.temp

                withContext(Dispatchers.Main){
                    binding.tempTv.text = temp.toString()
                    binding.textView.text = response.body().toString()
                }
                

//                CoroutineScope(Dispatchers.Main).launch{
//                    binding.tempTv.text = temp.toString()
//                    binding.textView.text = response.body().toString()
//                }

            }
        }
    }

코루틴으로 Dispatcher IO 영역에서 네트워크 통신을 하고, UI를 바꾸는 작업이 있으면 MAIN에서 작업을 해줘야 한다.

profile
컴퓨터와 교육 그사이 어딘가

0개의 댓글