美文网首页我爱编程
retofit + rxjava + kotlin 上传进度回调

retofit + rxjava + kotlin 上传进度回调

作者: Qin0821 | 来源:发表于2018-06-15 18:43 被阅读0次

HttpLoggingInterceptor

拦截器是个好东西, 查看retrofit请求和上传进度回调都靠它。

val loggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { 
            // 打印retrofit日志
            Log.i("RetrofitLog", "retrofitBack = $it")
        })
        // 不要设置成BODY,会导致writeTo调用两次
loggingInterceptor.level = HttpLoggingInterceptor.Level.HEADERS

level 不要设置成Body,会导致下载进度回调执行两次。

val httpClient = OkHttpClient.Builder()
                //                .cache(cache)
                .addInterceptor(loggingInterceptor)
                .connectTimeout(NETWORK_CALL_TIMEOUT.toLong(), TimeUnit.SECONDS)
                .readTimeout(NETWORK_CALL_TIMEOUT.toLong(), TimeUnit.SECONDS)
                .writeTimeout(NETWORK_CALL_TIMEOUT.toLong(), TimeUnit.SECONDS)
                .build()

val retrofit = Retrofit.Builder()
                .baseUrl(BaseUrlConstants.getBaseUrl(BaseUrlConstants.BaseUrlType.API))
                .client(httpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()

继承Callback

abstract class RetrofitCallback: Callback {

    override fun onResponse(call: Call, response: Response) {
        if (response.isSuccessful) {
            onSuccess(call, response)
        } else {
            onFailure(call, Throwable(response.message()) as IOException?)
        }
    }

    abstract fun onSuccess(call: Call, response: Response)

    abstract fun onLoading(total: Long, progress: Long)

}

主要是为了增加onLoading的回调

继承RequestBody

class FileRequestBody(
    private val requestBody: RequestBody,
    private val callback: RetrofitCallback
) : RequestBody() {
    /**
     * 包装完成的BufferedSink
     */
    private var bufferedSink: BufferedSink? = null

    @Throws(IOException::class)
    override fun contentLength(): Long {
        return requestBody.contentLength()
    }

    override fun contentType(): MediaType {
        return requestBody.contentType()
    }

    @Throws(IOException::class)
    override fun writeTo(sink: BufferedSink) {
        bufferedSink = Okio.buffer(sink(sink))

        //写入
        requestBody.writeTo(bufferedSink)
        //必须调用flush,否则最后一部分数据可能不会被写入
        bufferedSink!!.flush()
    }

    /**
     * 写入,回调进度接口
     */
    private fun sink(sink: Sink): Sink {
        return object : ForwardingSink(sink) {
            //当前写入字节数
            internal var bytesWritten = 0L
            //总字节长度,避免多次调用contentLength()方法
            internal var contentLength = 0L

            @Throws(IOException::class)
            override fun write(source: Buffer, byteCount: Long) {
                super.write(source, byteCount)
                if (contentLength == 0L) {
                    //获得contentLength的值,后续不再调用
                    contentLength = contentLength()
                }
                //增加当前写入的字节数
                bytesWritten += byteCount
                //回调
                callback.onLoading(contentLength, bytesWritten)
            }
        }
    }
}

注意重写的writeTo方法,如果把拦截级别设置成Body会导致该方法执行两次。

上传封装

以上传图片为例

fun uploadImage(file: File, type: Int, retrofitCallback: RetrofitCallback, subscriber: Consumer<ApiResponse<String>>) {

        Observable
            .just(file)
            .map {
                val requestBody = RequestBody.create(MediaType.parse("image/png"), it)
                val fileRequestBody = FileRequestBody(requestBody, retrofitCallback)
                MultipartBody.Part.createFormData("file", file.name, fileRequestBody)
            }
            .subscribe {
                AppApiHelper
                    .create(UploadDocumentService::class.java)
                    .uploadPicture(activity.access, it, type)
                    .subscribeOn(Schedulers.io())
                    .subscribe(subscriber)
            }
    }

调用示例

fun getPictureUri(file: File) {

        dialogManager.mdProgress("上传中...", true)

        uploadImage( file, type, object : RetrofitCallback() {
            override fun onSuccess(call: Call, response: Response) {
                Log.e("retrofit", "response: $response")
            }

            override fun onLoading(total: Long, progress: Long) {
                Log.e("retrofit", "total: $total ; progress: $progress")

                val p = (progress * 100 / total).toInt()
                dialogManager.updateProgress(p, 100, "", true)
                if (p == 100) {
                    SystemClock.sleep(200)
                    dialogManager.dialog.dismiss()
                }
            }

            override fun onFailure(call: Call?, e: IOException) {
                Log.e("retrofit", "e: $e")
            }
        },
        Consumer {
            if (networkUtil.checkResponse(it)) {
                activity.toastDebug(it.data ?: "上传成功")
            } else {
                activity.toastDebug(it.message ?: "上传失败")
            }
        }
    )
}

此处用的是我自行封装的dialog,睡200毫秒是为了在小文件上传的时候也能看见一点上传进度条。

相关文章

网友评论

    本文标题:retofit + rxjava + kotlin 上传进度回调

    本文链接:https://www.haomeiwen.com/subject/esppeftx.html