SQLiteAssetHelper NullPointerException 仅在某些设备上

Posted

技术标签:

【中文标题】SQLiteAssetHelper NullPointerException 仅在某些设备上【英文标题】:SQLiteAssetHelper NullPointerException only on some devices 【发布时间】:2014-01-08 17:07:15 【问题描述】:

我正在使用 SQLiteAssetHelper 库来处理我的 App DB 操作,它几乎适用于我测试过的所有手机。但是,一些使用 android 2.3.3 - 2.3.7 的用户在调用打开数据库时报告崩溃。我怀疑这是由于第一次从资产文件夹复制数据库时发生的一些问题(例如内部存储空间不足)。

助手类:

public class DbHelper extends SQLiteAssetHelper 

    final static String DB_NAME="db";
    static final int DB_VERSION = 1307181213;
    final Context context;

    public DbHelper(Context c) 
        super(c, DB_NAME, null, DB_VERSION);
        this.context = c;
        setForcedUpgradeVersion(DB_VERSION);
    

处理程序类(如果无法打开数据库,我将计数器设置为 10 秒后超时) :

public class DbHandlerDao 

    private SQLiteDatabase database;
private DbHelper dbHelper;

public DbHandlerDao(Context context) 
    dbHelper = new DbHelper(context);


public void open() throws SQLException 
    boolean opened = false;
    int timeout = 0;
    while (!opened && timeout < 10000) 
        try 
            database = dbHelper.getReadableDatabase();
            opened = true; 
        
        catch (IllegalStateException e) 
            timeout += 500;
            try 
                Thread.sleep(500);
             catch (Exception e2) 

            
        
    


public void close() 
    dbHelper.close();

调用类:

private class DbAsyncTask extends AsyncTask<Long, Void, Void> 

        //...

        @Override
        protected Void doInBackground(Long... params) 
            dataHandler.open();

例外:

java.lang.RuntimeException: An error occured while executing doInBackground()
    at android.os.AsyncTask$3.done(AsyncTask.java:200)
    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
    at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
    at java.lang.Thread.run(Thread.java:1019)
Caused by: java.lang.NullPointerException
    at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getWritableDatabase(SQLiteAssetHelper.java:180)
    at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getReadableDatabase(SQLiteAssetHelper.java:257)
    at com.example.DbHandlerDao.open(DbHandlerDao.java:25)
    at com.example.SearchActivity$DbAsyncTask.doInBackground(SearchActivity.java:244)
    at com.example.SearchActivity$DbAsyncTask.doInBackground(SearchActivity.java:1)
    at android.os.AsyncTask$2.call(AsyncTask.java:185)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)

【问题讨论】:

我在运行 Android 4.3 和 4.4 的几台设备上遇到了似乎相同的错误。奇怪的是,我无法始终如一地重现它。 我会将它作为 github 项目本身的问题发布,因为它几乎可以保证是库的问题 - 标准 SQLiteOpenHelper 没有这个问题。似乎其他人也有这个问题:github.com/jgilfelt/android-sqlite-asset-helper/issues/23 谢谢,好主意。但经过更多调查后,我意识到这是我自己的错误。长话短说,我正在从一个活动的onCreate 方法调用的AsyncTask 中复制一个数据库,然后尝试使用同一个数据库填充ListView。事实证明,只有当我很幸运并且后台任务在 ListView 需要数据库之前完成时才有效! 只是快速说明没有基于计时器的循环,用户无法取消。您最终可能会锁定 ui 线程,并且您的应用将显示无响应。 danny117,我在后台运行的 asyncTask 中调用 dataHandler.open() 【参考方案1】:

可能是用户已清空存储空间或没有 SD 卡?

偶尔发生的崩溃可能是怎么回事。

** 你实现了oncreate和onupgrade方法了吗?

我使用 open 助手。所以我从来没有遇到过打开数据库的麻烦。

 public class RidesDatabaseHandler extends SQLiteOpenHelper 

