【问题标题】:Using Singleton design pattern for SQLiteDatabase为 SQLiteDatabase 使用单例设计模式
【发布时间】:2011-10-17 20:16:20
【问题描述】:

我是 Android 的新手,我正在开发一个简单的应用程序来获得一些基本经验。我的应用程序非常简单,其中包括广播接收器和一些活动。两个组件都使用一个数据库,因此理论上两者都可能同时尝试访问数据库。

目前我只是在每次需要时实例化 db 对象(这是一个 SQLite db 帮助器类),并执行所需的操作:查询、插入等。

从我在这里和其他一些文档中阅读的内容来看,如果同时访问数据库,则会出现“数据库锁定”异常的问题,因此更好的方法是拥有该数据库的单个实例对象,因此所有组件始终使用相同的数据库连接。

以上推理正确吗?那么单身人士会是一个足够好的解决方案吗?我知道一些纯粹主义者可能会反对它,但请注意,这是一个相当简单的应用程序,因此我可以负担得起在其他情况下不会做的事情。

否则,更好的选择是什么?我已经阅读了有关使用内容提供程序的信息,但这样做太多了,此外我对与其他活动共享数据不感兴趣。我确实读过这个post 并发现它很有帮助。

【问题讨论】:

    标签: java android sqlite singleton android-loadermanager


    【解决方案1】:

    Click here to see my blog post on this subject.


    这里是一些示例代码,说明了三种可能的方法。这些将允许在整个应用程序中访问数据库。

    方法 #1:让 `SQLiteOpenHelper` 成为静态数据成员

    这不是完整的实现,但它应该让您了解如何正确设计DatabaseHelper 类。静态工厂方法确保任何时候都只存在一个 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://android-developers.blogspot.nl/2009/01/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;
        }
    }
    

    方法 #2:使用 `ContentProvider` 抽象 SQLite 数据库

    这是我建议的方法。一方面,新的CursorLoader 类需要ContentProviders,所以如果你想要一个Activity 或Fragment 用CursorLoader 实现LoaderManager.LoaderCallbacks<Cursor>(我建议你利用它,它很神奇!),你'需要为您的应用程序实现ContentProvider。此外,您无需担心使用 ContentProviders 创建 Singleton 数据库助手。只需从 Activity 中调用getContentResolver(),系统就会为您处理所有事情(换句话说,无需设计单例模式来防止创建多个实例)。

    希望这会有所帮助!

    【讨论】:

    • Alex,我使用你的方法 (#2),我喜欢它的简单性。但是最近我发现了一个问题。想知道您是否可以帮助我解决这个问题:stackoverflow.com/questions/10972719/… Btw,刚刚注意到我使用静态 mCxt(忘了为什么)。不知道这是否与我的问题有关
    • 以上链接已失效...这是更新后的链接:android-developers.blogspot.com/2009/01/…
    • 这是我编写/使用的一个包装器,它使在 android 环境中使用 SQLite 变得稍微容易一些 - SqlDb for Android
    • 感谢@AlexLockwood,虽然方法#1 对我来说非常有意义,但我认为只有在与其他应用程序共享数据库访问时才应该使用ContentProvider
    • 同步getInstance不是一个好主意吗?
    【解决方案2】:

    我从未读过有关使用单例访问 android 上的数据库的信息。你介意提供一个链接吗?

    在我的应用程序中,我使用简单的 dbhelper 对象,而不是单例,我认为这更多是 sql 引擎的工作,以确保 db 不被锁定,而不是你的 android 类的工作,它对我的​​工作非常好最大的中型应用。

    更新 #1:查看您提供的参考资料,看起来问题根本不在于使用 dbhelper 的不同实例。即使是单个实例也可能会遇到访问数据库的问题:问题来自并发访问。因此,确保不同线程正确访问数据库的唯一方法是使用简单的线程同步机制(synchronized 方法或块),这与使用单例几乎无关。

    更新#2:您提供的第二个链接清楚地表明,在多个线程同时写入数据库的情况下,它们需要单例 dbhelper 对象。例如,如果您从 AsyncTasks 执行 sql 操作(插入/更新/删除),就会发生这种情况。在这种情况下,单例对象 dbhelper 会简单地将所有 sql 操作放在某种管道中并按顺序执行。

    此解决方案可能比使用 java 中的同步方法使用适当的线程同步更容易实现。实际上,我认为应该在 android 文档的某个地方更加强调这个问题,并且可以鼓励使用单例 db 助手。

    感谢这个好问题和后续行动。

    【讨论】:

    猜你喜欢
    • 2017-10-12
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多