Android 开发中的单例模式(Singleton Pattern)

本科三年级的 Android 开发课程。

这周 Android 实验用到了 SQLite 数据库,验收的时候师姐问我对 SQLiteOpenHelper 实例是怎么处理的。我说那肯定是每一个 Activity 一个 SQLiteOpenHelper 实例,然后师姐说这样不太好让我改成单例的。

说起来羞愧,大一下学期买的设计模式到大三了还没看完,只记得观察者模式那几个常用的。回到去查了下文档就把数据库改成了单例。

一开始代码是这样的

class Database extends SQLiteOpenHelper {
private final String TABLE_NAME = "birthday_log";
Database(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
}

然后我每次在 Activity 里面操作数据库都 new 一个 Database 实例,如果在 Service 里面这样干,很容易会翻车的。我们希望全局只有一个 Database 实例。

防止被创建实例,我们可以把构造函数设置成 private 的,这样就不会被创建了。

class Database extends SQLiteOpenHelper {
private final String TABLE_NAME = "birthday_log";
private Database(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
}

这样怎样创建实例呢?Private 当然是 Class 内部调用了。

class Database extends SQLiteOpenHelper {
private final String TABLE_NAME = "birthday_log";
private static Database database = null;
private Database(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
static Database getDatabase(Context context) {
if (database == null) {
database = new Database(context, DB_NAME, null, DB_VERSION);
}
return database;
}
}

怎样理解 database 这个变量呢?这个是自身实例的引用,可以理解为 static class 里的 this 指针,当然实际上 static 的函数是没有 this 指针的。

以后要获取 Database 实例只需要 Database.getDatabase(context) 即可。

最后考虑一个极端的情况,假如 Database.getDatabase(context) 被两个进程并发执行怎么办,那样还是有可能创建两个 Database 实例。在 Java 中用 synchronized 关键字修饰函数,代表在同一时间只能有一个函数并发执行。

class Database extends SQLiteOpenHelper {
private final String TABLE_NAME = "birthday_log";
private static Database database = null;
private Database(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
static synchronized Database getDatabase(Context context) {
if (database == null) {
database = new Database(context, DB_NAME, null, DB_VERSION);
}
return database;
}
}

当然这样处理是会有性能损耗的,每次运行 Database.getDatabase(context) 都会有一个获取锁和释放锁的过程,弥补这样的方法是直接 private static Database database = new Database(context, DB_NAME, null, DB_VERSION);,这样会在运行时把对象构造好,然而 SQLiteOpenHelper 需要 context,暂时不清楚直接赋值如何获取 context

这样一番操作之后,以后就可以在任意地方通过 Database.getDatabase(context) 共享一个 SQLiteOpenHelper 实例了。

最后,还有,看来不能只顾着拿 Kindle 看小说了,要把设计模式拿回宿舍看了

All rights reserved
Except where otherwise noted, content on this page is copyrighted.