기본적으로 요청 응답 구조로 구성된다.
HTTP Request -> HTTP Response
HTTP Stateless : HTTP는 state를 저장하지 않는다. 요청/응답끼리 연결되어 있는 것이 아니라 독립적으로 요청/ 응답한다. 여러 요청 응답의 진행과정이나 데이터가 필요할 때는 쿠키나 세션을 사용한다.
Representational State Transfer
기존기술과 HTTP를 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍쳐
네트워크 상에서 클라이언트와 서버 사이의 통신 방식 중 하나
GET, POST, PUT, PATCH, DELETE
/ 구분자로 계층관계를 나타내면 Is-a 관계가 아닌 Has-a 관계이다.
GET : example.com/animals/mammals/whales
Square에서 만든 HTTP 라이브러리.
안드로이드에서 RESTful한 통신을 할 수 있도록도와줌.
기존의 httpConnection을 통한 방법보다 호출이나 비동기 처리 이용이 쉬움
네트워크 request 처리 위해 OkHttp를 사용하고,
gson, jackson, moshi 등의 json->자바 변환 라이브러리를 사용한다.
<uses-permission android:name="android.permission.INTERNET" />
// 레트로핏
// 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'
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 명으로 바꿔준다.
interface WeatherInterface {
@GET("weather")
fun getWeather(
@Query("q") q: String,
@Query("appid") appid: String
) : Call<WeatherResponse>
}
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을 오버라이딩 해주어야 한다.
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에서 작업을 해줘야 한다.