Android: Как создать свой собственный класс курсора?

#android #database #sqlite #android-cursor

#Android #База данных #sqlite #android-cursor

Вопрос:

Я использую Sqlite в Android и для получения значения из базы данных я использую что-то вроде этого:

 Cursor cursor = sqliteDatabase.rawQuery("select title,category from table", null);

int columnIndexTitle = cursor.getColumnIndex("title");
iny columnIndexCategory = cursor.getColumnIndex("category");

cursor.moveToFirst();
while (cursor.moveToNext()) {
    String title = cursor.getString(columnIndexTitle);  
    String category = cursor.getString(columnIndexCategory);    
}
cursor.close();
  

Я хочу создать свой собственный курсор, чтобы я мог выполнять getColumnIndex() и getString() одним методом. Что-то вроде этого:

 String title = cursor.getString("title");
  

Я хочу создать свой собственный класс, который расширяет курсор, который я получаю от sqliteDatabase.rawQuery , но я не уверен, как это сделать. Должен ли я расширить SQLiteCursor или как я должен это сделать? Возможно ли это вообще и хорошая ли это идея?

Комментарии:

1. У вас есть несколько строк? Создание вашей собственной getString вызовет поиск по карте для каждого вызова, а не только для getColumnIndex .

Ответ №1:

Я наткнулся на этот вопрос в поисках наилучшего способа создать пользовательский Cursor для использования вместе с SQLiteDatabase . В моем случае мне нужен был дополнительный атрибут для курсора, чтобы передавать дополнительную информацию, поэтому мой вариант использования не совсем такой, как в тексте вопроса. Публикую свои выводы в надежде, что это будет полезно.

Сложность для меня заключалась в том, что методы запроса SQLiteDatabase возвращают курсор, и мне нужно было передать пользовательский подкласс Cursor.

Я нашел решение в Android API: используйте класс CursorWrapper. Кажется, он предназначен именно для этого.

Класс:

 public class MyCustomCursor extends CursorWrapper {

    public MyCustomCursor(Cursor cursor) {
        super(cursor);
    }

    private int myAddedAttribute;

    public int getMyAddedAttribute() {
        return myAddedAttribute;
    }

    public void setMyAddedAttribute(int myAddedAttribute) {
        this.myAddedAttribute = myAddedAttribute;
    }

}
  

Использование:

 public MyCustomCursor getCursor(...) {
    SQLiteDatabase DB = ...;
    Cursor rawCursor = DB.query(...);
    MyCustomCursor myCursor = new MyCustomCursor(rawCursor);
    myCursor.setMyAddedAttribute(...);
    return myCursor;
}
  

Ответ №2:

Создание вашей собственной getString вызовет поиск по карте для каждого вызова, а не только для getColumnIndex .

Вот код для SQLiteCursor.getColumnIndex и AbstractCursor.getColumnIndex. Если у вас много строк, сокращение вызовов этой функции предотвратит ненужную обработку строк и поиск по карте.

Ответ №3:

Я бы не стал его расширять, я бы создал вспомогательный:

 class MartinCursor {
    private Cursor cursor;

    MartinCursor(Cursor cursor) {
        this.cursor = cursor;
    }

    String getString(String column) {
        ....
    }
}
  

или

 class MartinCursorHelper {
    static String getString(Cursor cursor, String column) {
        ....
    }
}
  

Лично я бы сделал последнее, если только вам не неприятно постоянно предоставлять этот дополнительный аргумент.

РЕДАКТИРОВАТЬ: Я забыл упомянуть важный момент pydave: если вы вызываете это в цикле, вы настраиваете себя на заметное влияние на производительность. Предпочтительный способ — выполнить поиск по индексу один раз, кэшировать его и использовать вместо этого.

Комментарии:

1. Спасибо! Похоже, это решение, которое сработает. Но возможно ли вместо этого расширить его и не использовать помощник?

2. Не совсем, поскольку экземпляр объекта создается операционной системой, а не вами, и нет способа создать подкласс курсора из другого.

Ответ №4:

Вам следует использовать статический метод DatabaseUtils.stringForQuery(), который уже есть в Android SDK, чтобы легко извлекать значение, этот пример предназначен для String бота. существует также метод для Long

 stringForQuery(SQLiteDatabase db, String query, String[] selectionArgs)
  

Служебный метод для запуска запроса в базе данных и возврата значения в первом столбце первой строки.

Что-то вроде

 String myString=DatabaseUtils.stringForQuery(getDB(),query,selectionArgs);
  

Комментарии:

1. О, извините, это было не то, что мне было нужно. Мой пример был, возможно, немного простым, я добавил некоторый код, чтобы показать, что я хотел.

Ответ №5:

Наткнулся на это в поисках другого решения, но просто хочу добавить это, поскольку я считаю, что ответы неудовлетворительны.

Вы можете легко создать свой собственный класс курсора. Чтобы разрешить функциям, требующим Cursor, принимать его, он должен расширять AbstractCursor . Чтобы решить проблему, из-за которой система не использует ваш класс, вы просто делаете свой класс оболочкой.

Здесь есть действительно хороший пример. https://android.googlesource.com/platform/packages/apps/Contacts/ /8df53636fe956713cc3c13d9051aeb1982074286/src/com/android/contacts/calllog/ExtendedCursor.java

 public class ExtendedCursor extends AbstractCursor {
/** The cursor to wrap. */
private final Cursor mCursor;
/** The name of the additional column. */
private final String mColumnName;
/** The value to be assigned to the additional column. */
private final Object mValue;
/**
 * Creates a new cursor which extends the given cursor by adding a column with a constant value.
 *
 * @param cursor the cursor to extend
 * @param columnName the name of the additional column
 * @param value the value to be assigned to the additional column
 */
public ExtendedCursor(Cursor cursor, String columnName, Object value) {
    mCursor = cursor;
    mColumnName = columnName;
    mValue = value;
}
@Override
public int getCount() {
    return mCursor.getCount();
}
@Override
public String[] getColumnNames() {
    String[] columnNames = mCursor.getColumnNames();
    int length = columnNames.length;
    String[] extendedColumnNames = new String[length   1];
    System.arraycopy(columnNames, 0, extendedColumnNames, 0, length);
    extendedColumnNames[length] = mColumnName;
    return extendedColumnNames;
}
  

Это общая идея того, как это будет работать.

Теперь перейдем к сути проблемы. Чтобы предотвратить снижение производительности, создайте хэш для хранения индексов столбцов. Это будет служить кешем. При вызове getString проверьте хэш для индекса столбца. Если он не существует, извлеките его с помощью getColumnIndex и кэшируйте его.

Извините, я не могу добавить какой-либо код в настоящее время, но я на мобильном устройстве, поэтому я попытаюсь добавить немного позже.