...

    // Creating Tables
@Override
public void onCreate(SQLiteDatabase db) 
    String CREATE_CONTACTS_TABLE = "CREATE TABLE " + TABLE_RIDES + "("
            + KEY_ID + " INTEGER PRIMARY KEY," + KEY_NAME + " TEXT,"
            + KEY_DESCRIPTION + " TEXT," + KEY_BOUNDS_NELAT + " TEXT,"
            + KEY_BOUNDS_NELONG + " TEXT," + KEY_BOUNDS_SWLAT + " TEXT,"
            + KEY_BOUNDS_SWLONG + " TEXT," + KEY_RESRAWID + " TEXT,"
            + KEY_RESDRAWABLEID + " TEXT," + KEY_PATH + " TEXT,"
            + " CONSTRAINT UNQ_" + KEY_PATH + " UNIQUE (" + KEY_PATH
            + ") ON CONFLICT ABORT)";

    // VERSION 43 ADDS THE KEY_DATE_UPDATE COLUMN
    CREATE_CONTACTS_TABLE += " , " + KEY_DATE_UPDATE + " INTEGER DEFAULT 0";

    db.execSQL(CREATE_CONTACTS_TABLE);


// Upgrading database
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 

    int upgradeTo = oldVersion + 1;
    while (upgradeTo <= newVersion) 
        switch (upgradeTo) 
        // update old resrawid's to constants
        // this was caused when I saved R values in the database
        // and then later realized they would change.
        // the old resrawid is changed to a constant.
        case 42:
            ContentValues values;
            @SuppressWarnings("unused")
            // used for debugging so I can see the value of the update.
            int res;
            int rs;
            rs = 2130968576;
            values = new ContentValues();
            values.put(
                    KEY_RESRAWID,
                    com.gosylvester.bestrides.ImageTextListViewActivity.SAMPLE_DRAGON);
            res = db.update(TABLE_RIDES, values, KEY_RESRAWID + " = ?",
                    new String[]  String.valueOf(rs) );

            rs = 2130968577;
            values = new ContentValues();
            values.put(
                    KEY_RESRAWID,
                    com.gosylvester.bestrides.ImageTextListViewActivity.SAMPLE_M119);
            res = db.update(TABLE_RIDES, values, KEY_RESRAWID + " = ?",
                    new String[]  String.valueOf(rs) );

            rs = 2130968578;
            values = new ContentValues();
            values.put(
                    KEY_RESRAWID,
                    com.gosylvester.bestrides.ImageTextListViewActivity.SAMPLE_MEDINA);
            res = db.update(TABLE_RIDES, values, KEY_RESRAWID + " = ?",
                    new String[]  String.valueOf(rs) );
            break;
        case 43:
            // new column added for last_viewed.
            db.execSQL(VERSION_43_ALTER);
            break;

        case 44:
            // new index on KEY_DATE_UPDATE DESC
            db.execSQL(VERSION_44_CREATE_INDEX);
        
        upgradeTo++;
    

祝你好运

【讨论】:

【参考方案2】:

即使 OP 表明它已在 cmets 中解决,这个问题也没有被接受的答案。所以这里是答案:

不要尝试使用从方法(例如onCreate)调用的AsyncTask 复制数据库,然后尝试使用同一方法中的数据库中的数据填充ListView。如果 AsyncTaskListView 需要数据时尚未完成,则会引发 NullPointerException。

而是从AsyncTaskonPostExecute 方法填充ListView。这将确保数据库已完成复制。

【讨论】:

以上是关于SQLiteAssetHelper NullPointerException 仅在某些设备上的主要内容,如果未能解决你的问题,请参考以下文章

Android开发中java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx}: java.lang.NullPoi

Android开发中java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx}: java.lang.NullPoi

我应该如何通知用户 SQLite Asset Helper 第一次加载数据库?

4-异常处理

使用自定义 Autowired 时出现 NullPointerException

从预填充的数据库中读取数据时出错