android gzip数据压缩 笔记

在 Android 中进行 GZIP 数据压缩,主要涉及向服务器发送压缩数据解压服务器返回的压缩数据两个场景。

以下是如何在客户端(Android)实现这两个关键操作的详细说明和代码示例。

一、发送数据:压缩请求体(POST/PUT等)

当你需要向服务器发送大量文本数据(如 JSON)时,在客户端压缩可以显著减少上传流量。

核心步骤:

  1. 压缩数据 :使用 GZIPOutputStream 将原始数据(如字符串)压缩为字节数组。
  2. 设置请求头 :在 HTTP 请求头中明确告诉服务器数据是经过 GZIP 压缩的,通常设置 Content-Encoding: gzip
  3. 发送二进制数据:将压缩后的字节数组作为请求体发送。

以下是使用 OkHttp 库发送 GZIP 压缩请求的推荐做法:

kotlin 复制代码
import okhttp3.*
import okio.*
import java.io.ByteArrayOutputStream
import java.util.zip.GZIPOutputStream

// 1. 创建一个 OkHttpClient 实例
val client = OkHttpClient()

// 2. 准备要发送的原始数据(例如一个 JSON 字符串)
val jsonString = """{"name":"张三","age":25,"comment":"这是一个可能会很长的文本字段..."}"""
val requestBody = jsonString.toRequestBody("application/json; charset=utf-8".toMediaType())

// 3. 使用 GZIP 压缩请求体
val gzippedRequestBody = requestBody.gzip() // 调用下面的扩展函数

// 4. 构建请求,并设置正确的请求头
val request = Request.Builder()
    .url("https://your.api.com/endpoint")
    .post(gzippedRequestBody) // 使用压缩后的请求体
    .header("Content-Encoding", "gzip") // 关键:声明内容编码方式
    .build()

// 5. 发起异步请求
client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        // 处理网络失败
    }
    override fun onResponse(call: Call, response: Response) {
        // 处理服务器响应
    }
})

// --- 关键:用于压缩 RequestBody 的 OkHttp 扩展函数 ---
fun RequestBody.gzip(): RequestBody {
    return object : RequestBody() {
        override fun contentType(): MediaType? = this@gzip.contentType()
        override fun contentLength(): Long = -1 // 压缩后长度未知,传递 -1
        override fun writeTo(sink: BufferedSink) {
            // 创建 GZIP 输出流,包装原始的 sink
            val gzipSink = GZIPOutputStream(sink.outputStream()).buffer()
            // 将原始 RequestBody 的内容写入压缩流
            this@gzip.writeTo(gzipSink)
            gzipSink.close() // 必须关闭 GZIP 流以写入尾部数据
        }
    }
}

二、接收数据:处理服务器返回的压缩响应(GET等)

更常见的场景是,服务器返回了经过 GZIP 压缩的响应体(如 HTML、JSON),客户端需要正确解压。好消息是,现代网络库(如 OkHttp)通常会自动处理这个过程

方式一:依赖网络库自动处理(推荐)

OkHttp、Retrofit 等库默认会自动处理 Content-Encoding: gzip 响应头,并对响应体进行透明解压。 你通常不需要手动写解压代码。

kotlin 复制代码
val client = OkHttpClient.Builder()
    .build() // 默认已支持自动解压 GZIP 响应

val request = Request.Builder()
    .url("https://api.example.com/data")
    .build()

client.newCall(request).enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
        // OkHttp 已在此处自动解压,你直接拿到的是解压后的字符串
        val responseBodyString = response.body?.string()
        // ... 处理数据
    }
    // ... onFailure
})

方式二:手动解压字节数据(如需)

如果你通过其他方式(如 HttpURLConnection)获得了原始的压缩字节流,可以手动解压。

kotlin 复制代码
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.util.zip.GZIPInputStream

fun decompressGzip(compressedBytes: ByteArray): String {
    // 使用 GZIPInputStream 包装压缩的字节输入流
    ByteArrayInputStream(compressedBytes).use { bis ->
        GZIPInputStream(bis).use { gzipIs ->
            // 读取解压后的数据
            ByteArrayOutputStream().use { bos ->
                val buffer = ByteArray(1024)
                var len: Int
                while (gzipIs.read(buffer).also { len = it } != -1) {
                    bos.write(buffer, 0, len)
                }
                // 转换为字符串(假设是文本)
                return bos.toString("UTF-8")
            }
        }
    }
}

// 使用示例
// val compressedData: ByteArray = ... // 从网络获取的原始字节
// val originalString = decompressGzip(compressedData)

三、关键实践要点与注意事项

要点 说明与建议
1. 服务器协同 发送时:服务器需能解析 Content-Encoding: gzip 请求头并自动解压。 接收时:服务器需正确设置 Content-Encoding: gzip 响应头。
2. 压缩收益 文本数据 (JSON、XML、HTML)压缩效果极佳(可达70%+)。 已压缩格式(如图片JPEG/PNG、视频、已为.gz文件)再次压缩无效,反而增加开销。
3. 性能权衡 压缩/解压消耗 CPU 资源 ,节省网络带宽和传输时间 。在移动网络下,通常利远大于弊。这与耗电优化需权衡:密集压缩可能增加耗电,但大幅缩短了网络活跃时间,通常整体更省电。
4. 测试验证 使用抓包工具(如 Charles、Fiddler)检查请求/响应头及原始数据大小,确认压缩生效。在代码中打印压缩前后数据长度进行对比。

四、总结与最佳实践

  1. 发送压缩请求 :使用 OkHttp 等库的扩展功能,在发送前压缩请求体,并正确设置 Content-Encoding: gzip 头。
  2. 接收压缩响应优先依赖网络库的自动解压功能,无需手动实现。
  3. 针对性压缩:主要对文本类 API 响应和请求进行压缩,避免对已压缩的二进制资源重复操作。
  4. 全程监控:通过抓包工具验证压缩效果,确保服务器和客户端配置正确。

将 GZIP 压缩与之前提到的网络请求合并、缓存策略、图片优化等手段结合,能全方位、有效地优化应用流量。

相关推荐
城东米粉儿2 小时前
android 流量优化笔记
android
似霰3 小时前
HIDL Hal 开发笔记10----添加硬件访问服务(Java 层调用 HIDL)
android·framework·hal
佛系打工仔4 小时前
绘制K线第三章:拖拽功能实现
android·前端·ios
我命由我123454 小时前
Android 项目路径包含非 ASCII 字符问题:Your project path contains non-ASCII characters
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
wszy18095 小时前
rn_for_openharmony_空状态与加载状态:别让用户对着白屏发呆
android·javascript·react native·react.js·harmonyos
城东米粉儿5 小时前
JobScheduler 相关笔记
android
城东米粉儿5 小时前
android 耗电优化 笔记
android
张小潇5 小时前
AOSP15的Zygote启动流程源码分析
android
毕设源码-钟学长5 小时前
【开题答辩全过程】以 基于安卓的医疗健康查询系统为例,包含答辩的问题和答案
android