数据库是应用开发中常用的技术,在Android应用中也不例外。Android默认使用了SQLite数据库,在应用程序开发中,我们使用最多的无外乎增删改查。纵使操作简单,也有可能出现查找数据缓慢,插入数据耗时等情况,如果出现了这种问题,我们就需要考虑对数据库操作进行优化了。本文将介绍一些实用的数据库优化操作,希望可以帮助大家更好地在开发过程中使用数据库。

建立索引

很多时候,我们都听说,想要查找快速就建立索引。这句话没错,数据表的索引类似于字典中的拼音索引或者部首索引。

索引的解释

重温一下我们小时候查字典的过程:

  • 对于已经知道拼音的字,比如这个字,我们只需要在拼音索引里面找到zhong,就可以确定这个字在词典中的页码。
  • 对于不知道拼音的字,比如这个字,我们只需要在部首索引里面查找这个字,就能找到确定这个字在词典中的页码。

没错,索引做的事情就是这么简单,使得我们不需要查找整个数据表就可以实现快速访问。

建立索引

创建索引的基本语法如下

1
CREATE INDEX index_name ON table_name;

创建单列索引

1
CREATE INDEX index_name ON table_name (column_name);

索引真的好么

毋庸置疑,索引加速了我们检索数据表的速度。然而正如西方谚语 “There are two sides of a coin”,索引亦有缺点:

  • 对于增加,更新和删除来说,使用了索引会变慢,比如你想要删除字典中的一个字,那么你同时也需要删除这个字在拼音索引和部首索引中的信息。
  • 建立索引会增加数据库的大小,比如字典中的拼音索引和部首索引实际上是会增加字典的页数,让字典变厚的。
  • 为数据量比较小的表建立索引,往往会事倍功半。

所以使用索引需要考虑实际情况进行利弊权衡,对于查询操作量级较大,业务对要求查询要求较高的,还是推荐使用索引的。

编译SQL语句

SQLite想要执行操作,需要将程序中的sql语句编译成对应的SQLiteStatement,比如select * from record这一句,被执行100次就需要编译100次。对于批量处理插入或者更新的操作,我们可以使用显式编译来做到重用SQLiteStatement。

想要做到重用SQLiteStatement也比较简单,基本如下:

  • 编译sql语句获得SQLiteStatement对象,参数使用?代替
  • 在循环中对SQLiteStatement对象进行具体数据绑定,bind方法中的index从1开始,不是0

请参考如下简单的使用代码

1
2
3
4
5
6
7
8
9
10
11
private void insertWithPreCompiledStatement(SQLiteDatabase db) {
String sql = "INSERT INTO " + TableDefine.TABLE_RECORD + "( " + TableDefine.COLUMN_INSERT_TIME + ") VALUES(?)";
SQLiteStatement statement = db.compileStatement(sql);
int count = 0;
while (count < 100) {
count++;
statement.clearBindings();
statement.bindLong(1, System.currentTimeMillis());
statement.executeInsert();
}
}

显式使用事务

在Android中,无论是使用SQLiteDatabase的insert,delete等方法还是execSQL都开启了事务,来确保每一次操作都具有原子性,使得结果要么是操作之后的正确结果,要么是操作之前的结果。

然而事务的实现是依赖于名为rollback journal文件,借助这个临时文件来完成原子操作和回滚功能。既然属于文件,就符合Unix的文件范型(Open-Read/Write-Close),因而对于批量的修改操作会出现反复打开文件读写再关闭的操作。然而好在,我们可以显式使用事务,将批量的数据库更新带来的journal文件打开关闭降低到1次。

具体的实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void insertWithTransaction(SQLiteDatabase db) {
int count = 0;
ContentValues values = new ContentValues();
try {
db.beginTransaction();
while (count++ < 100) {
values.put(TableDefine.COLUMN_INSERT_TIME, System.currentTimeMillis());
db.insert(TableDefine.TABLE_RECORD, null, values);
}
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
}
}

上面的代码中,如果没有异常抛出,我们则认为事务成功,调用db.setTransactionSuccessful();确保操作真实生效。如果在此过程中出现异常,则批量数据一条也不会插入现有的表中。

查询数据优化

对于查询的优化,除了建立索引以外,有以下几点微优化的建议

按需获取数据列信息

通常情况下,我们处于自己省时省力的目的,对于查找使用类似这样的代码

1
2
3
private void badQuery(SQLiteDatabase db) {
db.query(TableDefine.TABLE_RECORD, null, null, null, null, null, null) ;
}

其中上面方法的第二个参数类型为String[],意思是返回结果参考的colum信息,传递null表明需要获取全部的column数据。这里建议大家传递真实需要的字符串数据对象表明需要的列信息,这样做效率会有所提升。

提前获取列索引

当我们需要遍历cursor时,我们通常的做法是这样

1
2
3
4
5
6
private void badQueryWithLoop(SQLiteDatabase db) {
Cursor cursor = db.query(TableDefine.TABLE_RECORD, new String[]{TableDefine.COLUMN_INSERT_TIME}, null, null, null, null, null) ;
while (cursor.moveToNext()) {
long insertTime = cursor.getLong(cursor.getColumnIndex(TableDefine.COLUMN_INSERT_TIME));
}
}

但是如果我们将获取ColumnIndex的操作提到循环之外,效果会更好一些,修改后的代码如下:

1
2
3
4
5
6
7
8
private void goodQueryWithLoop(SQLiteDatabase db) {
Cursor cursor = db.query(TableDefine.TABLE_RECORD, new String[]{TableDefine.COLUMN_INSERT_TIME}, null, null, null, null, null) ;
int insertTimeColumnIndex = cursor.getColumnIndex(TableDefine.COLUMN_INSERT_TIME);
while (cursor.moveToNext()) {
long insertTime = cursor.getLong(insertTimeColumnIndex);
}
cursor.close();
}

