Android应用程序中的所有活动共享一个SQLiteOpenHelper实例是否可行?
将SQLiteOpenHelper的单个实例作为子类应用程序的成员是否可以,并且让所有需要SQLiteDatabase实例的活动从一个帮助器中获取?
拥有一个SQLiteOpenHelper
实例可以帮助处理案例。 由于所有的线程将共享公共的SQLiteDatabase
,所以提供了操作的同步。
但是,我不会做一个Application
的子类。 只需要有一个静态数据成员是你的SQLiteOpenHelper
。 两种方法都可以让你从任何地方访问。 但是, Application
只有一个子类,使得使用其他 Application
子类更加困难(例如,GreenDroid需要一个IIRC)。 使用静态数据成员可以避免这种情况。 但是,在实例化此静态SQLiteOpenHelper
(构造函数参数)时请使用Application
Context
,这样就不会泄漏其他Context
。
而且,在不处理多个线程的情况下,可以通过对每个组件使用一个SQLiteOpenHelper
实例来避免任何可能的内存泄漏问题。 然而,在实践中,你应该处理多个线程(例如,一个Loader
),所以这个build议只适用于微不足道的应用程序,比如在某些书中find的那些… 🙂
点击这里查看关于这个主题的博客文章。
CommonsWare是正确的(像往常一样)。 扩展他的职位,这里是一些示例代码,说明了三种可能的方法。 这些将允许在整个应用程序中访问数据库。
方法#1:应用程序的子类
如果你知道你的应用程序不会很复杂(例如,如果你知道你最终只有一个Application
子类),那么你可以创build一个Application
的子类,让你的主要活动扩展它。 这确保数据库的一个实例在整个应用程序的整个生命周期中运行。
public class MainApplication extends Application { /** * see NotePad tutorial for an example implementation of DataDbAdapter */ private static DataDbAdapter mDbHelper; /** * Called when the application is starting, before any other * application objects have been created. Implementations * should be as quick as possible... */ @Override public void onCreate() { super.onCreate(); mDbHelper = new DataDbAdapter(this); mDbHelper.open(); } public static DataDbAdapter getDatabaseHelper() { return mDbHelper; } }
方法2:让`SQLiteOpenHelper`成为一个静态数据成员
这不是完整的实现,但它应该给你一个关于如何正确deviseDatabaseHelper
类的好主意。 静态工厂方法确保在任何时候只存在一个DatabaseHelper实例。
/** * create custom DatabaseHelper class that extends SQLiteOpenHelper */ public class DatabaseHelper extends SQLiteOpenHelper { private static DatabaseHelper mInstance = null; private static final String DATABASE_NAME = "databaseName"; private static final String DATABASE_TABLE = "tableName"; private static final int DATABASE_VERSION = 1; private Context mCxt; public static DatabaseHelper getInstance(Context ctx) { /** * use the application context as suggested by CommonsWare. * this will ensure that you dont accidentally leak an Activitys * context (see this article for more information: * http://developer.android.com/resources/articles/avoiding-memory-leaks.html) */ if (mInstance == null) { mInstance = new DatabaseHelper(ctx.getApplicationContext()); } return mInstance; } /** * constructor should be private to prevent direct instantiation. * make call to static factory method "getInstance()" instead. */ private DatabaseHelper(Context ctx) { super(context, DATABASE_NAME, null, DATABASE_VERSION); this.mCtx = ctx; } }
方法3:用“ContentProvider”抽象SQLite数据库
这是我build议的方法。 首先,新的LoaderManager
类在很大程度上依赖于ContentProviders,所以如果你想要一个Activity或Fragment来实现LoaderManager.LoaderCallbacks<Cursor>
(我build议你利用它,这是神奇的!),你需要实现一个ContentProvider
为您的应用程序。 此外,您不必担心使用ContentProviders创buildSingleton数据库帮助器。 只需从Activity中调用getContentResolver()
,系统将为您处理所有事情(换句话说,不需要devise单例模式来防止创build多个实例)。
希望有所帮助!
我写了MultiThreadSQLiteOpenHelper这是一个增强的SQLiteOpenHelper的Android应用程序,其中几个线程可能会打开和closures相同的SQLite数据库。
线程不是调用close方法,而是要求closures数据库,防止线程在closures的数据库上执行查询。
如果每个线程要求closures,则closures实际上被执行。 每个活动或线程(ui-thread和user-threads)在恢复时对数据库执行一个公开调用,并要求在暂停或完成时closures数据库。
源代码和示例在这里可用: https : //github.com/d4rxh4wx/MultiThreadSQLiteOpenHelper
我对这个主题做了大量的研究,并且同意commonware提到的所有观点。 但是我认为这里有一个重要的问题,那就是这个问题的答案完全依赖于你的用例,所以如果你的应用程序正在通过multithreading读取数据库,并且只有使用Singleton才能读取,所有function都是同步的并连续执行,因为有一个单一的连接到数据库开源很好,顺便说一句。 你可以深入研究代码,看看发生了什么。 从这个和一些testing中,我了解到以下情况是正确的:
Sqlite takes care of the file level locking. Many threads can read, one can write. The locks prevent more than one writing. Android implements some java locking in SQLiteDatabase to help keep things straight. If you go crazy and hammer the database from many threads, your database will (or should) not be corrupted.
如果您尝试从实际不同的连接同时写入数据库,则会失败。 它不会等到第一个完成,然后写。 它不会写你的改变。 更糟的是,如果你没有在SQLiteDatabase上调用正确版本的insert / update,你将不会得到exception。 你会在你的LogCat中得到一个消息,就是这样。
第一个问题是真正的,独特的联系。 关于开源代码的伟大之处在于你可以正确的挖掘并看看发生了什么。 SQLiteOpenHelper类做了一些有趣的事情。 虽然有一种方法获得一个只读的数据库连接以及一个读写连接,但是它们始终是相同的连接。 假设没有文件写入错误,即使是只读连接也是单一的读写连接。 挺滑稽的。 所以,如果您在应用程序中使用一个助手实例,即使是来自多个线程,也不会真正使用多个连接。
此外,SQLiteDatabase类(每个帮助程序只有一个实例)在其自身上实现了Java级别locking。 所以,当你正在执行数据库操作时,所有其他数据库操作将被locking。 所以,即使你有多个线程来做这些事情,但是如果你这样做是为了最大限度地提高数据库的性能,那么我也有一些坏消息。 没有好处。
有趣的观察
如果closures一个写入线程,那么只有一个线程正在写入数据库,但另一个读取,并且都有自己的连接,读取性能会发射WAY,并且我没有看到任何locking问题。 这是要追求的东西。 我还没有尝试过写配料。
如果您要执行多个任何types的更新,请将其包含在事务中。 似乎我在事务中执行的50个更新所花费的时间与事务之外的1次更新时间相同。 我的猜测是,除了事务调用之外,每次更新都会尝试将db更改写入磁盘。 在事务内部,写入是在一个块中完成的,而写入的开销则使得更新逻辑本身变得更加困难。
是的,你应该这样做,有一个辅助类来处理需要数据库实例的活动。