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

参考http://blog.csdn.net/sdsxleon/article/details/18259973  很好

https://github.com/2point0/Android-Database-Locking-Collisions-Example 示例

http://www.eoeandroid.com/forum.php?mod=viewthread&tid=333473

http://bbs.51cto.com/thread-990260-1.html

用事务,速度会很会

方案1:

SQLite实质上是将数据写入一个文件,通常情况下,在应用的包名下面都能找到xxx.db的文件,拥有root权限的手机,可以通过adb shell,看到data/data/packagename/databases/xxx.db这样的文件。

我们可以得知SQLite是文件级别的锁:多个线程可以同时读,但是同时只能有一个线程写。Android提供了SqliteOpenHelper类,加入Java的锁机制以便调用。

如果多线程同时读写(这里的指不同的线程用使用的是不同的Helper实例),后面的就会遇到android.database.sqlite.SQLiteException: database is locked这样的异常。
对于这样的问题,解决的办法就是keep single sqlite connection,保持单个SqliteOpenHelper实例,同时对所有数据库操作的方法添加synchronized关键字。

完美解决sqlite的 database locked 或者是 error 5: database locked 问题

/**
 * @FileName : DatabaseHelper.java
 * @ProjectName : SqlitePractice
 * @PakageName : com.sqlitepractice.database
 * @Author : Brant
 * @CreateDate : 2012-12-16
 */
package com.sqlitepractice.database;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import android.util.Log;

/**
 * @Module : 隶属模块名
 * @Comments : 描述
 * @Author : Brant
 * @CreateDate : 2012-12-16
 * @ModifiedBy : Brant
 * @ModifiedDate: 2012-12-16
 * @Modified:
 * 2012-12-16: 实现基本功能
 */
public class DatabaseHelper extends SQLiteOpenHelper {
	public static final String TAG = "DatabaseHelper";
	private static final String DB_NAME = "practice.db";
	private final static String DB_TABLE_APP = "app";
	private static final int DB_VERSION = 1;
	private static final String DB_CREATE_TABLE_APP = "CREATE TABLE " + DB_TABLE_APP + "(" + APP_COLUMNS._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + APP_COLUMNS.PACKAGE_NAME
														+ " NTEXT NOT NULL," + APP_COLUMNS.APP_NAME + " NTEXT NOT NULL," + APP_COLUMNS.APP_NAME_PINYIN + " NTEXT,"
														+ APP_COLUMNS.CLASS_NAME + " NTEXT NOT NULL);";

	private static DatabaseHelper mInstance;

	protected DatabaseHelper(Context context) {
		super(context, DB_NAME, null, DB_VERSION);
	}

	public synchronized static DatabaseHelper getInstance(Context context) {
		if (mInstance == null) {
			mInstance = new DatabaseHelper(context);
		}
		return mInstance;
	}

	public synchronized static void destoryInstance() {
		if (mInstance != null) {
			mInstance.close();
		}
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(DB_CREATE_TABLE_APP);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub

	}

	public synchronized int getCount() {
		Log.d(TAG, "getCount");
		int count = -1;
		Cursor c = getReadableDatabase().query(DB_TABLE_APP, null, null, null, null, null, null);
		if (c.moveToFirst()) {
			count = c.getCount();
		}
		c.close();
		c = null;
		return count;
	}

	public synchronized void insert(String packageName, String appLabel, String pinyin, String className) {
		Log.d(TAG, "insert");
		ContentValues values = new ContentValues(4);
		values.put(APP_COLUMNS.PACKAGE_NAME, packageName);
		values.put(APP_COLUMNS.APP_NAME, appLabel);
		values.put(APP_COLUMNS.APP_NAME_PINYIN, pinyin);
		values.put(APP_COLUMNS.CLASS_NAME, className);
		getWritableDatabase().insert(DB_TABLE_APP, null, values);
	}

	public static class APP_COLUMNS implements BaseColumns {
		public static final String PACKAGE_NAME = "package_name";
		public static final String APP_NAME = "app_name";
		public static final String APP_NAME_PINYIN = "app_name_pinyin";
		public static final String CLASS_NAME = "class_name";

		public static final int ID_INDEX = 0;
		public static final int PACKAGE_NAME_INDEX = 1;
		public static final int APP_NAME_INDEX = 2;
		public static final int APP_NAME_PINYIN_INDEX = 3;
		public static final int CLASS_NAME_INDEX = 4;
	}

}

  

package com.sqlitepractice;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;

import com.sqlitepractice.database.DatabaseHelper;

public class MainActivity extends Activity {
	private static final String TAG = "MainActivity";
	private static int sThreadCounter = 0;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		final int threadCount = 8;
		final List<Thread> allThreads = new ArrayList<Thread>(threadCount);
		DatabaseHelper helper = DatabaseHelper.getInstance(this);
		for (int i = 0; i < threadCount; i++) {
			allThreads.add(new DbInsertThread(helper, 50, sThreadCounter++));
		}
		for (int i = 0; i < threadCount; i++) {
			allThreads.add(new FastSelectThread(helper, sThreadCounter++, 50));
		}

		for (Thread thread : allThreads) {
			thread.start();
		}

		// Wait for all threads to complete before running
		for (Thread thread : allThreads) {
			try {
				thread.join();
				Log.i(thread.getName(), "collected");
			} catch (InterruptedException e) {
				Log.e(TAG, "Interrupted", e);
			}
		}

