对SQLiteDatabase使用Singleton设计模式


77

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

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

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

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

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

Answers:


101

单击此处查看我关于此主题的博客文章。


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

方法#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,所以如果你想要一个活动或片段来实现LoaderManager.LoaderCallbacks<Cursor>CursorLoader(我建议你利用的,这是不可思议的!),你需要实现ContentProvider你的应用程序。此外,您不必担心使用ContentProviders创建Singleton数据库帮助程序。只需getContentResolver()从Activity中调用,系统便会为您处理所有事情(换句话说,无需设计Singleton模式来防止创建多个实例)。

希望这可以帮助!


亚历克斯,我使用您的方法(#2),我喜欢它的简单性。但是最近我注意到了一个问题。不知道您是否可以帮助我解决该问题:stackoverflow.com/questions/10972719/…顺便说一句,只是注意到我使用静态mCxt(忘记了原因)。不知道是否有什么与我的问题
米哈尔ķ

上面的链接已断开...这是一个更新的链接:android-developers.blogspot.com/2009/01/…–
Alex Lockwood

这是我编写/使用的包装,这使得在android的上下文中更好地使用SQLite更加容易-SqlDb for Android
kashif 2013年

1
感谢@AlexLockwood,虽然方法#1对我来说很有意义,但我认为只有ContentProvider在与其他应用程序共享数据库访问权限时才应该使用?
ericn 2015年

2
进行getInstance同步不是一个好主意吗?
马修·米切尔

22

我从未读过关于使用单例访问android上的数据库的信息。您介意提供有关此链接。

在我的应用程序中,我使用简单的dbhelper对象,而不是单例对象,我想这更多是sql引擎的工作,以确保db不被锁定,而不是android类的工作,并且对于我最大的应用程序来说,它工作得很好中型。

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

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

与在Java中使用同步方法使用适当的线程同步相比,此解决方案可能更容易实现。实际上,我认为应该在android文档中的某个地方对此问题进行更多的强调,并且可以鼓励使用singleton db helper。

感谢您提出这个很好的问题和后续措施。


谢谢斯特凡。我作为基础的文章是这些: stackoverflow.com/questions/2647542/... stackoverflow.com/questions/4302286/... 问候,丹

您要评论的问题是(AFAIK)对同一数据库使用不同的连接会导致失败。因此使用单例。

看到这个线程,似乎支持我的理论:stackoverflow.com/questions/2493331/…–
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.