关闭

Android多线程下安全访问数据库

2749人阅读 评论(0) 收藏 举报
分类:
为了记录如何线程安全地访问你的Android数据库实例,我写下了这篇小小札记。文章中引用的项目代码请点击这里

假设你已编写了自己的 SQLiteOpenHelper。

public class DatabaseHelper extends SQLiteOpenHelper { ... }

现在你想在不同的线程中对数据库进行写数据操作:

 // Thread 1
 Context context = getApplicationContext();
 DatabaseHelper helper = new DatabaseHelper(context);
 SQLiteDatabase database = helper.getWritableDatabase();
 database.insert(…);
 database.close();

 // Thread 2
 Context context = getApplicationContext();
 DatabaseHelper helper = new DatabaseHelper(context);
 SQLiteDatabase database = helper.getWritableDatabase();
 database.insert(…);
 database.close();

然后在你的Logcat中将输出类似下面的日志信息,而你的写数据操作将会无效。


android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
       上面问题的出现,源于你每创建一个 SQLiteOpenHelper  对象时,实际上也是在新建一个数据库连接。如果你尝试通过多个连接同时对数据库进行写数据操作,其一定会失败。


       为确保我们能在多线程中安全地操作数据库,我们需要保证只有一个数据库连接被占用。


       我们先编写一个负责管理单个 SQLiteOpenHelper 对象的单例 DatabaseManager 。 

public class DatabaseManager {

    private static DatabaseManager instance;
    private static SQLiteOpenHelper mDatabaseHelper;

    public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
        if (instance == null) {
            instance = new DatabaseManager();
            mDatabaseHelper = helper;
        }
    }

    public static synchronized DatabaseManager getInstance() {
        if (instance == null) {
            throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
                    " is not initialized, call initialize(..) method first.");
        }

        return instance;
    }

    public synchronized SQLiteDatabase getDatabase() {
        return mDatabaseHelper.getWritableDatabase();
    }

}

为了能在多线程中进行写数据操作,我们得修改一下代码,具体如下: 

 // In your application class
 DatabaseManager.initializeInstance(new DatabaseHelper());

 // Thread 1
 DatabaseManager manager = DatabaseManager.getInstance();
 SQLiteDatabase database = manager.getDatabase()
 database.insert(…);
 database.close();

 // Thread 2
 DatabaseManager manager = DatabaseManager.getInstance();
 SQLiteDatabase database = manager.getDatabase()
 database.insert(…);
 database.close();

这样会导致另一个crash:

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

 既然我们只有一个数据库连接,Thread1 和 Thread2 对方法 getDatabase() 的调用就会取得一样的 SQLiteDatabase 对象实例。之后的事情就是,当 Thread1 尝试管理数据库连接时,Thread2 却仍然在使用该数据库连接。这也就是导致 IllegalStateException 崩毁的原因。


        因此我们只能在确保数据库没有再被占用的情况下,才去关闭它。在 stackoveflow 上有一些讨论推荐“永不关闭”你的 SQLiteDatabase 。  如果你这样做,你的logcat将会出现以下的信息,因此我不认为这是一个好主意。

Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed

示例:

public class DatabaseManager {

    private AtomicInteger mOpenCounter = new AtomicInteger();

    private static DatabaseManager instance;
    private static SQLiteOpenHelper mDatabaseHelper;
    private SQLiteDatabase mDatabase;

    public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
        if (instance == null) {
            instance = new DatabaseManager();
            mDatabaseHelper = helper;
        }
    }

    public static synchronized DatabaseManager getInstance() {
        if (instance == null) {
            throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
                    " is not initialized, call initializeInstance(..) method first.");
        }

        return instance;
    }

    public synchronized SQLiteDatabase openDatabase() {
        if(mOpenCounter.incrementAndGet() == 1) {
            // Opening new database
            mDatabase = mDatabaseHelper.getWritableDatabase();
        }
        return mDatabase;
    }

    public synchronized void closeDatabase() {
        if(mOpenCounter.decrementAndGet() == 0) {
            // Closing database
            mDatabase.close();

        }
    }
}
 然后你可以怎样子去调用它:

   SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();
    database.insert(...);
    // database.close(); Don't close it directly!
    DatabaseManager.getInstance().closeDatabase(); // correct way


以后每当你需要使用数据库连接,你可以通过调用类 DatabaseManager 的方法openDatabase()。在方法里面,内置一个标志数据库被打开多少次的计数器。如果计数为1,代表我们需要打开一个新的数据库连接,否则,数据库连接已经存在。


