1. 背景
最近研究数据库知识,本来想接着dialog写alertdialog,把dialog的内容写完的。但是最近正好研究数据库,并且没有太多时间,就写下这个简单的类,跟大家分享下,加深了解。SQLiteOpenHelper这个类是帮助创建数据库的。这里的实例代码里面会有介绍
2. 使用方法
使用方法比较简单,这里只要复写onCreate()方法与onUpgrade()方法即可。这里我们创建两个表,一个tablecontact,一个是tablecall。这里我通过interface创建了几个静态常量,便于管理。如果里面的sql语句不懂的话还是要了解下sql语句,这种基础的还是要会的。
public class TestSQLiteOpenHelper extends SQLiteOpenHelper {
private final static String DATABASENAME = "testdatabase.db";
private final static int DATABASEVERSION = 1;
public interface TableContact {
String tablename = "TableContact";
String PeopleId = "_id";
String PeopleName = "name";
String Number = "number";
String SortKey = "sort_key";
}
public interface TableCall {
String tablename = "TableCall";
String callid = "_id";
String callType = "type";
String calltime = "time";
String callName = "name";
}
public TestSQLiteOpenHelper(Context context) {
this(context, DATABASENAME, null, DATABASEVERSION);
}
public TestSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TableContact.tablename + " ("
+ TableContact.PeopleId + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ TableContact.PeopleName + " TEXT,"
+ TableContact.Number + " TEXT,"
+ TableContact.SortKey + " TEXT"
+ ");");
db.execSQL("CREATE TABLE " + TableCall.tablename + " ("
+ TableCall.callid + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ TableCall.callType + " INTEGER,"
+ TableCall.calltime + " INTEGER,"
+ TableCall.callName + " TEXT"
+ ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TableContact.tablename + ";");
db.execSQL("DROP TABLE IF EXISTS " + TableCall.tablename);
onCreate(db);
}
}
使用的话我这里我可以写出如下代码:
//获取SQLiteOpenHelper的实例通过getWritableDatabase()获取SQLiteDatabase 的实例。
TestSQLiteOpenHelper testSQLiteOpenHelper = new TestSQLiteOpenHelper(this);
SQLiteDatabase sqLiteDatabase = testSQLiteOpenHelper.getWritableDatabase();
//这里是插入操作。需要一个表名和nullColumnHack参数。这个参数处理当插入的values为空的时候的情况。当null时候插入为空不操作。如果想填入默认值就通过insert第二个参数传入。第三个参数是个需要传入需要的值。通过一个hashmap来保存一个Parcelable数据,这里ContentValues 数据类型比较简单,就不在详细说明了
ContentValues values = new ContentValues();
values.put(TestSQLiteOpenHelper.TableContact.PeopleName,"小明");
values.put(TestSQLiteOpenHelper.TableContact.Number,"1111111");
sqLiteDatabase.insert(TestSQLiteOpenHelper.TableContact.tablename,null,values);
当查询时候我们可以使用如下代码:
SQLiteDatabase database = testSQLiteOpenHelper.getReadableDatabase();
String[] String = {TestSQLiteOpenHelper.TableContact.PeopleName,TestSQLiteOpenHelper.TableContact.Number};
Cursor cursor = database.query(TestSQLiteOpenHelper.TableContact.tablename,String,
null,null,null,null,null);
cursor.moveToFirst();
int nameColumnIndex = cursor.getColumnIndex(TestSQLiteOpenHelper.TableContact.PeopleName);
if (cursor != null)
Log.v(">>>>>>",cursor.getString(nameColumnIndex));
cursor.close();
数据库的查询我就不在细说。里面东西比较多,这里就暂时放在一边,以后机会我们研究SQLiteDatabase这个类的时候我们在来研究具体查询方法。
这里基本就完成成了代码的使用,但是是不是感觉每次调用都要不停的get各种datebase是不是比较麻烦。我们可以写个halper类来解决这个问题。至于解决办法我们可以生成一个静态内部类。我这里仅仅写下大概框架。实现就不在意义列举了
public class TestSQLiteOpenHelper extends SQLiteOpenHelper {
private TestSQLiteOpenHelper(Context context) {
......
}
......
public static class useHelper
TestSQLiteOpenHelper mhelper;
public useHelper(context contest){
mhelper = new TestSQLiteOpenHelper(context);
}
public void insert(){
......
}
public curser(){
......
}
.....
}
这里我讲解的比价简单。其实SQLiteOpenHelper仅仅是为了帮助我们创建一个datebase,没有套多问题。那么我们来研究源码的问题吧。到底他的调用周期是什么。
3. 源码分析
先看构造函数,
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
DatabaseErrorHandler errorHandler) {
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
mContext = context;
mName = name;
mFactory = factory;
mNewVersion = version;
mErrorHandler = errorHandler;
}
几个构造函数都指向这个,忽然发现好简单啊。这里没啥收获,那么我们就找在哪里调用。
public SQLiteDatabase getWritableDatabase() {
synchronized (this) {
return getDatabaseLocked(true);
}
}
public SQLiteDatabase getReadableDatabase() {
synchronized (this) {
return getDatabaseLocked(false);
}
}
明确了吧,就是getDatabaseLocked()这个函数。那么我们研究如何这个函数。
private SQLiteDatabase getDatabaseLocked(boolean writable) {
//已经拥有mDatabase的情况,如果打开的话。直接返回,没有置空,
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");
}
//好些源码都哎创建一个句柄。也就是c中的指针,这里感觉没必要。
SQLiteDatabase db = mDatabase;
try {
mIsInitializing = true;
if (db != null) {
//必须同时拥有读写权限
if (writable && db.isReadOnly()) {
db.reopenReadWrite();
}
} else if (mName == null) {
//这里没有名字就没法获取已经生成的datebase。只能重新生成。
db = SQLiteDatabase.create(null);
} else {
//这里两个最终丢失调用SQLiteDatabase.openDatabase()来打开或者生成database
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);
}
//设置打开模式。
db.beginTransaction();
try {
//0代表没生成。直接调用oncreat()
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();
}
}
}
主要的流程都通过上面的介绍完成。要结合源码程序来阅读,里面获取数据库这一段跟context有关,这是设置数据库权限的地方,代码很简单,到底有啥用处我就没有跟踪。
4. 总结
其实这个调用很符合我们的预期。当没有数据库的时候直接调用oncreat()。然后判断版本。确定升级还是降级。