`
zhiweiofli
  • 浏览: 514724 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

【翻译】Android多线程下安全访问数据库

阅读更多
      为了记录如何线程安全地访问你的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 initialize(Context context, 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 new mDatabaseHelper.getWritableDatabase();
    }

} 

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

// In your application class
 DatabaseManager.initializeInstance(getApplicationContext());

 // 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();

        然后又导致另个崩毁

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

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

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

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

 

          本文由zhiweiofli编辑发布,转载请注明出处,谢谢。
0
0
分享到:
评论

相关推荐

    android sqlite多线程和异步加载数据库数据示例

    首先,我们来看多线程访问SQLite数据库。在Android中,主线程负责UI的更新和交互,而长时间运行的操作,如数据库查询,如果在主线程执行,可能会导致应用无响应(ANR)。因此,我们需要将这些操作放在工作线程或...

    MulThreadSQLiteTest多线程操作数据库

    1. **线程安全**:SQLite本身是线程安全的,意味着在多线程环境中,不同的线程可以直接访问同一个数据库连接,不会引发数据不一致的问题。但要注意,同一时刻,只有一个线程可以执行写操作,这是为了避免并发写入...

    Android多线程操作sqlite(Sqlite解决database locked问题)

    然而,在多线程环境中,由于并发访问数据库,可能会遇到“database locked”(数据库被锁定)的问题。本文将深入探讨如何在Android中使用多线程操作SQLite并解决数据库被锁定的问题。 首先,理解“database locked...

    Android多线程操作

    标题"Android多线程操作"和描述"Android多线程开发实例,对使用多线程的用户有一定的参考价值!"暗示我们将深入探讨Android中的线程管理以及如何在实践中有效利用。 Android系统默认运行在主线程,也被称为UI线程,...

    android多线程实例

    本实例将深入探讨Android多线程的实践应用及理论知识。 首先,我们要理解多线程的基本概念。在单线程环境中,程序按照顺序执行,而多线程则允许多个任务并行运行,提高了系统资源的利用率。Android主线程,也被称为...

    Android多线程处理[参考].pdf

    Android多线程处理是移动应用开发中的关键概念,尤其是在性能优化和用户体验提升方面。Android系统采用的是单线程模型,即主线程(也称为UI线程)主要负责处理用户界面的交互和绘图,包括按键事件、触摸事件以及屏幕...

    你想要Android数据库操作精华(安全、并发、单例等)

    在Android中,当多个线程同时访问数据库时,如果没有正确处理并发,可能会导致数据不一致或数据丢失的问题。通常,我们会使用ContentProvider、SQLiteOpenHelper的子类或者使用线程同步机制(如synchronized关键字,...

    android断点续传_多线程下载demo

    在Android开发中,断点续传和多线程下载是提高用户下载体验的重要技术。本文将深入探讨如何在Android客户端实现这些功能,并结合服务器端的配合来完成整个流程。 首先,断点续传(Resumable Download)允许用户在...

    Android中SQLite数据库查看工具

    SQLite是一个进程内的库,...SQLite 事务是完全兼容 ACID 的,允许从多个进程或线程安全访问。 SQLite 支持 SQL92(SQL2)标准的大多数查询语言的功能。 SQLite 使用 ANSI-C 编写的,并提供了简单和易于使用的 API。

    Android应用源码之Android快速框架+多线程下载框架的技术(Android + Afinal+gson).rar

    该压缩包文件主要包含了关于Android应用开发中的一些关键技术,特别是Android快速框架和多线程下载框架的实现。这里我们将深入探讨这两个主题,并结合Afinal和Gson库来理解它们在实际开发中的应用。 首先,Android...

    android多任务多线程断点下载.pdf

    总结来说,实现Android的多任务多线程断点下载,需要结合HTTP请求、数据库操作、多线程编程以及文件系统操作等技术,确保在各种网络条件下提供稳定、高效的下载体验。上述代码片段展示了如何利用SQLite数据库记录和...

    Android多线程断点下载Demo

    总的来说,这个"Android多线程断点下载Demo"涵盖了Android开发中的多个重要知识点,包括数据库操作、跨进程通信、后台服务以及多线程并发控制,是学习和理解Android高级特性的良好实践。通过深入研究和实践这个Demo...

    android多线程断点续传下载

    在Android开发中,多线程断点续传...理解这些知识点并能实际应用,开发者就能构建出功能完善的Android多线程断点续传下载应用。通过阅读和学习`MulThreadDownload`中的源代码,可以深入理解这一过程的具体实现细节。

    delphiXE7开发android直接操作SQL2008数据库

    总之,通过Delphi XE7在Android平台上开发直接操作SQL Server 2008的应用,需要综合运用网络编程、数据库访问、Web服务设计以及移动应用开发等多个领域的知识。在实践中,不断学习和优化,才能打造出高效、稳定且...

    android通过JDBC直接访问Mysql数据库.pdf

    JDBC(Java Database Connectivity)是Java中用于访问数据库的标准API,也可以被Android平台使用。以下是如何在Android应用中通过JDBC直接访问MySQL数据库的详细步骤和注意事项: 1. **导入必要的库**: 在Android...

    基于Android的socket服务器,带数据库

    6. **权限管理**:在Android中,使用Socket和访问数据库都需要相应的权限。在AndroidManifest.xml中,需要添加INTERNET和READ/WRITE_EXTERNAL_STORAGE权限,以允许应用进行网络通信和读写数据库。 7. **异常处理**...

    Android多线程断点下载源码

    这个"Android多线程断点下载源码"提供了实现这一功能的参考,适合开发者学习和实践。下面将详细介绍相关的知识点。 首先,我们要理解**多线程下载**的概念。在单线程下载中,文件数据从服务器到客户端是顺序传输的...

    android多线程断点续传

    ### Android多线程断点续传技术解析 #### 一、多线程断点续传的概念及重要性 在移动互联网应用中,特别是在资源下载方面,用户体验是非常关键的一环。对于大文件的下载,传统的单线程下载方式往往无法满足用户的...

    Android异步操作数据库

    异步操作数据库时,需要注意线程安全问题,防止多个线程同时访问数据库导致数据不一致。可以通过synchronized关键字、Lock对象或者使用数据库事务来保证数据一致性。 了解和掌握以上这些技术,你就能有效地在...

    C#解决SQlite并发异常问题的方法(使用读写锁)

    本文实例讲述了C#解决SQlite并发异常问题的方法。分享给大家供大家参考,...作者利用读写锁(ReaderWriterLock),达到了多线程安全访问的目标。 using System; using System.Collections.Generic; using System.Text;

Global site tag (gtag.js) - Google Analytics