Android性能优化——I/O优化

Bitmap decode

BitmapFactory.java提供多个decode Bitmap的API,有decodeFile()、 decodeResource()、 decodeByteArray()、 decodeFileDescriptor()、 decodeStream()、 decodeResourceStream()。 而大家最常用的是decodeFile()。

Android 4.3 decodeFile()方法 最终使用的是BufferedInputStream,使用的Buffer大小为16KB。而4.4以后使用的是FileInputStream。
BufferedInputStream是字节缓冲流,BufferedInputStream比FileInputStream多了一个缓冲区,执行read时先从缓冲区读取,当缓冲区数据读完时再把缓冲区填满;

FileInputStream是字节流;使用BufferedInputStream读资源比FileInputStream读取资源的效率高(BufferedInputStream的read方法会读取尽可能多的字节),且FileInputStream对象的read方法会出现阻塞;

因此可以使用BufferedInputStream包裹输入流(FileInputStream)调用decodeStream(),来代替decodeFile()、decodeResource()方法。(大多数情况下差别不大)

随机读写的效率

  1. 当写操作在数据库的db文件和journal[1]文件中来回发生时,会导致随机读写。
  1. 当设置了AUTOINCREMENT的表中插入多条数据,那么每插入一条数据,都需要操作两张数据库表,这就意味着存在随机写。
  • 随机读写的缺点:

    1. 失去预读(read-ahead)的优化效果。
    2. 写入放大[2]

冗余读/写

  • SharedPreference apply
  • 需要在多个地方多次读取同一文件时(如配置文件),只读一次,把需要的内容写到缓存中
  • 优化sql语句减少无谓的查询(如 数据库中写入一条数据,若这条数据不存在,插入数据,若已存在,修改这条数据,这种情况下使用"insert or replace" 语句来避免先查询,后操作)。

主线程读写

  • 由于存在写入放大的情况,可能会让平时时间很短的操作被放大几十倍,因此 I/O 操作要在子线程中进行

    测试工具
    StrictMode Doc: https://developer.android.google.cn/reference/android/os/StrictMode.html

    • 作用
      主线程I/O 发现 + 定位 (也可以测主线程访问网络,将违规代码在log中定位)

    • 示例

    // 在Application 或Activity的onCreate中调用
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                  .detectDiskReads()
                  .detectDiskWrites()
                  .detectNetwork()   // or .detectAll() for all detectable problems
                  .penaltyLog()
                  .build());
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                  .detectLeakedSqlLiteObjects()
                  .detectLeakedClosableObjects()
                  .penaltyLog()
                  .penaltyDeath()
                  .build());
      
    

读写效率

  • ObjectOutputStream 读写序列化对象的优化
    ObjectOutputStream 在保存对象的时候,每个数据成员会带来一次 I/O 操作,即使很小的文件也会有成千上万次I/O的。
    如果在ObjectOutputStream上面再封装一个输出流ByteArrayOutputStream,先将对象序列 化后的信息写到缓存区中,然后再一次性地写到磁盘上,可减少磁盘操作。

  • 合理设置Buffer大小
    这里推荐使用Java默认的Buffer大小8KB;Buffer大小至少应为4KB。

    Buffer也不是越大越好,Buffer如果太大,会导致申请Buffer的时间变长,反而整体效率不高。

    还有一种更智能地确定Buffer大小的方法。这个方法由两个影响因子决定,一是Buffer size不能大于 文件大小;二是Buffer size 根据文件保存所挂载的目录的block size来确认Buffer大小,而数据库的pagesize,就是这样确定的(具体可见Android源码中SQLiteGlobal.java的getDefaultPageSize())。

数据库的打开与关闭

getWritableDatabase()方法的注释如下:

```Java
/**
 * Create and/or open a database that will be used for reading and writing.
 * The first time this is called, the database will be opened and
 * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
 * called.
 *
 * <p>Once opened successfully, the database is cached, so you can
 * call this method every time you need to write to the database.
 * (Make sure to call {@link #close} when you no longer need the database.)
 * Errors such as bad permissions or a full disk may cause this method
 * to fail, but future attempts may succeed if the problem is fixed.</p>
 *
 * <p class="caution">Database upgrade may take a long time, you
 * should not call this method from the application main thread, including
 * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
 *
 * @throws SQLiteException if the database cannot be opened for writing
 * @return a read/write database object valid until {@link #close} is called
 */
public SQLiteDatabase getWritableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(true);
    }
}
```

数据库打开以后,在真正不需要访问数据库以后再调用close关闭,避免重复打开,造成不必要的浪费。如果App中多处使用数据库,在应用程序退出时关闭即可。

AUTOINCREMENT的坑

The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.
In SQLite, a column with type INTEGER PRIMARY KEY is an alias for the ROWID (except in WITHOUT ROWID tables) which is always a 64-bit signed integer.
On an INSERT, if the ROWID or INTEGER PRIMARY KEY column is not explicitly given a value, then it will be filled automatically with an unused integer, usually one more than the largest ROWID currently in use. This is true regardless of whether or not the AUTOINCREMENT keyword is used.
If the AUTOINCREMENT keyword appears after INTEGER PRIMARY KEY, that changes the automatic ROWID assignment algorithm to prevent the reuse of ROWIDs over the lifetime of the database. In other words, the purpose of AUTOINCREMENT is to prevent the reuse of ROWIDs from previously deleted rows.

—— SQLite官方文档

�AUTOINCREMENT可以保证主键的严格递增,但使用AUTOINCREMENT会增加额外的开销,INSERT数据时耗时 1倍以上,所以非必需时尽量不要用。


  1. 文件.db_journal 是sqlite的一个临时的日志文件,主要用于sqlite事务回滚机制,在事务开始时产生。在事务结束时删除。当程序发生崩溃或者系统断电时该文件将留在磁盘上,以便下次程序运行时进行事务回滚。

  2. 写入放大(英语:Writeamplification,简称WA)是闪存和固态硬盘(SSD)中一种不期望的现象,即实际写入的物理信息量是将要写入的逻辑数量的多倍。因为闪存在可重新写入数据前必须先擦除,执行这些操作的过程就产生了一次以上的用户数据和元数据的移动(或重新写入)。此倍增效应会增加请求写入的次数,这会缩短SSD的寿命,从而减小SSD可靠运行的时间。增加的写入也会消耗闪存的带宽,这个效应主要会降低SSD的随机写入性能。许多因素会影响SSD的写入放大;一些可以由用户来控制,而另一些则是数据写入和SSD使用的直接结果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容

  • 转 # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    吕品㗊阅读 9,710评论 0 44
  • DiskLurCache 使用教程 源码解析 使用 打开缓存 打开缓存函数public static DiskLr...
    super小立立阅读 911评论 1 1
  • 1、安装包太大会对下载失败率有影响。 2、图片采用webP,可以节省用户流量。 3、减少随机读写操作,以此减少对磁...
    Kyunban阅读 704评论 0 0
  • 乌云压低的天空,心情如这般难过 原来,我们没有缘分 人海里我再也没有看到你的身影 通过那扇窗,我才知道你忙着和所有...
    风起旧巷雨阅读 234评论 0 1
  • 受过的伤害积累多了以后,就慢慢学会了一个新技能,原地复活随时随地转移注意力。 再也不会要死要活,为情所困而去跳楼的...
    吧啦吧啦kitty阅读 145评论 0 0