ContentValues的容量调整

SQLiteDatabase提供了方便的ContentValues简化了我们处理列名与值的映射,ContentValues内部采用了HashMap来存储Key-Value数据,ContentValues的初始容量是8,如果当添加的数据超过8之前,则会进行双倍扩容操作,因此建议对ContentValues填入的内容进行估量,设置合理的初始化容量,减少不必要的内部扩容操作。

及时关闭Cursor

使用数据库,比较常见的就是忘记关闭Cursor。关于如何发现未关闭的Cursor,我们可以使用StrictMode,详细请戳这里Android性能调优利器StrictMode

耗时异步化

数据库的操作,属于本地IO,通常比较耗时,如果处理不好,很容易导致ANR,因此建议将这些耗时操作放入异步线程中处理,这里推荐一个单线程 + 任务队列形式处理的HandlerThread实现异步化。

源码下载

示例源码,存放在Github,地址为AndroidSQLiteTuningDemo

Android 中 SQLite 性能优化的更多相关文章

  1. 那些Android中的性能优化

    性能优化是一个大的范畴,如果有人问你在Android中如何做性能优化的,也许都不知道从哪开始说起. 首先要明白的是,为什么我们的App需要优化,最显而易见的时刻:用户say,什么狗屎,刷这么久都没反应 ...

  2. Android中的性能优化

    由于手机硬件的限制,内存和CPU都无法像pc一样具有超大的内存,Android手机上,过多的使用内存,会容易导致oom,过多的使用CPU资源,会导致手机卡顿,甚至导致anr.我主要是从一下几部分进行优 ...

  3. 在 Android开发中,性能优化策略十分重要

    在 Android开发中,性能优化策略十分重要本文主要讲解性能优化中的布局优化,希望你们会喜欢.目录 示意图 1. 影响的性能 布局性能的好坏 主要影响 :Android应用中的页面显示速度 2. 如 ...

  4. 转——Android应用开发性能优化完全分析

    [工匠若水 http://blog.csdn.net/yanbober 转载请注明出处.] 1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉 ...

  5. Android 应用开发性能优化完全分析

    1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉大家你一总结.我一总结的都说到了很多优化注意事项,但是看过这些文章后大多数存在一个问题就是只 ...

  6. 【转】Android应用开发性能优化完全分析

    http://blog.csdn.net/yanbober/article/details/48394201 1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关 ...

  7. Android应用开发性能优化完全分析

    1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉大家你一总结.我一总结的都说到了很多优化注意事项,但是看过这些文章后大多数存在一个问题就是只 ...

  8. ym——Android之ListView性能优化

    转载请注明本文出自Cym的博客(http://blog.csdn.net/cym492224103),谢谢支持! Android之ListView性能优化 假设有看过我写过的15k面试题的朋友们一定知 ...

  9. <只看这个就够了。。。>Android自动化测试及性能优化

    Android自动化测试及性能优化 分类: Android Java Tools2012-12-09 23:31 4300人阅读 评论(0) 收藏 举报 软件自动化测试对于程序员来说能够确保软件开发的 ...

随机推荐

  1. 2016021904 - 如何使用Memory Analyzer

    如何使用Memory Analyzer呢? 0.有内存溢出的代码code.<深入理解java虚拟机>中代码 package neutron.oom.heap; import java.ut ...

  2. ios8新特性widget开发-b

    os8发布已经有一段时间了,伴随着ios8同时也出现了许多新的特性,ios系统将会越来越开放,这是好事.其中一个新特性就是在下拉通知栏里加入了个性的widget,开发者可以自己定义widget的样式内 ...

  3. require.js 入门学习-备

    一.为什么要用require.js? 最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了.后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载.下面的网页代 ...

  4. Phonegap 3.0 拍照 出错的说明

    在官方3.0 提供的摄像机操作例子是不成功的,因为该例子没有说明摄像机操作需要添加Plugin. 添加插件方法(安装cordova3.0时必须使用官方命令行方式,通过nodejs安装,且装上了git) ...

  5. socket、tcp、http

    第一部分.概念的理解 1.什么是Socket? Socket又称之为“套接字”,是系统提供的用于网络通信的方法.它的实质并不是一种协议,没有规定计算机应当怎么样传递消息,只是给程序员提供了一个发送消息 ...

  6. 用了OneAPM CT,宕机早知道!

    Twitter 的公司网站和移动应用在 1 月 19 日早上出现宕机,导致全球部分地区用户无法正常访问.这次宕机影响了很多用户,英国和印度用户已经无法访问 Twitter .第三方监测机构 DownD ...

  7. java 集合框架图

    Java平台提供了一个全新的集合框架.“集合框架”主要由一组用来操作对象的接口组成.不同接口描述一组不同数据类型. Java 2集合框架图集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架 ...

  8. error: /lib64/libc.so.6: symbol _dl_starting_up, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference

    ]$ sudo yum install libnotify*Loaded plugins: fastestmirror, refresh-packagekit, securitySetting up ...

  9. Mysql慢日志查询

    MYSQL慢查询配置: 查看是否启用慢日志查询: show VARIABLES like '%quer%'; 没有配置的情况下慢查询是关闭的:

  10. Idea学习笔记

    (1)安装,启动,配置: 1.安装完的插件如果不需要的插件可以不启动它:可以在setting里面配置plugins勾选不需要的插件:好处是加快idea的启动: 2.安装完成之后启根据系统32位,64位 ...