시연영상
https://www.youtube.com/watch?v=PTlpwTyFiak&feature=youtu.be
필요한 이유
현재 만들고 있는 안드로이드 앱 프로젝트에서 로그인 Api가 요청에 대한 응답을 만들어 내는데 긴 시간이 걸린다.
그래서 기존에 쓰던 방법으로 레트로핏을 사용했더니 응답을 받기전에 다음 코드를 실행해 버리는 문제가 발생했다.
따라서 응답이 올때까지 기다리게 하고 다음 코드를 실행 할 수 있도록 수정해야 할 필요성이 있다.
그래서 예시 프로젝트를 만들어 봤다.
시연영상을 만들기 위한 코드를 살펴보자
소스코드
https://github.com/okanekudasai/retrofit_sync
GitHub - okanekudasai/retrofit_sync
Contribute to okanekudasai/retrofit_sync development by creating an account on GitHub.
github.com
만든 방법
자잘한 설정들
1. manifest에 추가할 것
<uses-permission android:name="android.permission.INTERNET" />
http 요청을 하기 위한것
android:usesCleartextTraffic="true"
https로 요청을 안보내도 오류가 안보내게 하는 코드
Manifast.xml의 전체코드는 아래와 같습니다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Example_response"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2. build.gradle에 설정할 것
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.example_response'
compileSdk 33
-- 뷰 바인딩을 위한것 --
viewBinding {
enabled = true
}
defaultConfig {
applicationId "com.example.example_response"
minSdk 24
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
-- 레트로핏과 코루틴을 사용하기 위한 것 --
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.5.2"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
}
MainActivity
1. api 인스턴스를 만들고 GlobalScope를 관리해 주자
package com.example.example_response
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.example.example_response.databinding.ActivityMainBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import java.util.concurrent.TimeUnit
// Api를 만들기 위한 interface
interface Api {
@GET("long_response_api/{time}")
suspend fun waitResponse(@Path("time") time: String): Response<String>
}
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
val okHttpClient = OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS) // 읽기 Timeout 설정 (초 단위)
.connectTimeout(60, TimeUnit.SECONDS) // 연결 Timeout 설정 (초 단위)
.build()
val api: Api = Retrofit.Builder()
.baseUrl("http://10.0.2.2:8080/")
.addConverterFactory(ScalarsConverterFactory.create())
.client(okHttpClient)
.build()
.create(Api::class.java)
binding.button.setOnClickListener {
Log.d("Okane", "버튼이 눌림")
// 이 스코프 안에 있는 것은 하나의 쓰레드안에서 수행되기 때문에 코드가 순차적으로 수행된다.
GlobalScope.launch(Dispatchers.IO) {
var t = binding.editText.text.toString()
Log.d("Okane", t)
val response = api.waitResponse(t)
// if(response.isSuccessful) {
// Log.d("Okane", response.body()!!)
// }
Log.d("Okane", "여긴 response를 받아오고 수행됨")
}
Log.d("Okane", "여긴 reponse를 받기전에 수행됨")
}
}
}
GlobalScope.launch(Dispatchers.IO)은 Kotlin에서 kotlinx.coroutines 라이브러리를 사용하여 글로벌 스코프에서 IO 디스패처를 사용하여 코루틴을 시작하는 코드다
- GlobalScope: 글로벌 스코프는 애플리케이션 수명 주기와 관련이 있으며 애플리케이션이 실행되는 동안 계속해서 코루틴을 관리하는 스코프다. 글로벌 스코프에서 실행되는 코루틴은 애플리케이션 전체에서 접근 가능
- launch(Dispatchers.IO): launch 함수는 비동기적으로 코드 블록을 실행하는 새로운 코루틴을 시작한다. Dispatchers.IO는 입출력 작업에 최적화된 디스패처로, 파일 입출력, 네트워크 호출과 같은 블록되는 작업들을 효과적으로 처리할 수 있도록 최적화되어 있다.
따라서, GlobalScope.launch(Dispatchers.IO)은 글로벌 스코프에서 IO 디스패처를 사용하여 비동기적으로 코드를 실행하는 새로운 코루틴을 시작하는 것을 의미한다. 이를 통해 입출력 작업을 블록하지 않고 비동기적으로 처리할 수 있다