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

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qingfeng812/article/details/62217743 问题:Android中的SQLite数据库并发访问attempt to re-open an already-closed object 因为我们只使用一个数据库连接,Thread1和Thread2的都是由getDatabase()方法返回的相同连接。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qingfeng812/article/details/62217743

问题:Android中的SQLite数据库并发访问

  • attempt to re-open an already-closed object
    因为我们只使用一个数据库连接,Thread1和Thread2的都是由getDatabase()方法返回的相同连接。发生的什么事呢,在Thread2还在使用数据库连接时,Thread1可能已经把它给关闭了,那就是为什么你会得到崩溃异常。

    我们需要确保在没有任何一个人在使用数据库时,才去关闭它。在StackOverflow上推荐的做法是永远不要关闭数据库。Android会尊重你这种做法,但会给你如下的提示。所以我一点也不推荐这种做法。

  • android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

    每次都创建了一个新的SQLiteOpenHelper,实际上你每次都创建了一个数据库的连接。如果你在同一时间用不同的数据库连接来对同一的数据库进行写操作的话,那么其中一个会失败。

    解决方案:AtomicInteger

具体模拟代码(Android代码类似):

DataBaseManager

package com.gradle.java.thread;

import java.util.concurrent.atomic.AtomicInteger;

import com.gradle.android.utils.OkhttpUtils;

/**
 * @author Arison
 * 管理sqllite数据库
 */
public class DataBaseManager {

    private static DataBaseManager instance;

    /**
     * 保证多线程下原子操作
     */
    private  AtomicInteger i=new AtomicInteger();

    public static DataBaseManager getInstance(){
        if(instance==null){
            synchronized (DataBaseManager.class) {
                if (instance==null) {
                    OkhttpUtils.println("数据库管理类DataBaseManager--->单例初始化!");
                    instance=new DataBaseManager();
                }
            }
        }
        return instance;
    }

    /**
     * 模拟Android 数据库在多线程下的并发问题
     * @param args
     */
    public static void main(String[] args) {
        for(int i=1;i<=2000;i++){

            new Thread(new Runnable() {

                @Override
                public void run() {
                    //切记,打开,关闭数据库的操作不能直接SQLiteDatabase.getInstance().openDB();SQLiteDatabase.getInstance().closeDB();
                    //必须调用管理者单例类DataBaseManager 来调用打开和关闭操作,从而解决多线程下访问sqlite数据库的问题
                    DataBaseManager.getInstance().openDataBase();
                    DataBaseManager.getInstance().closeDataBase();
                }
            },""+i).start();
        }

    }

    public synchronized AtomicInteger openDataBase(){
        OkhttpUtils.println("+++++++++++++++++++++++++++++++++++++++++++");
        OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->open开始---->当前数据库连接数:"+i);
        if (i.incrementAndGet()==1) {
            OkhttpUtils.println("线程"+Thread.currentThread().getName()+"打开数据库之前!数据库状态:"
                    +SQLiteDatabase.getInstance().getStateDB());
            SQLiteDatabase.getInstance().openDB();//单例类模拟数据库打开操作
            OkhttpUtils.println("线程"+Thread.currentThread().getName()+"执行数据库打开操作!");
            OkhttpUtils.println("线程"+Thread.currentThread().getName()+"打开数据库之后!数据库状态:"
                    +SQLiteDatabase.getInstance().getStateDB());
        }else{
            OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->open()操作无效!新增一条数据库连接!---> 数据库状态:"
                    +SQLiteDatabase.getInstance().getStateDB());
        }
        OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->open完毕---->当前数据库连接数:"+i);
        OkhttpUtils.println("+++++++++++++++++++++++++++++++++++++++++++");
        return i;
    }

    public synchronized AtomicInteger closeDataBase(){
        OkhttpUtils.println("--------------------------------------------");
        OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->close开始---->当前数据库连接数:"+i);
        if (i.decrementAndGet()==0) {

            OkhttpUtils.println("线程"+Thread.currentThread().getName()+"关闭数据库之前!数据库状态:"
                    +SQLiteDatabase.getInstance().getStateDB());
            SQLiteDatabase.getInstance().closeDB();//单例类模拟数据库关闭操作
            OkhttpUtils.println("线程"+Thread.currentThread().getName()+"执行数据库关闭操作!");
            OkhttpUtils.println("线程"+Thread.currentThread().getName()+"关闭数据库之后!数据库状态:"
                    +SQLiteDatabase.getInstance().getStateDB());

        }else{
            OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->close()操作无效!关闭一条数据库连接!--->数据库状态:"
                    +SQLiteDatabase.getInstance().getStateDB());
        }
        OkhttpUtils.println("线程"+Thread.currentThread().getName()+"--->close完毕---->当前数据库连接数:"+i);
        OkhttpUtils.println("--------------------------------------------");
        return i;
    }

}

