이번 포스팅은 Kotlin + RxJava2 + Retrofit2 + OkHttp3 를 이용하여 HTTP 웹 통신을 하는데, Json 결과로 주고 받는 내용입니다.
예제는 Github의 Contributor 를 불러올 수 있는 API로 구성되어 있습니다.
완성된 예제는 https://github.com/ldhcjs/KotlinOKHttpRetrofitRxJava 에서 보실 수 있습니다.
바로 본론부터 들어갑시다.
App 모듈의 build.gradle 은 아래와 같은 라이브러리가 추가되어야 합니다.
2019년 8월 기준 가장 최신 라이브러리로 구성되었습니다.
가끔 호환성 에러가 날 수 있는데 그럴 경우 이번 버전의 라이브러리로 교체해야 할 수도 있습니다.
// App 모듈의 build.gradle
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 기본 Kotlin 확장
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
// OkHttp3
implementation 'com.squareup.okhttp3:okhttp:4.0.1'
implementation 'com.squareup.okhttp3:logging-interceptor:4.0.1'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.0.1'
// Retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.1'
// Json 파싱을 위한 Gson
implementation 'com.google.code.gson:gson:2.8.5'
// RxJava2
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.11'
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
// 중략..
}
Android Manifest에는 INTERNET 권한만 주면 되겠죠.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ldhcjs.kotlinokhttpretrofitrxjava">
<!-- 인터넷 권한은 당연히 필수 -->
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
https://api.github.com/repos/[owner]/[repo]/contributors 의 API의 Json 응답은 아래와 같습니다.
응답 형식은 Json Array 이네요.
[
{
"login": "ldhcjs",
"id": 29829392,
"node_id": "QPS6VXTlcjE3MSE0NZP0",
"avatar_url": "https://avatars2.githubusercontent.com/u/17514504?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/ldhcjs",
"html_url": "https://github.com/ldhcjs",
"followers_url": "https://api.github.com/users/ldhcjs/followers",
"following_url": "https://api.github.com/users/ldhcjs/following{/other_user}",
"gists_url": "https://api.github.com/users/ldhcjs/gists{/gist_id}",
/* 중략.. */
}
]
이 응답을 가지고 Contributors POJO 클래스를 만들어 줍니다.
SerializeName은 Json의 element와 매칭시켜주겠다는 것이고, Expose는 element의 값이 NULL일 경우 생략하겠다는 소리입니다.
// 너무 길어서 코드는 중략했습니다. Github의 전체 소스 참고하세요.
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
class Contributors {
@SerializedName("login")
@Expose
private var login: String? = null
@SerializedName("id")
@Expose
private var id: Int? = null
@SerializedName("node_id")
@Expose
private var nodeId: String? = null
/* 중략... */
fun getLogin(): String? {
return login
}
fun setLogin(login: String) {
this.login = login
}
fun getId(): Int? {
return id
}
fun setId(id: Int?) {
this.id = id
}
fun getNodeId(): String? {
return nodeId
}
fun setNodeId(nodeId: String) {
this.nodeId = nodeId
}
/* 중략... */
}
Retrofit 인터페이스를 만들어줍니다.
@Path는 {파라미터} 에 값을 넣을 수 있게 해줍니다.
아래 예제에서는 {owner} 에 owner로 들어오는 값을 넣을 수 있게 해주게 되죠.
Github로 보면 User id 가 owner 의 값으로 들어가게 됩니다.
응답은 받아서 파싱해서 보여주고 끝이기 때문에 Single로 받았습니다.
또한 Json Array 형식이기 때문에 제네릭 타입은 Array<Contributors>로 했네요, ArrayList<Contributors>로 해도 됩니다.
import retrofit2.http.GET
import io.reactivex.Single
import retrofit2.http.Path
interface KotlinRetrofitInterface {
@GET("repos/{owner}/{repo}/contributors")
fun requestContributors(
@Path("owner") owner:String,
@Path("repo") repo:String
) : Single<Array<Contributors>>
}
이제 OkHttp Manager 클래스를 하나 만들어 줍니다.
필요하다면 SSL인증서 무시하는 기능을 추가해야 할 수도 있습니다. 여기서는 필요가 없어서 넣지는 않았습니다.
object 클래스로 선언한 이유는 OkHttp 인스턴스를 한번만 생성해서 계속 사용하는 경우가 많기 때문입니다.
음 마치 static 변수같은 느낌이죠.
object KotlinOKHttpRetrofitRxJavaManager {
val CONNECT_TIMEOUT: Long = 15
val WRITE_TIMEOUT: Long = 15
val READ_TIMEOUT: Long = 15
val API_URL: String = "https://api.github.com/"
var mOKHttpClient: OkHttpClient
var mRetrofit: Retrofit
var mKotlinRetrofitInterface: KotlinRetrofitInterface
init {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
mOKHttpClient = OkHttpClient().newBuilder().apply {
addInterceptor(httpLoggingInterceptor)
connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
}.build()
mRetrofit = Retrofit.Builder().apply {
baseUrl(API_URL)
client(mOKHttpClient)
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
addConverterFactory(GsonConverterFactory.create())
}.build()
mKotlinRetrofitInterface = mRetrofit.create()
}
fun getInstance(): KotlinRetrofitInterface {
return mKotlinRetrofitInterface
}
}
그리고 이제 마지막 MainActivity 네요.
사실 이 예제에서는 따로 UI가 없고, 앱이 실행하자마자 응답이 제대로 넘어오면 Good이라는 토스트 팝업을 띄우고,
에러가 발생하면 doOnError 와 같은 내용의 토스트 팝업을 띄우는 예제입니다.
class MainActivity : AppCompatActivity() {
val TAG: String = "MainActivity"
@SuppressLint("CheckResult")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = KotlinOKHttpRetrofitRxJavaManager.getInstance()
// "ldhcjs", "GetPackagesName" 에는 각자의 Github id와 Repository를 넣으셔도 됩니다.
adapter.requestContributors("ldhcjs", "GetPackagesName")
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnError {
Toast.makeText(this, "doOnError", Toast.LENGTH_SHORT).show()
Log.d(TAG, "doOnError")
}
.unsubscribeOn(Schedulers.io())
.onErrorReturn { t: Throwable ->
Log.d(TAG, "onErrorReturn : " + t.message)
arrayOf(Contributors())
}
.subscribe { result ->
if ("User" == result[0].getType()) {
Toast.makeText(this, "Good", Toast.LENGTH_SHORT).show()
Log.d(TAG, "subscribe good")
} else {
Log.d(TAG, "subscribe bad")
}
}
}
}
대략 이런식으로 코틀린, Rx자바, 레트로핏, OkHttp를 연동시켜서 HTTP 통신을 할 수 있습니다.
도움이 되셨다면 하트 한번 눌러주세요~! 그냥 누르실 수 있습니다.
'IT > Android' 카테고리의 다른 글
[Android] FragmentPagerAdapter와 FragmentStatePagerAdapter 차이 (1) | 2019.08.27 |
---|---|
[Kotlin] 코틀린에서 !! 연산자의 의미 (0) | 2019.08.16 |
[Android] Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $ 에러 (0) | 2019.08.09 |
[Kotlin] 코틀린에서 변수 타입 ? 는 무엇을 의미 하는가 (0) | 2019.08.01 |
[Kotlin] Not enough information to infer type variable T 에러 (1) | 2019.07.31 |