单例方法的SQLite内存问题 [英] SQLite memory issue with singleton approach

查看:99
本文介绍了单例方法的SQLite内存问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个支持Android应用程序中所有内容的SQLite数据库.

I have a SQLite database powering all the content in an Android app.

我有一个扩展SQLiteAssetHelper的DatabaseHelper类.

I have a DatabaseHelper class that extends SQLiteAssetHelper.

我的数据库实例过多,然后遇到了SQLiteCantOpenDatabaseException.

I have been having problems with too many instances of my database and then getting a SQLiteCantOpenDatabaseException.

为了解决这个问题,我更改了类以维护DatabaseHelper对象的单个实例.

To counter this, I have changed my class to maintain a single instance of the DatabaseHelper object.

我有以下内容:

private static DatabaseHelper databaseHelper;

public static synchronized DatabaseHelper getInstance(Context context, boolean singleRow, boolean showLoader){
    if(databaseHelper == null) {
        databaseHelper = new DatabaseHelper(context, singleRow, showLoader);
    }
    return databaseHelper;
}

public DatabaseHelper(Context context, boolean singleRow, boolean showLoader){
    super(context, (new File(DatabaseManager.getDatabasePath(context))).getName(), (new File(DatabaseManager.getDatabasePath(context))).getParentFile().getAbsolutePath(), null, DATABASE_VERSION);

    this.context = context;
    this.singleRow = singleRow;
    this.showLoader = showLoader;

}

然后我按如下所示调用getInstance静态方法:

I then call the getInstance static method as follows:

DatabaseHelper databaseHelper = DatabaseHelper.getInstance(activity.getApplicationContext(), false, false);

经过一定数量的数据库活动后,该应用程序仍会由于内存原因而崩溃.

After a certain amount of database activity, the app is still crashing on memory grounds.

然后我得到这个错误:

Error Code : 2062 (SQLITE_CANTOPEN_EMFILE)

Caused By : Application has opened two many files. Maximum of available file descriptors in one process is 1024 in default.

(unable to open database file (code 2062))

采用单例方法,我仍然有些困惑,为什么它仍然会导致此内存泄漏.

Having taken the singleton approach, I'm a bit lost for why this is still causing this memory leak.

任何帮助将不胜感激.

推荐答案

如果您收到一条消息,指出打开了太多文件,则很可能是因为有太多的Cursor仍在打开.

If you are getting a message indicating too many files open, a cause may well be that there are too many Cursor that are still open.

但是,返回的消息可能并不总是相同的,并且可能特定于所调用的任务/呼叫.

However, the message returned may not always be the same and is probably specific to the task/call being called.

在这种情况下,消息为(unable to open database file (code 2062)),而在另一种情况下(通过SELECT,消息为unable to open database file (code 14)). SQLite无法打开频繁执行"SELECT"查询的数据库文件(代码14).

In this case the message was (unable to open database file (code 2062)), yet in another case (from a SELECT the message was unable to open database file (code 14)). SQLite unable to open database file (code 14) on frequent "SELECT" query.

上面的链接也指向我发表的一篇文章,该文章很清楚地表明创建游标会导致打开一个或多个文件.

The above link also points to a post I made what quite clearly shows that creating a Cursor results in a file (or files) being opened.

该示例循环遍历约500行,并为每行创建/重新创建每行3个游标(因此,即使仅使用4个游标对象,也可能有1500+个游标).

The example was looping through about 500 rows and for each row it was creating/recreating 3 cursors for each row (so potentially 1500+ cursors even though only using 4 cursor objects).

最初,它仅关闭结尾处的3个游标(所有父项的最后一行),从而导致unable to open database File (code 14).在每次迭代中关闭3个游标即可解决此问题.

Initially it was only closing the 3 cursors at the end (last row of the parent of all) resulting in the unable to open database File (code 14). Closing the 3 cursors for each iteration resolved the issue.

失败的代码是:-

        SQLiteDatabase db = getWritableDatabase();
        Cursor shoplistcursor = getAllRowsFromTable(SHOPLIST_TABLE_NAME);
        Cursor productcsr;
        Cursor aislecsr;
        Cursor prdusecsr;
        while(shoplistcursor.moveToNext()) {
            productcsr = getProductFromProductId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
            aislecsr = getAisleFromAisleId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)));
            prdusecsr = getProductUsage(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)),
                    shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
            if (productcsr.getCount() < 1 | aislecsr.getCount() < 1 | prdusecsr.getCount() < 1) {
                deleteShopListEntry(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_ID)));
            } 
            if(shoplistcursor.isLast()) {
                prdusecsr.close();
                aislecsr.close();
                productcsr.close();
            }
        }
        shoplistcursor.close();
        db.close();
}

虽然固定代码是:-

        SQLiteDatabase db = getWritableDatabase();
        Cursor shoplistcursor = getAllRowsFromTable(SHOPLIST_TABLE_NAME);
        Cursor productcsr;
        Cursor aislecsr;
        Cursor prdusecsr;
        while(shoplistcursor.moveToNext()) {
            productcsr = getProductFromProductId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
            aislecsr = getAisleFromAisleId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)));
            prdusecsr = getProductUsage(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)),
                    shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
            if (productcsr.getCount() < 1 | aislecsr.getCount() < 1 | prdusecsr.getCount() < 1) {
                productcsr.close();
                aislecsr.close();
                prdusecsr.close();
                deleteShopListEntry(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_ID)));
            } else {
                productcsr.close();
                aislecsr.close();
                prdusecsr.close();
            }
        }
        shoplistcursor.close();
        db.close();
    }

我现在倾向于遵循以下规则/做法:-

I tend to now follow the following rule/practice :-

  • 如果只是得到结果,例如要获取行数,请在方法中关闭Cursor.

  • If just getting the result e.g. getting the number of rows, close the Cursor in the method.

如果使用游标进行显示,例如一个ListView,然后在活动的onDestroy方法中关闭光标.

If using the Cursor for a display e.g. a ListView, then close the cursor in the activity's onDestroy method.

如果将游标用于更复杂的处理,例如删除带有基础引用的行,然后在处理循环内完成处理后立即关闭游标.

If using the Cursor for what I'll call more complex processing e.g. deleting rows with underlying references then close the cursors as soon as they are done with, within the processing loop(s).

这篇关于单例方法的SQLite内存问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