SQLiteDatabase

package com.gradle.java.thread;


/**
 * @author Arison
 * 模拟Android sqlite数据库
 */
public class SQLiteDatabase {

    private static SQLiteDatabase instance;

    private boolean isOpen=false;

    public static SQLiteDatabase getInstance(){
         if(instance==null){
             synchronized (SQLiteDatabase.class) {
                if (instance==null) {
                    instance=new SQLiteDatabase();
                }
            }
         }
        return instance;
    }

    public  void openDB(){
        this.isOpen=true;
    }

    public  void  closeDB(){
        this.isOpen=false;
    }

    public  boolean getStateDB(){
        return isOpen;
    }
}

运行效果图(局部):

这里写图片描述
这里写图片描述

项目代码:

github【Gradle-demo】

联系方式:

见左边栏目

相关文章
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
【7月更文挑战第28天】在Android开发中,确保UI流畅性至关重要。多线程与异步编程技术可将耗时操作移至后台,避免阻塞主线程。我们通常采用`Thread`类、`Handler`与`Looper`、`AsyncTask`及`ExecutorService`等进行多线程编程。
172 2
|
7月前
|
XML 数据库 Android开发
Android数据库的使用(增删改查)
本文介绍了一个简单的数据库操作Demo,包含创建数据库、增删改查功能。通过5个按钮分别实现创建数据库、插入数据、删除数据、更新数据和查询数据的操作。代码结构清晰,适合初学者学习Android SQLite数据库基础操作。
222 5
|
7月前
|
数据库 Android开发
Android外部数据库的引用
简介:本文介绍了在Android项目中引用外部数据库的方法。首先,将现成的数据库文件放入项目的`assets`文件夹中(需手动创建)。其次,在APP引导界面通过代码将数据库拷贝至App目录下,确保数据库可用。最后,对数据库进行增删改查等操作。关键步骤包括判断数据库是否存在、使用`AssetManager`读取数据库文件并写入App私有目录,实现外部数据库的顺利集成与使用。
|
7月前
|
数据库 Android开发 开发者
Android常用的room增删改查语句(外部数据库)
本文分享了将一个原生数据库驱动的单词APP重构为使用Room库的过程及遇到的问题,重点解决了Room中增删改查的常用语句实现。文章通过具体示例(以“forget”表为例),详细展示了如何定义实体类、Dao接口、Database类以及Repository和ViewModel的设计与实现。同时,提供了插入、删除、更新和查询数据的代码示例,包括模糊查询、分页加载等功能。此外,针对外部数据库导入问题,作者建议可通过公众号“计蒙不吃鱼”获取更多支持。此内容适合有一定Room基础的开发者深入学习。
240 0
Android常用的room增删改查语句(外部数据库)
|
10月前
|
SQL 数据建模 BI
【YashanDB 知识库】用 yasldr 配置 Bulkload 模式作单线程迁移 300G 的业务数据到分布式数据库,迁移任务频繁出错
问题描述 详细版本:YashanDB Server Enterprise Edition Release 23.2.4.100 x86_64 6db1237 影响范围: 离线数据迁移场景,影响业务数据入库。 外场将部分 NewCIS 的报表业务放到分布式数据库,验证 SQL 性能水平。 操作系统环境配置: 125G 内存 32C CPU 2T 的 HDD 磁盘 问题出现的步骤/操作: 1、部署崖山分布式数据库 1mm 1cn 3dn 单线启动 yasldr 数据迁移任务,设置 32 线程的 bulk load 模式 2、观察 yasldr.log 是否出现如下错
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
Java 关系型数据库 MySQL
如何用java的虚拟线程连接数据库
本文介绍了如何使用Java虚拟线程连接数据库,包括设置JDK版本、创建虚拟线程的方法和使用虚拟线程连接MySQL数据库的示例代码。
277 6
如何用java的虚拟线程连接数据库
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
509 15
一个Android App最少有几个线程?实现多线程的方式有哪些?