		Log.i(TAG, "All threads finished!");
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}

	class DbInsertThread extends Thread {
		private final String TAG = DbInsertThread.class.getSimpleName();

		private final DatabaseHelper mDbHelper;
		private int mRunCount;

		DbInsertThread(DatabaseHelper helper, int runCount, int id) {
			setName(String.format("%1$s-%2$d", TAG, id));

			mDbHelper = helper;
			mRunCount = runCount;
			Log.i(getName(), helper.toString());
		}

		@Override
		public void run() {
			for (int i = 0; i < mRunCount; i++) {
				try {
					mDbHelper.insert("com.xx.xxxx", "测试", "ceshi", "MainActivity");
				} catch (Exception e) {
					Log.e(getName(), "Insert failed!!!, stopping writes", e);
					break;
				}
			}

			Log.i(getName(), "finished!");

		}
	}

	class FastSelectThread extends Thread {
		private final String TAG = FastSelectThread.class.getSimpleName();

		private final DatabaseHelper mHelper;
		private final int mCount;

		FastSelectThread(DatabaseHelper helper, int id, int selectCount) {
			setName(String.format("%1$s-%2$d", TAG, id));

			mHelper = helper;
			mCount = selectCount > 0 ? selectCount : 50;

			Log.i(getName(), helper.toString());
		}

		@Override
		public void run() {
			int count = 0;
			while (count < mCount) {
				mHelper.getCount();

				Log.i(getName(), "start wait");
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				Log.i(getName(), "end wait");

				count++;
			}

			Log.i(getName(), "finished!");
		}
	}

}

  

时间: 2024-11-01 06:30:34

Android多线程操作sqlite(Sqlite解决database locked问题)的相关文章

Android多线程操作——线程池管理综述

题记-- 难过了,悄悄走一走: 伤心了,默默睡一觉: 优雅不是训练出来的,而是一种阅历: 淡然不是伪装出来的,而是一种沉淀: 时间飞逝,老去的只是我们的容颜: 时间仿佛一颗灵魂,越来越动人: 1.简述: 在多线程的世界中,是那么的神奇 与 高效以及合理: 2.创建线程池实例 官方推荐使用Executors类工厂方法来创建线程池管理,Executors类是官方提供的一个工厂类,里面封装了好多功能不一样的线程池,从而使得我们创建线程池非常的简单:                    3.使用线程池

sqlite:多线程操作数据库“database is locked”解决方法(二)

上一篇博客<sqlite:多线程操作数据库“database is locked”解决方法>通过注册延时函数的方法来处理数据库被锁的问题.此方法固然能解决问题,但是在多个线程向数据库写入大量数据的情况下,延时会拖慢进度. 想出方法二: 1. 创建一个链表,链接如下格式的结构体,线程1,线程2,线程3......不直接改写数据库,而是把sql语句插入链表中: typedef struct { uint8_t *buf; uint32_t len; } sqlItem_t; 2. 创建一个独立的线

解决SQLite中的 database is locked

前些时候,同事在站点服务端使用SQlite存储一些临时数据,但是在多人并发的时候Sqlite会抛出异常:The database file is locked , database is locked,而且这个是在客户生产环境下提示出来的,开发环境很难重现,同事实在没辙,竟然想发动所有研发同事通过操作软件重现问题,我只能呵呵了.既然是Sqlite的原因,直接写个小程序测试下sqlite不就行了,而且就算重现了,难不成要改Sqlite源码... Sqlite的特点: 简单(simple):SQLi

Android开发之通过Android的API对sqlite数据库的操作以及数据库事务的练习

一.通过Android的API对sqlite数据库的操作 通过已有的ContentValues类,实例一个对象value来调用其中内部的方法来操作sqlite数据库 代码: package com.example.databasedemo; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sql

无废话Android之android下junit测试框架配置、保存文件到手机内存、android下文件访问的权限、保存文件到SD卡、获取SD卡大小、使用SharedPreferences进行数据存储、使用Pull解析器操作XML文件、android下操作sqlite数据库和事务(2)

1.android下junit测试框架配置 单元测试需要在手机中进行安装测试 (1).在清单文件中manifest节点下配置如下节点 <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.example.demo1" /> 上面targetPackage指定的包要和应用的package相同. (2)在清单文件中ap

通过adb shell操作android真机的SQLite数据库

要通过命令行直接操作android真机上的SQLite数据库,可以直接通过adb shell来完成,不过,前提是必须获得root权限. 另外,android系统其实就是linux的shell,这个应该大家都知道,不过一般情况下,在/system/xbin/目录下, 没有sqlite3命令,需要手动copy一个进去,通常情况下,需要两个文件 sqlite3.libncurses.so 解压后两个文件都有了,比如解压到:~/Downloads/sqlite3/ 然后就是通过下面的这些个命令,一步一步

[Android] Android 使用 Greendao 操作 db sqlite(2)-- 封装DaoUtils类1

[Android] Android 使用 Greendao 操作 db sqlite(2)-- 封装DaoUtils类 原文地址:https://www.cnblogs.com/wukong1688/p/10725092.html

使用FMDB多线程访问数据库,及database is locked的问题

今天终于解决了多线程同时访问数据库时,报数据库锁定的问题,错误信息是: Unknown error finalizing or resetting statement (5: database is locked) 最后通过FMDatabaseQueue解决了这个问题,本文总结一下: FMDatabase不能多线程使用同一个实例 多线程访问数据库,不能使用同一个FMDatabase的实例,否则会发生异常.如果线程使用单独的FMDatabase实例是允许的,但是同样有可能发生database is

Android开发之利用SQLite进行数据存储

Android开发之利用SQLite进行数据存储 Android开发之利用SQLite进行数据存储 SQLite数据库简单介绍 Android中怎样使用SQLite 1 创建SQLiteOpenHelper对象并创建表 2 通过SQLiteDatabase对象运行增删改查操作 3 SQLiteDatabase之事务transaction 1.SQLite数据库简单介绍 SQLite.是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包括在一个相对小的C库中.它是D.RichardHip