[Android] OKHttp3 + JSONObject vs Retrofit + GSON

이제일·2022년 8월 15일
0

Android

목록 보기
7/15
post-thumbnail

공공데이터를 대량으로 저장해야하는 경우가 생겼다.
API 형식이 다양하여 body값을 JSONObject로 바꿔 사용하려 했는데 객체로 변환하는데까지도 너무 오래(약 10초)걸렸다.

그래서 Retrofit과 GSON을 사용하는 경우와 기존에 했던 OkHttp와 JSONObject를 사용하는 경우를 나눠 시간을 측정해봤다.

아래는 현재 인터넷 속도이고, 측정할 디바이스는 AVD로 블루스택, cpu와 메모리, 성능 모두 낮음으로 설정이 되어있다.

OKHttp3

실행 클래스


            var start = System.currentTimeMillis()
            MndAPI().divideData(N)
            var end = System.currentTimeMillis()
            var current = end-start
            Log.d("TAG 총 걸린 시간(ms)", current.toString())

API 클래스

	suspend fun divideData(div:Int) = coroutineScope {
        for(i in 0 until div) {
            launch {
                getData(65535/div*i, 65535/div*(i+1))
            }
        }
    }
    fun getData(startIndex:Int,endIndex:Int){
        var start = System.currentTimeMillis()
        val request = Request.Builder()
            .url( Mnd.BASE_URL + Mnd.TYPE.CEMETERY_1 + "/$startIndex/$endIndex")
            .build()


        val response = Mnd.request.newCall(request).execute()

        var end = System.currentTimeMillis()
        var current = end-start
        Log.d("TAG api 응답 시간(ms)",current.toString())


        start = System.currentTimeMillis()
        val json = JSONObject(response.body!!.string())
        end = System.currentTimeMillis()
        current = end-start
        Log.d("TAG json parse 시간(ms) ", current.toString())

        start = System.currentTimeMillis()

        val row = json.getJSONObject(Mnd.TYPE.CEMETERY_1).getJSONArray("row")

        val data = Array(row.length()){
                Cemetery(row.getJSONObject(it))
        }

        end = System.currentTimeMillis()
        current = end-start
        Log.d("TAG 객체 생성 시간(ms) ", current.toString())
    }

데이터 클래스

data class Cemetery(
    val name:String,
    val rank:String, // 계급
    val identity:String, // 신분
    val moveDate:String, // 안장 일자
    val movePlc:String, // 안장 위치
    val deathDate:String, // 사망 일자
    val deathPlc:String, // 사망 장소
    @PrimaryKey(autoGenerate = true)
    val id:Int = 0
    ){
    constructor(jsonObject: JSONObject): this(
        jsonObject.getString("stmt"),
        jsonObject.getString("rank"),
        jsonObject.getString("mildsc"),
        jsonObject.getString("buraldate"),
        jsonObject.getString("buralpstn"),
        jsonObject.getString("dthdt"),
        jsonObject.getString("deathplc"),
    )
}

한 번에 65535개의 json 데이터 가져오기 및 처리
Api의 응답시간은 약 3초, json객체를 파싱하는게 약 6초로 총 걸린 시간은 약 9초이다.

해당 실행문을 병렬처리로 3개로 나누어 실행했다.
Api 응답시간은 약 1.8초, json객체를 파싱하는게 약 4초로 총 걸린 시간은 약 6초로 약간 속도가 향상되었다.

병렬처리를 10개로 나누어 실행했다.
3개를 동시실행 했을 경우와 비슷한 속도가 나왔다.

이를 30개로 나누어 했을때 또한 비슷하다.

Retrofit

실행 클래스

  CoroutineScope(Dispatchers.IO).launch {
            var start = System.currentTimeMillis() // start = 시작시간 - 1970년

            divideData(N)

            var end = System.currentTimeMillis() // end = 종료시간 - 1970년
            var current = end-start
            Log.d("TAG 총 걸린 시간(ms)", current.toString())
}



    suspend fun divideData(div:Int) = coroutineScope {

        for(i in 0 until div) {
            launch {
                MndAPI.request.getData(65535/div*i, 65535/div*(i+1))
            }
        }
    }

api 클래스


object MndAPI {

    private val retrofit: Retrofit by lazy {
        Retrofit.Builder()
            .baseUrl(Mnd.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    val request: Request by lazy {
        retrofit.create(Request::class.java)
    }
}


interface Request {
    @GET("${Mnd.TYPE.CEMETERY_1}/{startIndex}/{endIndex}")
    suspend fun getData(@Path("startIndex") startIndex: Int,@Path("endIndex") endIndex: Int): ResponseData
}

데이터 클래스


data class ResponseData(
    @SerializedName(Mnd.TYPE.CEMETERY_1)
    val type:ResponseInData
)
data class ResponseInData(
    @SerializedName("list_total_count")
    val count:Int,
    @SerializedName("row")
    val row:List<Cemetery>
)

data class Cemetery(
    @SerializedName("stmt")
    val name:String,
    @SerializedName("rank")
    val rank:String, // 계급
    @SerializedName("mildsc")
    val identity:String, // 신분
    @SerializedName("buraldate")
    val moveDate:String, // 안장 일자
    @SerializedName("buralpstn")
    val movePlc:String, // 안장 위치
    @SerializedName("dthdt")
    val deathDate:String, // 사망 일자
    @SerializedName("deathplc")
    val deathPlc:String, // 사망 장소
    @PrimaryKey(autoGenerate = true)
    val id:Int = 0
    )

Retrofit이 객체를 구체화 하기에 내부 속도는 측정하기 어려워서 총 걸린 시간만 체크하였다.

한번에 했을경우 약 5초 후반이 나왔고

3개 병렬은 약 3초 후반

10개 또한 3초후반

30개로 했을 경우 4초 중반으로 속도가 약간 줄었다.

나름의 결론

JSONObject로 String을 객체로 바꾸는 시간이 오래걸리는데 GSON의 경우 해당 작업이 매우 빠르므로 위의 두 결과가 차이났던 것 같다.

병렬 실행의 경우 전체 데이터를 받아오는 것보다 응답시간을 줄여주어 적당한 크기로 나누어 실행해주는 것이 좋겠다.

profile
세상 제일 이제일

0개의 댓글