• Date:2015-4-23
  • Tag:android; sqlite; 事务; 多线程
  • Author:踏雪
  • Email:shuwoom.wgc@gmail.com

一、 Sqlite的基本操作—-增删改查

1、创建或打开数据库

SQLiteDatabase db = openOrCreateDatabase(DATABASE_NAME, MODE_PRIVATE, null);

2、创建或删除table

db.execSQL("DROP TABLE IF EXISTS " + Student.TABLE_NAME);        db.execSQL("CREATE TABLE " + Student.TABLE_NAME         + "("         + Student.ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "        + Student.NAME + " VARCHAR, "        + Student.SCORE + " VARCHAR "        + ");");

3、插入数据

if(db.delete(Student.TABLE_NAME, Student.NAME + "=?", new String[]{"wgc"}) == 0) {            Log.v("base_operation", "删除失败!");        } else {            Log.v("base_operation", "删除成功!");        }

4、修改数据

values.put(Student.NAME, "wgc2");        values.put(Student.SCORE, "99");        int rows = db.update(Student.TABLE_NAME, values, Student.NAME + "=?", new String[]{"wgc"});

5、查询数据

Cursor cursor = db.rawQuery(                "select * from "                 + Student.TABLE_NAME                + " where " + Student.NAME + "=?"                 , new String[] {"wgc2"});        Student student = new Student();        while(cursor.moveToNext()){            student.id = cursor.getString(cursor.getColumnIndex(Student.ID));            student.name = cursor.getString(cursor.getColumnIndex(Student.NAME));            student.score = cursor.getString(cursor.getColumnIndex(Student.SCORE));            Log.v("base_operation", student.toString());        }

二、Sqlite的事务属性

应用程序初始化时如果单独使用for+Insert的方式插入大量的数据,会导致应用响应慢。因为SQLite的数据库本质上是一个磁盘上的文件,所以一些的数据库操作其实就是对文件的操作。而频繁的文件操作是一个很耗时的过程,极大地影响了数据库的存储速度。

sqlite插入数据的时候默认一条语句就是一个事务,有多少条数据就有多少次磁盘操作。初始化1000条数据,也就要1000次磁盘的操作,将会重复的打开关闭数据库1000次,所以速度回满,而且还不能保证所有的数据都能同时插入。

解决方法是使用事务处理。
(1)db.beginTransaction();//手动设置开始事务

(2)db.setTransactionSuccessful();//设置事务处理成功,不设置会自动回滚不提交,在setTransactionSuccessful和endTransaction之间不进行任何数据库操作

(3) db.endTransaction();//处理完成

这样SQLite将把全部要执行的SQL语句先缓存到内存当中,然后等到commit的时候一次性写入到数据库。这样数据库文件只被打开关闭了一次,效率就大大提高。

1、未使用事务属性,插入1000条数据

long startMillis = System.currentTimeMillis();for(int i = 0; i < 1000; i++) {    ContentValues val = new ContentValues();    val.put(Student.NAME, "wgc"+i);    val.put(Student.SCORE, "99");    db.insert(Student.TABLE_NAME, null, values);}Log.v("transaction", "未开始事务属性之前总共耗时:" + (System.currentTimeMillis() - startMillis) + "");

2、使用事务属性,插入1000天数据

db.beginTransaction();startMillis = System.currentTimeMillis();for(int i = 0; i < 1000; i++) {    ContentValues val = new ContentValues();    val.put(Student.NAME, "wgc"+i);    val.put(Student.SCORE, "99");    db.insert(Student.TABLE_NAME, null, val);}db.setTransactionSuccessful();db.endTransaction();Log.v("transaction", "使用事务属性之后总共耗时:" + (System.currentTimeMillis() - startMillis) + "");

下面是一个对比结果,可以明显看出,使用事务属性后,效率大大提高了。

三、Sqlite的多线程操作问题

我们可以得知SQLite是文件级别的锁:多个县城可以同时读,但是同时只能只有一个线程写。如果多线程同时读写(这里指不同的线程使用的是不同的SQLiteDatabase实例),后面就会遇到android.database.sqlite.SQLiteException:database is locked这样的异常。

解决办法是保持一个sqlite连接,保持单个SQLiteDatabase实例。同时对所有数据库操作方法添加synchroned关键字。

1、 下面看一个多线程同时进行写操作的后果:

class WriteThread extends Thread{        @Override        public void run() {            SQLiteDatabase db = openOrCreateDatabase("demo.db", MODE_PRIVATE, null);            db.beginTransaction();            for(int i = 0; i < 8000; i++) {                ContentValues val = new ContentValues();                val.put(Student.NAME, "wgc"+i);                val.put(Student.SCORE, "99");                db.insert(Student.TABLE_NAME, null, val);            }            db.setTransactionSuccessful();            db.endTransaction();            Log.v("multiThread2", "Thread ID:"+getId());        }    }…………………..    //开启5个线程执行写操作        for(int i = 0; i < 5; i++) {            new WriteThread().start();        }

