Android 数据压缩思路

前言

这里的数据不仅仅指的是文件或者某个String,所有的东西都可以称为数据,比如图片、视频、音频,甚至一些建模或者做一些CG采集到的信息,这些东西在代码中的表现都是"数据",只要是数据,那就都存在可压缩的操作。所以在开发时我们应该有一种习惯的思维,一切皆数据,是否要压缩。

之前也写过一些浅谈数据压缩的文章:
juejin.cn/post/720408... (字符串压缩)
juejin.cn/post/717255... (视频压缩)
juejin.cn/post/684490... (图片压缩)

这些文章都是讲了具体某个类型的压缩要怎么做,和一些的基础原理介绍。而这篇主要是说明一些压缩的思路和策略。

压缩算法

每种类型的数据基本都有比较成熟的压缩算法,比如视频的压缩有H264编码,String或者字节的压缩有Gzip,比如拿Gzip来举个例子

kotlin 复制代码
object StringCompressUtils {

    fun compressString(input: String): ByteArray {
        val byteArrayOutputStream = ByteArrayOutputStream()
        GZIPOutputStream(byteArrayOutputStream).use { gzipOutputStream ->
            gzipOutputStream.write(input.toByteArray(Charsets.UTF_8))
        }
        return byteArrayOutputStream.toByteArray()
    }

    fun decompressString(compressed: ByteArray): String {
        val byteArrayInputStream = ByteArrayInputStream(compressed)
        GZIPInputStream(byteArrayInputStream).use { gzipInputStream ->
            val reader = InputStreamReader(gzipInputStream, Charsets.UTF_8)
            return reader.readText()
        }
    }

}

调用

ini 复制代码
val testString = "一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二"  
val compressString = StringCompressUtils.compressString(testString)  
val resultString = StringCompressUtils.decompressString(compressString)  
Log.d("mmp","result: ${resultString}")

得到结果

可以看出一般都有成熟的算法,和方便调用的API提供我们使用。当然如果你想说想完全弄懂其中原理,甚至能自己根据原理来写出一套算法出来,这个就是更牛逼的一个境界了,同样这个过程也不是只花个一两天只看个一两篇文章能做到的。比如这里的Gzip的原理就是内部用的LZ77和哈夫曼编码,虽然我在juejin.cn/post/720408... 这篇文章也有介绍这两个东西,但也只是皮毛中的皮毛,你要完全弄懂,甚至说要弄懂为什么这个算法在设计的时候是选用这些技术,而不是其他技术,这就是不是一个简单的过程了。

我想说的是,在平时开发中我们直接拿现成的压缩算法来用就行,不用纠结这个。

压缩策略思考

普通的场景,像图片、视频、音频这些的存储肯定是会做压缩的,就不考虑这种情况。大多数人都不会考虑的情况就是普通数据(字符串、字节等)做压缩。

比如获取用户的Logcat日志上传到服务端,很多人就没有考虑过做压缩,导致上传的时候日志比较大的话就会上传得慢。当然数据大的时候压缩也同样会慢,所以这就是一个做策略的过程。

再比如我的业务有个场景是聊天,像微信那样的聊天,那聊天有历史记录吧,这个历史记录是存本地的(所以你看你的微信和QQ会占手机内存很多)。正常做的话会把这个历史记录放到本地数据库里,但是数据库默认是不会帮我们做压缩的。所以我也可以做成把数据压缩后放某个文件中,这样就能比直接存数据库的情况下省掉很多内存。

但是现实中的场景就没有这么简单,比如数据量大的话,压缩和解压都是会相应的耗时,那我总不能在聊天记录多的情况下让用户感觉菊花转半天吧?

这种场景下我是不是可以考虑分页,上拉分页,每次分页加载多少条历史记录,我对每一页的历史记录单独做压缩,这样就不会因为一次性解压而导致花费时间过长。

又或者说我直接推翻一切的思路,直接不压,因为现在的Android 设备,都是大内存,不像苹果一样内存贵得一批,Android的内存不值多少钱。这其实就是"时间换空间"的问题,那既然我空间大大的充裕,我直接不压缩,直接存。是不是可以写个策略来判断,第一次进应用的时候判断设备所剩余的空间大于某个阈值时,我就不压缩了,直接存,小于的话再做进一步的压缩策略。

那历史记录某些类型其实是会存在数据刷新的情况,比如我发一条链分享给你,这条分享的记录是这条链接,但是如果后台换链接了,你这个历史记录存的链接就过期了,所以可以做新链接的同步。那就会存在历史记录更新的情况,如果我对全部数据都压缩,那每次我更新数据都要解压,再更新,再压缩,这多麻烦。所以针对这种情况下,我是不是也可以写个策略"针对某些类型的数据不进行压缩"

以我过去的开发经验,我来看这个问题,我觉得是一个挺复杂的问题,其核心确实还是"时间和空间"的问题,空间够了我就省时间,空间不足我就用时间去换,它没有一个具体的方案说你应该怎么做才是对的,这和你的业务有关,你的业务场景数据量少,你可以不做任何操作,你的场景数据量大的话,我认为是应该去考虑是否压缩,甚至说做一个选择性压缩的策略。

这个策略如果要做,我认为是单独针对这个场景的一个策略,他不适用于其他场景,只适用于当前的场景。并且随着业务逻辑的迭代改变,策略也可能会做相应的改变。虽然策略是针对业务的,没有复用可言,没有标准可言,但我有一套自己的思路。

核心的数据不压缩,比如一些比较经会操作的数据,我的策略就是对这部分数据不做压缩,免得每次操作数据的都要执行解压重压。稍微不重要的并且数据量大的数据,可以做压缩。十分不重要的数据,我直接不缓存了,用到的时候再去请求。

相关推荐
IvanCodes35 分钟前
MySQL 视图
android·数据库·sql·mysql·oracle
KevinWang_39 分钟前
Java 和 Kotlin 混编导致的 bug
android·kotlin
好学人1 小时前
Android动画系统全面解析
android
leverge20091 小时前
android studio 运行java main报错
android·ide·android studio
RichardLai881 小时前
Flutter 环境搭建
android·flutter
思想觉悟1 小时前
ubuntu编译android12源码
android·ubuntu·源码
好学人1 小时前
Android自定义控件事件传递机制
android
V少年2 小时前
深入浅出 C++ 标准库
android
V少年2 小时前
深入浅出 C++ 特有关键字
android
weixin_432989552 小时前
Kotlin delay方法解析
android·开发语言·kotlin