为什么ContentProvider在App更新后返回空游标?

时间:2019-08-06 21:35:28

标签: java android android-sqlite android-contentprovider

我最近为我的应用推出了一个更新,并且我从一些用户获得反馈,称他们的数据丢失了。经过分析,我确定我使用的单个内容提供者始终返回一个空游标。

我没有更改内容提供商本身的代码或查询它的代码。数据格式没有改变。该问题仅在少数设备上可见,并且似乎仅限于Android 6.0(主要是三星,但也包括某些LG设备)。不仅会丢失初始数据,而且所有随后的插入操作不会保留

可以通过全新安装应用(删除+安装)解决该问题。

有人知道如何解决/解决方法吗?

我认为这无关紧要,但这是代码:

Cursor cursor = contentResolver.query(Columns.contentUri(), Columns.ALARM_QUERY_COLUMNS, null, null, Columns.DEFAULT_SORT_ORDER);

List<AlarmActiveRecord> alarms = new ArrayList<AlarmActiveRecord>();
try {
    if (cursor.moveToFirst()) {
        do {
            alarms.add(factory.create(cursor));
        } while (cursor.moveToNext());
    }
} finally {
    cursor.close();
}
return alarms;

以及内容提供商:

@Override
public Cursor query(Uri url, String[] projectionIn, String selection, String[] selectionArgs, String sort) {
    SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

    // Generate the body of the query
    Preconditions.checkArgument(sURLMatcher.match(url) == ALARMS, "Invalid URL %s", url);
    qb.setTables("alarms");

    SQLiteDatabase db = mOpenHelper.getReadableDatabase();
    Cursor ret;
    try {
        ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);
    } catch (SQLException e) {
        log.e("query failed because of " + e.getMessage() + ", recreating DB");
        db.execSQL("DROP TABLE IF EXISTS alarms");
        // I know this is not nice to call onCreate() by ourselves :-)
        mOpenHelper.onCreate(db);
        ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);
    }
    if (ret != null) {
        ret.setNotificationUri(getContext().getContentResolver(), url);
    }

    return ret;
}

还有数据库助手:

public class AlarmDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "alarms.db";
private static final int DATABASE_VERSION = 5;
private final Logger log;

public AlarmDatabaseHelper(Context context, Logger log) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
    this.log = log;
}

@Override
public void onCreate(SQLiteDatabase db) {
    // @formatter:off
    db.execSQL("CREATE TABLE alarms (" +
            "_id INTEGER PRIMARY KEY," +
            "hour INTEGER, " +
            "minutes INTEGER, " +
            "daysofweek INTEGER, " +
            "alarmtime INTEGER, " +
            "enabled INTEGER, " +
            "vibrate INTEGER, " +
            "message TEXT, " +
            "alert TEXT, " +
            "prealarm INTEGER, " +
            "state STRING);");
    // @formatter:on
    // insert default alarms
    String insertMe = "INSERT INTO alarms " + "(hour, minutes, daysofweek, alarmtime, enabled, vibrate, "
            + "message, alert, prealarm, state) VALUES ";
    db.execSQL(insertMe + "(8, 30, 31, 0, 0, 1, '', '', 0, '');");
    db.execSQL(insertMe + "(9, 00, 96, 0, 0, 1, '', '', 0, '');");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
    log.d("Upgrading alarms database from version " + oldVersion + " to " + currentVersion
            + ", which will destroy all old data");
    db.execSQL("DROP TABLE IF EXISTS alarms");
    onCreate(db);
}

public Uri commonInsert(ContentValues values) {
    SQLiteDatabase db = getWritableDatabase();
    long rowId = db.insert("alarms", Columns.MESSAGE, values);
    if (rowId < 0) throw new SQLException("Failed to insert row");
    log.d("Added alarm rowId = " + rowId);

    return ContentUris.withAppendedId(Columns.contentUri(), rowId);
}
}

1 个答案:

答案 0 :(得分:0)

该迁移脚本导致了所描述的行为。 DROP TABLE确实按照其说的去做,因此,除非数据库的旧版本已备份到云中,否则所有先前输入的记录都将丢失。此问题可能不仅限于Samsung或LG设备,因为该部分是明确的:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
    db.execSQL("DROP TABLE IF EXISTS alarms");
}

您需要将数据库从一个版本迁移到另一个版本;为example

Room也支持Migrating Room databases(情况相同)。