运行程序,会见到程序奔溃,并报错,错误提示如下:

2、 下面看一个多线程同时进行读的后果:

class ReadThread extends Thread {        @Override        public void run() {            SQLiteDatabase db = openOrCreateDatabase("demo.db", MODE_PRIVATE,                    null);            for (int i = 0; i < 8000; i++) {                StringBuilder builder = new StringBuilder();                builder.append("select * from ");                builder.append(Student.TABLE_NAME);                builder.append(" where " + Student.NAME + "=?");                Cursor cursor = db.rawQuery(builder.toString(),                        new String[] { "wgc2" });                Student student = new Student();                if (cursor.moveToNext()) {                    student.id = cursor.getString(cursor                            .getColumnIndex(Student.ID));                    student.name = cursor.getString(cursor                            .getColumnIndex(Student.NAME));                    student.score = cursor.getString(cursor                            .getColumnIndex(Student.SCORE));                }                Log.v("multiThread",                        "Thread ID:" + getId() + "===" + student.toString());            }        }    }…………………..//开启5个线程执行读操作for(int i = 0; i < 5; i++) {    new ReadThread().start();}

程序运行后没有任何异常,也没有报错。

下面给出解决多线程同时写的解决方法:
(1) 首先是使用单例模式,始终保持一个连接,使用一个SQLiteDatabase实例。

public class SQLiteDatabaseManager {    private static SQLiteDatabase db = null;    public static SQLiteDatabase getInstance(Context context, String name, int mode) {        if(db == null) {            db = context.openOrCreateDatabase(name, mode, null);        }         return db;    }}

通过SQLiteDatabaseManager的getInstance方法就能保证始终使用同一个SQLiteDatabase实例。给write函数添加synchronized关键字能保证该方法统一时刻不会被同时调用。

然后我们队对第一次多线程写操作的代码做一些修改:

private synchronized void write() {        SQLiteDatabase db = SQLiteDatabaseManager.getInstance(                getApplicationContext(), "demo.db", MODE_PRIVATE);        db.beginTransaction();        for (int i = 0; i < 8000; i++) {            ContentValues val = new ContentValues();            val.put(Student.NAME, "wgc" + i);            val.put(Student.SCORE, "99");            db.insert(Student.TABLE_NAME, null, val);        }        db.setTransactionSuccessful();        db.endTransaction();    }        class WriteThread extends Thread {        @Override        public void run() {            write();        }    }
private synchronized void read() {        SQLiteDatabase db = SQLiteDatabaseManager.getInstance(                getApplicationContext(), "demo.db", MODE_PRIVATE);        for (int i = 0; i < 8000; i++) {            StringBuilder builder = new StringBuilder();            builder.append("select * from ");            builder.append(Student.TABLE_NAME);            builder.append(" where " + Student.NAME + "=?");            Cursor cursor = db.rawQuery(builder.toString(),                    new String[] { "wgc2" });            Student student = new Student();            if (cursor.moveToNext()) {                student.id = cursor                        .getString(cursor.getColumnIndex(Student.ID));                student.name = cursor.getString(cursor                        .getColumnIndex(Student.NAME));                student.score = cursor.getString(cursor                        .getColumnIndex(Student.SCORE));            }        }    }class ReadThread extends Thread {        @Override        public void run() {            read();        }    }

我们再次进行多线程的读写操作:

// 开启5个线程执行读操作for (int i = 0; i < 5; i++) {    new ReadThread().start();}// 开启5个线程执行写操作for (int i = 0; i < 5; i++) {    new WriteThread().start();}

这次程序就没有异常,也没有报错了。

源码下载地址:
http://download.csdn.net/detail/wen294299195/9308083

更多相关文章

  1. 【Android学习系列】android Content Provider 应用步骤
  2. android listview的创建及行删除操作
  3. Android(安卓)intent数据传递
  4. Android(安卓)9.0 Launcher启动详解
  5. android百度地图(二)之定位
  6. Android中的数据库——SQLite
  7. 你真的会用Android的Dialog吗?由一个Exception想到的
  8. Sqlite3 增删改查操作实例
  9. Android(安卓)ListView实现通讯录的例子

随机推荐

  1. Android(安卓)OpenGL开发目录
  2. 2.GridView
  3. Android命令大全
  4. [转]Android(安卓)源代码结构
  5. Android(安卓)后台Service : 向服务器发
  6. Android(安卓)xml manifest属性详解
  7. Android(安卓)Vertical TextView 文字竖
  8. Android:实现无标题的两种方法
  9. Android(安卓)android:gravity属性介绍及
  10. Android(安卓)android下的短信发送器