在方法 closeDatabase() 中,情况也一样。每次我们调用 closeDatabase() 方法,计数器都会递减,直到计数为0,我们就需要关闭数据库连接了。


         提示: 你应该使用 AtomicInteger 来处理并发的情况


         现在你可以线程安全地使用你的数据库连接了。


原文连接:https://github.com/dmytrodanylyk/dmytrodanylyk/blob/gh-pages/articles/Concurrent%20Database%20Access.md

0
0
查看评论

Android数据库并发操作解决思路

一种Android数据库的并发操作解决思路
  • lizhengwei1989
  • lizhengwei1989
  • 2017-04-05 17:07
  • 617

Android多线程下安全访问数据库

为了记录如何线程安全地访问你的Android数据库实例,我写下了这篇小小札记。文章中引用的项目代码请点击这里       假设你已编写了自己的 SQLiteOpenHelper。 public class DatabaseHelper extends SQL...
  • Sky_Monkey
  • Sky_Monkey
  • 2014-04-16 08:55
  • 11996

Android下数据库线程安全问题

一.概述在实际应用中,当同时有多个线程一起访问数据库时,可能会发生一些异常情况,我们先来看看会发生什么异常:假设我们已经定义好了 SQLiteOpenHelperpublic class DatabaseHelper extends SQLiteOpenHelper { ... }现在我们使用不同的...
  • Small_Lee
  • Small_Lee
  • 2016-02-17 11:02
  • 773

Android 里面的 Handler 究竟是如何保证线程安全的?

关键在于handler对应的MessageQueue是线程安全的,请看MessageQueue的message入队方法,看到synchronized关键字没有,就是她了: final boolean enqueueMessage(Message msg, long when) { ...
  • lxyyzm
  • lxyyzm
  • 2017-01-23 20:54
  • 818

android 多线程数据库读写分析与优化

时间 2013-08-04 10:43:21 CSDN博客 原文  http://blog.csdn.net/lize1988/article/details/9700723 最新需要给软件做数据库读写方面的优化,之前无论读写,都是用一个 SQLiteOpenHelper...
  • hehui1860
  • hehui1860
  • 2015-03-05 09:23
  • 3757

Android中多线程访问数据库、DBHelper

DBHelper: package com.ruru.sqlitedemo; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite....
  • rururu2211785113
  • rururu2211785113
  • 2017-02-06 17:38
  • 777

[Android][转]Android多线程下安全访问数据库

转自:http://zhiweiofli.iteye.com/blog/2041977 为了记录如何线程安全地访问你的Android数据库实例,我写下了这篇小小札记。文章中引用的项目代码请点击这里       假设你已编写了自己的 SQLiteOpe...
  • andrewyls
  • andrewyls
  • 2015-08-24 23:12
  • 351

【Android】SQLite实例(多线程下安全访问数据库)

1.前言  最近有个刚接触开发的朋友在使用SQLite进行数据存储时出现各种问题,所以特意写了个DEMO发给他用于参考,以规避掉那些容易发生的突发状况。以下先付出代码部分,然后标出对方在阅读Demo时产生的疑问。2.代码部分1.DBHelper类package com.example.sqlitet...
  • Crazy_zihao
  • Crazy_zihao
  • 2015-12-22 16:29
  • 937

Android多线程下操作sqlite数据库解决方案

问题:Android中的SQLite数据库并发访问 attempt to re-open an already-closed object 因为我们只使用一个数据库连接,Thread1和Thread2的都是由getDatabase()方法返回的相同连接。发生的什么事呢,在Thread2还在使用数据...
  • qingfeng812
  • qingfeng812
  • 2017-03-15 11:19
  • 817

关于Android SQLite3多线程并发问题,学习笔记。

最近有看到过Sqlite3的相关文章,在这做一下学习笔记,
  • RockCode_li
  • RockCode_li
  • 2014-09-03 10:54
  • 6106
    个人资料
    • 访问:11091499次
    • 积分:77345
    • 等级:
    • 排名:第24名
    • 原创:506篇
    • 转载:912篇
    • 译文:4篇
    • 评论:2263条
    打赏
    如果您认为本博客不错,读后觉得有收获,不妨打赏赞助我一下,让我有动力继续写出高质量的博客。



    赠人玫瑰,手有余香。分享技术,传递快乐。

    有心课堂,传递的不仅仅是技术!

    QQ交流群:250468947

    有心课堂会员,请加入VIP QQ交流群:213725333

    github
    我的视频
    博客专栏
    最新评论