IT/Android

[Android] Kotlin + RxJava + Retrofit + OkHttp 로 http 통신하기

토마토조아 2019. 8. 12. 09:55
728x90

이번 포스팅은 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 통신을 할 수 있습니다.

도움이 되셨다면 하트 한번 눌러주세요~! 그냥 누르실 수 있습니다.

728x90