Android随机文件读写效率,Android 性能优化知识点

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

魔鬼藏于细节之中,每一段粗心的代码都可能影响软件的性能,本文主要记录一些不那么常见的性能优化知识点。

200937570e0cdc89b0b8fbf1192bd5bf.png

每次打开、读写文件,操作系统需要从用户态切换到内核态,这种状态切换很消耗性能。

优化核心

减少磁盘 I/O 操作量,尤其是主线程的。

多使用缓存,避免重复读写。

合并读写,延迟写入。

知识点

1.避免随机读写随机读写会失去预读(read-ahead)的优化效果

随机读写会触发 写入放大效应,增加延迟写入放大(英语:Write amplification,简称WA)是闪存和固态硬盘(SSD)中一种不良的现象,即实际写入的物理数据量是写入数据量的多倍。

由于闪存在可重新写入数据前必须 先擦除,而擦除操作的 粒度 与写入操作相比低得多,执行这些操作就会多次移动(或改写)用户数据和元数据。因此,要改写数据,就需要读取闪存某些已使用的部分,更新它们,并写入到新的位置,如果新位置在之前已使被用过,还需连同先擦除。由于闪存的这种工作方式,必须擦除改写的闪存部分比新数据实际需要的大得多。此倍增效应会增加请求写入的次数,缩短SSD的寿命。

在某些情况下,一个块(假设 512KB)里只有一个 “页” 可以使用,本来只需要写入 4KB 的数据,在 SSD 中随机读写,就需要这么多操作:

读取 A 块数据(512KB)到 B块 → B块修改 4KB → 擦除 A块(512KB) → 将 B 的数据写入 A (512KB )

写入放大出现场景:手机长期使用,磁盘空间不足

应用触发大量随机写

2.数据库

DB 文件和普通文件本质上一样,优化数据库最终也是要减少磁盘 I/O。

优化表结构、使用索引、增加缓存、调整 page-size

慎用 AUTOINCREMENT

主键就是行号,不使用自增,被删掉的行,还可以复用。

使用它后,只能使用行号大于之前的行,而且需要另外维护一张 sql_sequence 表,导致插入性能降低。

缓存数据库连接

数据库在打开后,先不要关闭,在 app 退出时再关闭。

3.SharedPreferences 延迟写入

每调用一次 SharedPreferences.commit() 就对应一次文件的打开、关闭。

多次 SP 操作,最好避免多次 commit

建议用 apply() 代替 commit:apply 是异步操作

commit 是同步操作

建议提前初始化 SP,初始化过程的 I/O 在主线程。

4.用对 Java I/O APIhttps://docs.oracle.com/javase/tutorial/essential/io/streams.html

在做磁盘读写时,是否关心过使用哪个 API 性能更好呢?

选择对的 API,可能会让现有的 I/O 效率提升好几倍。

一个错误的例子是直接使用 FileOutputStream 作为参数传递给 ObjectOutputStream:

public void write(String file, Object data) {

ObjectOutputStream os = null;

try {

os = new ObjectOutputStream(openFileOutput(file, MODE_PRIVATE));

os.writeObject(data);

os.flush();

} catch (IOException e) {

e.printStackTrace();

} finally {

IOUtil.closeQuietly(os);

}

}

这样在数据是一个列表时,可能会 I/O 多次。

建议使用一个 buffer 包装一层(比如 ByteArrayOutputStream, BufferedOutputStream ),先把数据写到缓存区,然后再写入文件:

public void write(String file, Object data) {

ObjectOutputStream os = null;

ByteArrayOutputStream baos;

try {

// 先写到缓存区

baos = new ByteArrayOutputStream();

os = new ObjectOutputStream(baos);

os.writeObject(data);

os.flush();

//一次性写到磁盘

FileOutputStream fos = openFileOutput(file, MODE_PRIVATE);

baos.writeTo(fos);

baos.flush();

fos.flush();

} catch (IOException e) {

e.printStackTrace();

} finally {

//...

}

}

5.Buffer 使用合适的大小

byte buffer[] = new byte[1024];

byte buffer[] = new byte[8*1024];

Buffer 容量太大,会导致申请 Buffer 的时间变长;太小会导致 I/O 太频繁。结合要写入的数据大小,适度提高 buffer 容量。

推荐 buffer 容量为 8KB,Java 默认的容量大小就是这个。

确定 Buffer 大小:不能大于文件大小

根据文件保存所挂载目录的 块大小(block size)确定

2.decode 图片优先使用 BitmapFactory.decodeStream() 并且使用 BufferInputStream 做缓存,降低读取磁盘次数,减少耗时。

6.压缩文件的 APIZipFile

ZipInputStream

ZipFile 优势场景:文件已在磁盘上

须全部解压

随机访问

其他场景使用 ZipInputStream。

7.解码图片使用 decodeStream 优于 decodeFile

BitmapFactory 解码图片时,决定写磁盘次数的是调用 native 方法 nativeDecodeStream 的次数,普通文件流会调用多次,而缓存文件流则会少很多。

BitmapFactory.decodeFile() 生成的文件流是 FileInputStream,无法修改;BitmapFactory.decodeStream() 可以传递一个 BufferedInputStream 进去:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath))

Bitmap bitmap = BitmapFactory.decodeStream(bis, null, ops);

工具Systrace,发现主线程 I/O 或者 I/O 操作耗时过长

STRICTMODE,发现主线程 I/O

内存常驻内存问题(图片)

泄漏问题

GC 问题(GC for Alloc)

图片加载到内存是如何计算占用内存大小的,有什么优化策略?磁盘大小是在磁盘上占用的空间

内存大小是加载到内存中占用的内存大小

图片内存大小:高度 * 图片宽度 * 一个像素占用的内存大小

一个像素占用的内存大小,涉及到图片的编码格式。

编码格式及占用内存如下:ALPHA_8: 1,每个像素只要一个字节,因为没编码颜色信息,只有 alpha 通道

RGB_565: 2,每个像素要 2 字节,只编码 RGB 通道

ARGB_4444: 2,每个像素要 2 字节,不过被遗弃了,建议使用下面这个

ARGB_8888: 4,每个像素要 4 字节,质量最好的一个,默认是这种定义在 Bitmap.Config 中

在 BitmapFactory.decodeResourceStream() 中可以看到,放在 res 目录下的图片,在不同屏幕密度的手机和不同的目录下,会对图片做不同程度的缩放,最终影响占用内存大小。

public static Bitmap decodeResourceStream(Resources res, TypedValue value,

InputStream is, Rect pad, Options opts) {

if (opts == null) {

opts = new Options();

}

if (opts.inDensity == 0 && value != null) {

final int density = value.density;

if (density == TypedValue.DENSITY_DEFAULT) {

opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;

} else if (density != TypedValue.DENSITY_NONE) {

opts.inDensity = density;

}

}

if (opts.inTargetDensity == 0 && res != null) {

opts.inTargetDensity = res.getDisplayMetrics().densityDpi;

}

return decodeStream(is, pad, opts);

}

缩放后图片的宽度 = 原图宽度 * (设备 dpi / 目录 dpi),高度也是如此。

目录 dpi 对应的密度:

d24bc8601e910321b4d4636bfdbe2ae0.png

可以看到,图片放置的目录密度越高,缩放后图片分辨率相对越小,所以我们开发中尽量将图片放置到高 dpi 的目录。LruCache

DiskLruCache

BlobCache P57

网络

CPU

电池

Thanks《Android 移动性能实战》

https://www.cnblogs.com/dasusu/p/9789389.html

https://www.cnblogs.com/popfisher/p/6770018.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值