Android中的数据库存储是直接使用了SQLite。在Android应用中创建数据库后数据库文件是存储在/data/ data/应用包名/databases/下。
在Android中使用到SQLite会涉及到以下三个类或接口,分别如下:
SQLiteOpenHelper 构造方法,一般传递一个要创建的数据库名称name参数。主要的方法如下:
onCreate 创建数据库时调用
onUpgrade 版本更新时调用
getReadableDatabase 创建或打开一个只读数据库
getWritableDatabase 创建或打开一个读写数据库
openOrCreateDatabase 打开或者创建一个数据库。主要方法如下:
insert 添加一条记录
delete 删除一条记录
query 查询记录
update 更新记录
execSQL 执行一条SQL语句
close 关闭数据库
getCount 总记录条数
isFirst 判断是否第一条记录
isLast 判断是否最后一条记录
moveToFirst 移动到第一条记录
moveToLast 移动到最后一条记录
move 移动到指定记录
moveToNext 移动到下一条记录
moveToPrevious 移动到上一条记录
getColumnIndexOrThrow根据列名称获得列索引
getInt 获得指定列索引的int类型值
getString 获得指定列索引的String类型值
注:某些方法是有重载的,可以结合docs熟悉下。
1、创建数据库只要自定义一个类继承SQLiteOpenHelper即可。在SQLiteOpenHelper的子类中至少需要实现三个方法,分别如下:
构造方法,调用父类SQLiteOpenHelper的构造函数。需要四个参数:上下文环境(例如一个Activity);数据库名称;一个可选的游标工厂(通常是null);一个正在使用的数据库版本。
onCreate方法,需要一个SQLiteDatabase对象作为参数,根据需要对这个对象填充表和初始化数据。
onUpgrade方法,需要三个参数:一个SQLiteDatabase对象,一个旧的版本号和一个新的版本号。
具体Java代码如下:
/** * 数据库操作助手类 * @author zuolongsnail */ public class AndroidSQLiteOpenHelper extends SQLiteOpenHelper { // 数据库名称 public static final String DBNAME = "android.db"; // 数据库版本 public static final int VERSION = 2; // 建表语句,大小写不敏感 private static final String CREATETABLE = "create table " + Person.TABLENAME + "(id string, name string, gender int, age int)"; public AndroidSQLiteOpenHelper(Context context) { super(context, DBNAME, null, VERSION); } // 创建表 @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATETABLE); } // 更新表 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { this.deleteDB(db); this.onCreate(db); } // 删除表 private void deleteDB(SQLiteDatabase db) { db.execSQL("drop table if exists " + Person.TABLENAME); } }
2、对数据库表进行操作,包括添加、删除、修改和查询
有两种方法可以对数据库表进行操作,分别如下:
使用execSQL方法执行SQL语句;
使用insert、delete、update和query方法,把SQL语句的一部分作为参数。
(注:查询数据库时执行SQL语句是使用SQLiteDatabase的rawQuery方法而不是execSQL)
具体代码如下:
/** * 数据库管理类 * @author zuolongsnail */ public class DatabaseManager { private AndroidSQLiteOpenHelper dbHelper; public DatabaseManager(Context context) { dbHelper = new AndroidSQLiteOpenHelper(context); } // 插入记录 public int insert(Person person) { Log.e("SQLite", "----insert----"); SQLiteDatabase db = dbHelper.getWritableDatabase(); db.beginTransaction(); try { db.execSQL("insert into " + Person.TABLENAME + " values(?, ?, ?, ?)", new Object[] { person.id, person.name, person.gender, person.age }); db.setTransactionSuccessful(); } catch (Exception e) { return 0; } finally { db.endTransaction(); } db.close(); return 1; } // 删除记录 public int delete(Person person) { Log.e("SQLite", "----delete----"); SQLiteDatabase db = dbHelper.getWritableDatabase(); db.beginTransaction(); try { db.execSQL("delete from " + Person.TABLENAME + " where id = ?", new Object[] { person.id }); db.setTransactionSuccessful(); } catch (Exception e) { return 0; } finally { db.endTransaction(); } db.close(); return 1; } // 更新记录 public int update(Person person) { Log.e("SQLite", "----update----"); SQLiteDatabase db = dbHelper.getWritableDatabase(); db.beginTransaction(); try { db.execSQL("update " + Person.TABLENAME + " set name=?, gender=?, age=? where id=?", new Object[] { person.name, person.gender, person.age, person.id }); db.setTransactionSuccessful(); } catch (Exception e) { return 0; } finally { db.endTransaction(); } db.close(); return 1; } // 查询记录 public ArrayList<Person> query(String id) { Log.e("SQLite", "----query----"); SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor; Person person; ArrayList<Person> list = new ArrayList<Person>(); // 若fileId为null或""则查询所有记录 if (id == null || id.equals("")) { cursor = db.rawQuery("select * from " + Person.TABLENAME, null); } else { cursor = db.rawQuery("select * from " + Person.TABLENAME + " where id=?", new String[] { id }); } while (cursor.moveToNext()) { person = new Person(); person.id = cursor.getString(cursor.getColumnIndex("id")); person.name = cursor.getString(cursor.getColumnIndex("name")); person.gender = cursor.getString(cursor.getColumnIndex("gender")); person.age = cursor.getInt(cursor.getColumnIndex("age")); Log.e("SQLite", person.toString()); list.add(person); } cursor.close(); db.close(); if (list.size() == 0) { Log.e("SQLite", "****表中无数据****"); } return list; } }
3、在shell中进入sqlite并使用sql语句操作数据库
启动命令行,执行adb shell命令,前提是启动了一个模拟器。进入/data/ data/应用包名/databases文件夹下,我们这里是/data/data/code.sqlite/databases(确保此应用已经建立数据库,不然包名下是没有databases目录的)。
查看databases目录下的数据库文件,这里可以看到数据库文件是android.db。
使用sqlite3命令进入sqlite来使用sql语句操作数据库,我们执行"sqlite3 android.db"命令。
使用".tables"命令查询数据库下有哪些表。可见我们这里有一张person表。
使用sql语句可以操作这张表,比如我们使用"select * from person;"可以查询到表中记录。
使用".quit"命令可以退出sqlite。
一个Android应用给用户的第一印象非常重要,除了要有好的创意和美观的界面,性能也是很关键的部分,本文讨论的就是第一次启动的速度问题。 Android应用的启动过程不能让用户等待太长时间,个人觉得最好控制在3秒之内。一般来说,内容的初始化是影响Android应用第一次启动速度的主要因素之一,尤其是创建数据库并插入一定数量的初始记录,对于这种问题,最好的办法莫过于在首次初始化时导入静态数据库。
在Android中导入静态数据库很简单,首先将准备好的静态数据库文件放到Android工程的res目录中的raw子目录下面,如果没有这个子目录的话就手动创建该目录,然后在应用的初始化阶段通过类似下面的代码将数据库文件拷贝到特定的目录下面,假设Android应用的包名是com.test,那么大部分情况下该应用默认的数据库文件位于/data/data/com.test/databases目录下面。
具体java代码:
String dbDirPath = "/data/data/com.test/databases"; File dbDir = new File(dbDirPath); if( !dbDir.exists() ) { // 如果不存在该目录则创建 dbDir.mkdir(); } // 打开静态数据库文件的输入流 InputStream is = context.getResources().openRawResource(R.raw.data); // 打开目标数据库文件的输出流 FileOutputStream os = new FileOutputStream(dbDirPath+"/data.db"); byte[] buffer = new byte[1024]; int count = 0; // 将静态数据库文件拷贝到目的地 while ((count = is.read(buffer)) > 0) { os.write(buffer, 0, count); } is.close(); os.close();
不过,这种方式是假定所有Android设备的应用安装目录是相同的,而且数据库文件的目录都是/data/data/包名/databases,但是Android的文档中并没有明确规定所有设备具有此种目录结构,所以将静态数据库文件拷贝到一个事先定死的目录的做法还是有一定危险性的。更好的做法是使用Android系统提供的API去解决这个问题,总之,我们要避免的就是使用固定目录,下面是更好的拷贝过程:
// 打开静态数据库文件的输入流 InputStream is = context.getResources().openRawResource(R.raw.data); // 通过Context类来打开目标数据库文件的输出流,这样可以避免将路径写死。 FileOutputStream os = context.openFileInput("data.db"); byte[] buffer = new byte[1024]; int count = 0; // 将静态数据库文件拷贝到目的地 while ((count = is.read(buffer)) > 0) { os.write(buffer, 0, count); } is.close(); os.close();
最终的数据库文件将位于/data/data/com.data/files目录下,需要注意的是,使用Context类的openOrCreateDatabase方法或者SQLiteOpenHelper 工具类时,不能再传递数据库的名称作为参数,而是要把数据库文件的全路经传递给它们。