Android原生支持sqlite数据库操做,sqlite时轻量级关系型数据库,支持标准sql语句。Android对sqlite进行良好的接口封装来避免sql注入等安全问题。android
本文解决的问题:
一、Android App内如何建立数据库
二、Android App内建立数据库如何自定义文件路径
三、Android App内获取数据库流程解析
四、无Context模式使用数据库,可在uiautomator1.0测试框架和其余app_process启动的进程内使用数据库。sql
Android应用内使用数据库须要借助于SQLiteOpenHelper类实现对数据库的操做,使用数据库经过如下几步:
一、建立私有类集成自SQLiteOpenHelper方法,并覆写onCreate、onUpdate方法实现对数据库升级降级操做。
二、获取SQLiteOpenHelper对象实例。
三、获取只读、读写类型数据库SQLiteDatabase对象实例(getReadableDatabase()/getWritableDatabase()),当数据库升级或建立时才会调用onCreate、onUpdate方法。
四、使用SQLiteDatabase接口实现数据库操做(增删改查)数据库
//一、建立私有SQLiteOpenHelper类 class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context, String dbName, int version) { super(context, dbName, null, version); LogUtil.print("DatabaseHelper dbName[%s] version[%d]",dbName+"",version); } @Override public void onCreate(SQLiteDatabase db) { //数据库建立时调用 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //数据库升级时调用 } } //二、获取SQLiteOpenHelper对象实例。 int dbVersion=0; DatabaseHelper databaseHelper=new (context,"test.db",dbVersion); //三、获取只读、读写类型数据库SQLiteDatabase对象实例 SQLiteDatabase db = databaseHelper.getWritableDatabase(); //四、使用SQLiteDatabase接口实现数据库操做(增删改查) String sql = "select * from testTable where id=?" Cursor cursor = db.rawQuery(sql, new String[]{"1111"});
一、SQLiteOpenHelper构造方法仅初始化参数:版本号必须大于0缓存
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version, int minimumSupportedVersion, DatabaseErrorHandler errorHandler) { if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version); mContext = context; mName = name; mFactory = factory; mNewVersion = version; mErrorHandler = errorHandler; mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion); }
二、获取数据库实例:真正建立数据库文件安全
private SQLiteDatabase getDatabaseLocked(boolean writable) { if (mDatabase != null) { if (!mDatabase.isOpen()) { // Darn! The user closed the database by calling mDatabase.close(). mDatabase = null; //若是获取只读数据库且缓存了可写数据库则直接返回 } else if (!writable || !mDatabase.isReadOnly()) { // The database is already open for business. return mDatabase; } } if (mIsInitializing) { throw new IllegalStateException("getDatabase called recursively"); } SQLiteDatabase db = mDatabase; try { mIsInitializing = true; if (db != null) { //若是获取可写数据库,复用缓存的只读数据库 if (writable && db.isReadOnly()) { db.reopenReadWrite(); } } else if (mName == null) { db = SQLiteDatabase.create(null); } else { try { //获取只读类型数据库实例 if (DEBUG_STRICT_READONLY && !writable) { final String path = mContext.getDatabasePath(mName).getPath(); db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } else { //获取可读写数据库实例 db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ? Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0, mFactory, mErrorHandler); } } catch (SQLiteException ex) { //获取数据库异常时,若是获取可写数据库,则直接抛出 if (writable) { throw ex; } Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", ex); //获取数据库异常时,若是获取只读数据库,重试获取只读类型数据库。 final String path = mContext.getDatabasePath(mName).getPath(); db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } } onConfigure(db); final int version = db.getVersion(); //数据库升级处理逻辑 if (version != mNewVersion) { if (db.isReadOnly()) { throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " + mNewVersion + ": " + mName); } //程序运行时更新数据库:使用缓存数据库时走此逻辑,从新加载数据库 if (version > 0 && version < mMinimumSupportedVersion) { File databaseFile = new File(db.getPath()); onBeforeDelete(db); db.close(); if (SQLiteDatabase.deleteDatabase(databaseFile)) { mIsInitializing = false; return getDatabaseLocked(writable); } else { throw new IllegalStateException("Unable to delete obsolete database " + mName + " with version " + version); } } else { //事务更新(降级/升级)数据库 db.beginTransaction(); try { if (version == 0) { onCreate(db); } else { if (version > mNewVersion) { onDowngrade(db, version, mNewVersion); } else { onUpgrade(db, version, mNewVersion); } } db.setVersion(mNewVersion); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } } onOpen(db); if (db.isReadOnly()) { Log.w(TAG, "Opened " + mName + " in read-only mode"); } mDatabase = db; return db; } finally { mIsInitializing = false; if (db != null && db != mDatabase) { db.close(); } } }
private SQLiteDatabase getDatabaseLocked(boolean writable) { if (mDatabase != null) { if (!mDatabase.isOpen()) { // Darn! The user closed the database by calling mDatabase.close(). mDatabase = null; } else if (!writable || !mDatabase.isReadOnly()) { //若是获取只读数据库且缓存了可写数据库则直接返回 // The database is already open for business. return mDatabase; } } if (mIsInitializing) { throw new IllegalStateException("getDatabase called recursively"); } SQLiteDatabase db = mDatabase; try { mIsInitializing = true; if (db != null) { //若是缓存的数据库为只读类型的,而且当次获取可写类型数据库,则覆用缓存数据库 if (writable && db.isReadOnly()) { db.reopenReadWrite(); } } else if (mName == null) { db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build()); } else { //获取数据库的存储路径:注-仅次一处使用到了context对象 final File filePath = mContext.getDatabasePath(mName); SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build(); try { //打开数据库实例对象 db = SQLiteDatabase.openDatabase(filePath, params); // Keep pre-O-MR1 behavior by resetting file permissions to setFilePermissionsForDb(filePath.getPath()); } catch (SQLException ex) { if (writable) { throw ex; } Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", ex); params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build(); db = SQLiteDatabase.openDatabase(filePath, params); } } onConfigure(db); final int version = db.getVersion(); if (version != mNewVersion) { if (db.isReadOnly()) { throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " + mNewVersion + ": " + mName); } //程序运行时更新数据库:使用缓存数据库时走此逻辑,从新加载数据库 if (version > 0 && version < mMinimumSupportedVersion) { File databaseFile = new File(db.getPath()); onBeforeDelete(db); db.close(); if (SQLiteDatabase.deleteDatabase(databaseFile)) { mIsInitializing = false; return getDatabaseLocked(writable); } else { throw new IllegalStateException("Unable to delete obsolete database " + mName + " with version " + version); } } else { //事务更新(降级/升级)数据库 db.beginTransaction(); try { if (version == 0) { onCreate(db); } else { if (version > mNewVersion) { onDowngrade(db, version, mNewVersion); } else { onUpgrade(db, version, mNewVersion); } } db.setVersion(mNewVersion); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } } onOpen(db); if (db.isReadOnly()) { Log.w(TAG, "Opened " + mName + " in read-only mode"); } mDatabase = db; return db; } finally { mIsInitializing = false; if (db != null && db != mDatabase) { db.close(); } } }
三、ContextImpl获取数据库路径app
public File getDatabasePath(String name) { File dir; File f; if (name.charAt(0) == File.separatorChar) { //数据库文件名为绝对路径时,例如(/sdcard/test.db)则返回该绝对路径的文件对象。 String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar)); dir = new File(dirPath); name = name.substring(name.lastIndexOf(File.separatorChar)); f = new File(dir, name); if (!dir.isDirectory() && dir.mkdir()) { FileUtils.setPermissions(dir.getPath(), 505, -1, -1); } } else { //若是仅指定文件名称时,经过conext对象获取数据库文件存储目录,获取该目录下的数据库文件对象想。 dir = this.getDatabasesDir(); f = this.makeFilename(dir, name); } return f; }
经过上面源码分析发现获取数据库对象的具体调用逻辑:框架
一、经过getWritableDatabase()方法获取数据库对象,实际是调用getDatabaseLocked(true)方法。ide
public SQLiteDatabase getWritableDatabase() { synchronized (this) { return getDatabaseLocked(true); } }
getDatabaseLocked(true)方法获取数据库实例:获取数据库路径源码源码分析
private SQLiteDatabase getDatabaseLocked(boolean writable) { SQLiteDatabase db = mDatabase; try { mIsInitializing = true; if (db != null) { ... } else if (mName == null) { ... } else { final File filePath = mContext.getDatabasePath(mName); SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build(); try { db = SQLiteDatabase.openDatabase(filePath, params); ... } catch (SQLException ex) { ... db = SQLiteDatabase.openDatabase(filePath, params); } } ... mDatabase = db; return db; } finally { ... } }
在getDatabaseLocked(true)获取数据库存储路径是调用Context的getDatabasePath(mName)方法,在Android中contex的具体实现类为ContextImpl,因此获取数据库存储路径要看ContextImpl.getDatabasePath(String)方法的实现。测试
public File getDatabasePath(String name) { File dir; File f; if (name.charAt(0) == File.separatorChar) { //数据库文件名为绝对路径时,例如(/sdcard/test.db)则返回该绝对路径的文件对象。 String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar)); dir = new File(dirPath); name = name.substring(name.lastIndexOf(File.separatorChar)); f = new File(dir, name); if (!dir.isDirectory() && dir.mkdir()) { FileUtils.setPermissions(dir.getPath(), 505, -1, -1); } } else { //若是仅指定文件名称时,经过conext对象获取数据库文件存储目录,获取该目录下的数据库文件对象想。 dir = this.getDatabasesDir(); f = this.makeFilename(dir, name); } return f; }
咱们在使用数据库时有时候须要自定义数据库存储路径,下面介绍三种改变数据库文件路径的方法。
一、自定义Application public class MyApplication extends Application{ public static MyApplication getInstance() { return mInstance; } @Override public void onCreate() { super.onCreate(); } @Override public File getDatabasePath(String name) { return new File("/sdcard/"); } } 二、在manifest文件中指定application <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.qihoo.qa"> //申请权限 ... <application android:name=".MyApplication" android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:supportsRtl="true" android:theme="@style/AppTheme"> </application> </manifest> 四、获取DatabaseHelper,数据库文件为/sdcard/test.db DatabaseHelper dataBaseHelper = new DatabaseHelper(context, "test.db", 0);
//获取DatabaseHelper,数据库文件为/sdcard/test.db DatabaseHelper dataBaseHelper = new DatabaseHelper(context, "/sdcard/test.db", 0);
class DatabaseHelper extends SQLiteOpenHelper { //一、指定数据库文件名 String dbName="/sdcard/test.db"; int version; public DatabaseHelper(Context context, String dbName, int version) { super(context, dbName, null, version); } @Override public void onCreate(SQLiteDatabase db) { //数据库建立时调用 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //数据库升级时调用 } //覆写获取数据库实现 @Override public SQLiteDatabase getWritableDatabase() { return getDataBase(dbName, version); } //覆写获取数据库实现 @Override public SQLiteDatabase getReadableDatabase() { return getDataBase(dbName, version); } //实现获取数据库具体逻辑 private SQLiteDatabase getDataBase(String fileName, int version){ try { SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(fileName, null); int oldVer = database.getVersion(); database.setVersion(version); if (version > oldVer) { this.onUpgrade(database, oldVer, version); } if(version<oldVer){ this.onDowngrade(database, oldVer, version); } return database; } catch (Exception var4) { LogUtil.e(var4); return super.getWritableDatabase(); } }
注意
方法1、方法二须要传递context对象,若是Context为null,获取数据库实例时会致使空指针异常。
方法三的实现不依赖Context对象,能够将Context指定为null。