返回介绍

6.8 SQLite 相关的异常

发布于 2024-08-17 23:46:12 字数 5428 浏览 0 评论 0 收藏 0

在App中,一般都使用SQLite这个数据库,本节介绍的Crash也是围绕着这个主题发生的。SQLite相关的异常大都和IO操作不当有关,由于我们无法猜测用户手机发生崩溃时的状态,所以这类异常是最难修复的。

6.8.1 No transaction is active

异常中的关键字:

android.database.sqlite.SQLiteException:cannot commit–no transaction is active

发生频率:★★★

在事务中,逐条循环插入(for+insert)大量数据时会导致这类崩溃。Android中在SqlLite插入数据的时候默认一条语句就是一个事务,有多少条数据就有多少次磁盘操作,而且不能保证所有数据都能同时插入。

相应的解决方案是使用SQLLite提供的批量插入语法,一次性地把这些数据都插入到数据库中,如下所示:

public void insertOrUpdateDataBatch() {
  SQLiteDatabase db = getWritableDatabase();
  db.beginTransaction();
  try {
    for (String sql : sqls) {
      db.execSQL(sql);
    }
    // 设置事务标志为成功,当结束事务时就会提交事务



    db.setTransactionSuccessful();
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    db.endTransaction();
    db.close();
  }
}

这段代码的成功与否,就在于setTransactionSuccessful()这个方法。在这个方法执行前,所有的execSQL方法都不会更新到数据库;等这个方法执行后,会一次性把所有execSQL方法都执行完成,数据会同步到数据库。不信的话可以在for循环处打个断点,看数据库是否有变化。

6.8.2 忘记关闭Cursor

异常中的关键字:

android.database.CursorWindowAllocationException:Cursor window allocation of 2048 kb failed

发生频率:★★★

这个异常是因为使用SQLLite时忘记释放游标导致的,内存泄漏得多了,就崩溃了。相应的解决办法就是手动关闭Cursor,如下所示:

cursor.close();

6.8.3 数据库被锁定

异常中的关键字:

android.database.sqlite.SQLiteDatabaseLockedException:database is locked

发生频率:★

当我们试图在不同的线程中创建多个连接时,就会抛出这个异常。相应的解决方案是将数据库做成一个单例。 [1]

单例固然能解决单进程操作数据库的情况,但是对于多进程App而言,还是需要ContentProvider。

6.8.4 试图再打开已经关闭的对象

异常中的关键字:

java.lang.IllegalStateException:attempt to re-open an already-closed object

发生频率:★

这个问题是上一个问题的延续。即使做成了单例,如果在不同的线程中创建多个连接,就会报当前的错误信息。

频繁地操作SQLite数据库容易产生这个崩溃。我们习惯于每执行一次数据库操作,都打开和关闭数据库各一次。这就会导致当两个线程同时操作数据库时,比如,A为读数据,B为写数据,当A读完就会关闭数据库,而B这时正在写数据,那么上述Crash就会产生了。

在实际应用中,App中的IM,因为要把聊天信息存放到本地SQLite,最容易看到这类异常,这时好的做法是,在当前聊天室,保持数据库一直处于Open状态,等退出聊天室再执行close方法。

6.8.5 文件加密了或无数据库

异常中的关键字:

SQLiteDatabaseCorruptException:file is encrypted or is not a database

发生频率:★

请注意SQLLite DB文件的版本,如果有两个DB,一个是2.8.17,另一个是3.7.7.1,那么就会出现这个异常。将其统一成一个版本即可。

此外,如果DB破损,也可能出现这种异常。当我们将App安装在SD卡上,多次插拔就会导致部分文件破损。

6.8.6 WebView中SQLLite缓存导致的崩溃

异常中的关键字:

SQLiteDiskIOException:disk I/O error……at

android.webkit.WebViewDatabase$1.run(WebViewDatabase.java:1000)

发生频率:★

注意

这个异常信息中还带有WebViewDatabase的内容,说明我们的程序使用了WebView控件的缓存技术。但是原因不详。有人说把数据库删除了就会崩溃,但我试过了,对WebView是无效的。

由此而谈到Android中WebView的缓存策略。WebView中存在着两种缓存:

·网页数据缓存,存储打开过的页面及资源。

·Html5缓存,即appcache。

缓存数据的构成如图6-4所示。

图6-4 WebView中的cache数据

WebView自带的缓存机制里面,会将url保存在webviewCashe.db中,将url内容保存在webviewCashe文件夹下,比如说图中的10d8d5cd,就是url对应的一张图片,此外html、js等文件也会存下来。

而对于databases目录下的webview.db和webviewCashe.db,都会自动生成1个名为android_metadata的表,只要创建SQLLite数据库中的表,就会自动创建这个表,表中只有一个locale字段,里面存放的是en-US或者zh-CN这样的值(是哪个值取决于Android系统),用于标示语言文化。从数据库中读取的文本是否为乱码,就取决于这个值了。

我的系统是英文的,所以我检查过locale值是en-US;而我同事的中文系统则显示zh-CN。

缓存模式有五种,见表6-1。

表6-1 缓存模式

根据以上几种模式,建议缓存策略为:判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK。

6.8.7 磁盘读写错误

异常中的关键字:

android.database.sqlite.SQLiteDiskIOException:disk I/O error(code 1802)

发生频率:★

我曾经认为,在UI线程执行dbHelper.getWritableDatabase()这句话的时候,UI线程会把数据库锁住。但是后来Bugly的“精神哥”告诉我,dbHelper只有在创建数据库、进行事务处理时才会锁住数据库。默认情况下dbHelper会缓存DB实例,执行类似于getWritableDatabase的操作是立即返回的,并不会上锁。

disk I/O error这类异常的抛出,是因为多线程修改DB,比如一个线程在写数据,另一个线程却在删除数据。

6.8.8 android_metadata表不存在

异常中的关键字:

android.database.sqlite.SQLiteException:no such table:android_metadata SQLiteOpenHelper.getReadableDatabase

发生频率:★

开发中需要连接SQLite数据库,当使用如下方法打开数据库时就会抛出上述错误:

SQLiteDatabase database = SQLiteDatabase.openDatabase(
      PATH, null, SQLiteDatabase.OPEN_READONLY);

解决办法是,将openDatabase方法中最后一个参数修改为SQLiteDatabase.

NO_LOCALIZED_COLLATORS即可。

6.8.9 android_metadata表中的locale字段

异常中的关键字:

android.database.sqlite.SQLiteException:Failed to change locale for db'/data/data/appname/databases/webview.db'to'zh_CN'.

发生频率:★

根据对6.8.8中Crash的分析,我们知道android_metadata这个表中有个locale字段。

这里要介绍的Crash发生在WebView控件生成的缓存数据库中,但是发生的概率极小(个位数)。对此,众说纷坛。甚至有美国人在StackOverFlow上说中国产的手机也报这个异常,只是不能转换为en-US而已。我只能怀疑是ROM的问题。

6.8.10 数据库或磁盘满了

异常中的关键字:

android.database.sqlite.SQLiteFullException:database or disk is full

发生频率:★

当数据库文件存放在内存中时,就和存文件或者SharedPreferences一样,会因为内存满了而报错,只是这次的错误信息更具体,会提示我们数据库/磁盘满了。

[1] 单例的实现请参见:http://zhiwei.neatooo.com/blog/detail?blog=5343818a9d4869f0310000de。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文