Android OkHttp 下载限速方案实现

一、背景

下载时,大量的文件写入,会影响其他操作以及带宽,需要限制下载速度

二、实现方案

1、添加一个限制下载速度的请求头

RateLimitedInterceptor

Kotlin 复制代码
package com.vc.appbackup.net

/**
 * @Description : RateLimitedInterceptor 下载限速请求头
 */
import okhttp3.Interceptor
import okhttp3.Response
import okio.Buffer
import okio.BufferedSource
import okio.ForwardingSource
import okio.buffer
import java.io.IOException
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong

class RateLimitedInterceptor(
    private val maxBytesPerSecond: Long,//单位字节,即大B
    private val bucketSize: Long = maxBytesPerSecond // 令牌桶容量
):Interceptor{

    private val tokens = AtomicLong(bucketSize)
    private var lastRefillTime = System.nanoTime()

    @Synchronized
    private fun refillTokens() {
        val now = System.nanoTime()
        val elapsedNanos = now - lastRefillTime
        val tokensToAdd = (elapsedNanos * maxBytesPerSecond) / TimeUnit.SECONDS.toNanos(1)

        if (tokensToAdd > 0) {
            tokens.set(minOf(bucketSize, tokens.get() + tokensToAdd))
            lastRefillTime = now
        }
    }

    private fun consumeTokens(bytes: Long) {
        var remainingBytes = bytes

        while (remainingBytes > 0) {
            refillTokens()

            val available = tokens.get()
            if (available <= 0) {
                // 等待令牌补充
                try {
                    val waitTime = TimeUnit.NANOSECONDS.toMillis(
                        TimeUnit.SECONDS.toNanos(1) / maxBytesPerSecond
                    )
                    Thread.sleep(maxOf(1, waitTime))
                } catch (e: InterruptedException) {
                    Thread.currentThread().interrupt()
                    break
                }
                continue
            }

            val toConsume = minOf(available, remainingBytes)
            if (tokens.compareAndSet(available, available - toConsume)) {
                remainingBytes -= toConsume
            }
        }
    }

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalResponse = chain.proceed(chain.request())

        return originalResponse.newBuilder()
            .body(originalResponse.body?.let {
                TokenBucketResponseBody(it, this::consumeTokens)
            })
            .build()
    }
}

class TokenBucketResponseBody(
    private val originalBody: okhttp3.ResponseBody,
    private val consumeTokens: (Long) -> Unit
) : okhttp3.ResponseBody() {

    override fun contentType() = originalBody.contentType()

    override fun contentLength() = originalBody.contentLength()

    override fun source(): BufferedSource {
        return originalBody.source().let { source ->
            object : ForwardingSource(source) {
                @Throws(IOException::class)
                override fun read(sink: Buffer, byteCount: Long): Long {
                    val bytes = super.read(sink, byteCount)
                    if (bytes > 0) {
                        consumeTokens(bytes)
                    }
                    return bytes
                }
            }
        }.buffer()
    }

    override fun close() {
        originalBody.close()
    }
}
2、在请求中加入RateLimitedInterceptor拦截器

示例代码如下:

Kotlin 复制代码
   val client = OkHttpClient.Builder()
                .callTimeout(0, TimeUnit.SECONDS)
                .connectTimeout(120, TimeUnit.SECONDS)
                .readTimeout(120, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
                .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC))
                .addInterceptor(RateLimitedInterceptor(DeviceUtil.getInstance().getMaxBytesPerSecond().toLong()))//添加限速拦截器
                .eventListener(object:EventListener() {
                     override fun callFailed (call: Call, ioe:IOException) {
                        
                     }
                }).build()
相关推荐
2501_915106321 天前
Perfdog 成本变高之后,Windows 上还能怎么做 iOS APP 性能测试
android·ios·小程序·https·uni-app·iphone·webview
愤怒的代码1 天前
从开发调试到生产上线:全维度 Android 内存监控与分析体系构建
android·java·kotlin
jzlhll1231 天前
Android最简化发布模块到mavenCentral
android·mavencentral
2501_915106321 天前
iOS 安装了证书,HTTPS 还是抓不到
android·网络协议·ios·小程序·https·uni-app·iphone
好奇龙猫1 天前
【人工智能学习-AI-MIT公开课13.- 学习:遗传算法】
android·人工智能·学习
TO_ZRG1 天前
Unity打包安卓、iOS知识点
android·unity·android studio
周杰伦fans1 天前
AndroidStudioJava国内镜像地址gradle
android·java·android-studio
艾莉丝努力练剑1 天前
【Linux进程控制(一)】进程创建是呼吸,进程终止是死亡,进程等待是重生:进程控制三部曲
android·java·linux·运维·服务器·人工智能·安全
2501_924064111 天前
2026年移动应用渗透测试流程方案及iOS与Android框架对比
android·ios