우찬쓰 개발블로그

안드로이드 retrofit2 + rxjava2 로 파일 다운로드 progress 표시하기 본문

안드로이드/안드로이드 개발

안드로이드 retrofit2 + rxjava2 로 파일 다운로드 progress 표시하기

이우찬 2019. 5. 22. 18:45
반응형

큰 파일을 다운로드하여 안드로이드 저장소에 넣어야 하는 경우, 유저의 네트워크 상황에따라 오래 걸리는 경우가 있을 수 있다.

 

이런 경우를 대비하여 유저 편의성을 위해 progress bar를 표시해주어야 하는데, 직접 하나하나 구현해 보도록 하자.

 

 

1. 먼저 간단하게 Retrofit Service 클래스를 만든다.

 

interface RetrofitService {

    /**
     * 파일 요청.
     */
    @GET("테스트 파일이 있는 url path")
    fun requestFile(): Flowable<ResponseBody>
}

 

가볍게 하기위해 API 요청시 필요한 데이터는 없다고 가정한다.

 

 

 

2. 이 파일을 내려받는 과정에서의 진행도를 노티받기 위하여 ResponseBody를 상속받은 클래스를 하나 만들어야한다.

 

class ProgressResponseBody(val responseBody: ResponseBody, val onAttachmentDownloadUpdate: (Int) -> Unit) : ResponseBody() {

    private var bufferedSource = Okio.buffer(source(responseBody.source()))

    override fun contentLength(): Long {
        return responseBody.contentLength()
    }

    override fun contentType(): MediaType? {
        return responseBody.contentType()
    }

    override fun source(): BufferedSource {
        return bufferedSource
    }

    private fun source(source: Source): Source {
        return object : ForwardingSource(source) {
            var totalBytesRead = 0L
            override fun read(sink: Buffer, byteCount: Long): Long {
                val bytesRead = super.read(sink, byteCount)

                totalBytesRead += if (bytesRead != -1L) bytesRead else 0

                val percent = if (bytesRead == -1L) 100f else totalBytesRead.toFloat() / responseBody.contentLength().toFloat() * 100

                onAttachmentDownloadUpdate(percent.toInt())

                return bytesRead
            }
        }
    }
}

 

핵심은 source(source: Source) 부분으로, 내려받은 bytes 상태를 퍼센트로 변환하여 0~100의 수치로 콜백해 주는 역할을 한다.

 

 

 

3. 그다음 OkHttpClient를 만들어 주어야 한다.

 

private fun createOkHttpProgressClient(onAttachmentDownloadUpdate: (Int) -> Unit): OkHttpClient {
    if (BuildConfig.DEBUG) {
        val builder = OkHttpClient.Builder()
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        builder.addInterceptor(interceptor)
        builder.addInterceptor { chain ->
            val originalResponse = chain.proceed(chain.request())
            originalResponse.newBuilder()
                .body(ProgressResponseBody(originalResponse.body(), onAttachmentDownloadUpdate))
                .build()
        }
        return builder.build()
    } else {
        val builder = OkHttpClient.Builder()
        return builder.build()
    }
}

 

OkHttp에서 인터셉트를 함으로써 ProgressResponseBody의 로직을 거쳐 파일다운로드 진행상황을 받아올 수 있는 것임을 알 수 있다.

 

 

 

4. 네트워크 요청 로직을 완료한다.

 

private fun buildingRetrofit(onAttachmentDownloadUpdate: (Int) -> Unit){

    val disposable = Retrofit.Builder()
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .baseUrl("https://www.서버 주소.kr/")
        .client(createOkHttpProgressClient(onAttachmentDownloadUpdate))
        .build()
        .create(RetrofitService::class.java)
        .requestFile()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({
            //TODO: 네트워크 완료 후 처리
        }, {
            //TODO: 네트워크 에러 처리
        })
    compositeDisposable.add(disposable)
}

 

 

실제 서비스 구현시에는, 좀더 세분화해서 처리하지만 포스팅을 위해 간략히 기재하였다.

 

이렇게 구현하면 buildingRetrofit을 호출할때 람다로 깔끔하게 UI 업데이트를 할 수 있다.

 

buildingRetrofit {
    runOnUiThread {
        progressBar.progress = it
    }
}

 

반응형
Comments