моб разраб. Лабораторные работы с 1 по 21. Лабораторная работа Использование значений строк и цветов
Скачать 5.49 Mb.
|
Работа с базами данных в AndroidМеханизм работы с базами данных в Android позволяет хранить и обрабатывать структурированную информацию. Любое приложение может создавать свои собственные базы данных, над которыми оно будет иметь полный контроль. В Android используется библиотека SQLite, представляющую из себя реляционную СУБД, обладающую следующими характерными особенностями: свободно распространяемая (open source), поддерживающая стандартный язык запросов и транзакции, легковесная, одноуровневая (встраиваемая), отказоустойчивая. Курсоры (Cursor) и ContentValues Запросы к СУБД возвращают объекты типа Cursor. Для экономии ресурсов используется подход, когда при извлечении данных не возвращаются копии их значений из СУБД, а создается Cursor, обеспечивающий навигацию и доступ к запрошенному набору исходных данных. Методы объекта Cursor предоставляют различные возможности навигации, назначение которых, как правило, понятно из названия: moveToFirst moveToNext moveToPrevious getCount getColumnIndexOrThrow getColumnName getColumnNames moveToPosition getPosition При добавлении данных в таблицы СУБД применяются объекты класса ContentValues. Каждый такой объект содержит данные одной строки в таблице и, по сути, является ассоциативным массивом с именами столбцов и соответствующими значениями. Работа с СУБД SQLite При создании приложений, использующих СУБД, во многих случаях удобно применять инструменты, называющиеся ORM (Object-Relationship Mapping), отображающие данные из одной или нескольких таблиц на объекты используемого языка программирования. Кроме того, ORM позволяют абстрагироваться от конкретной реализации и структуры таблиц и берут на себя обязанности по взаимодействию с СУБД. К сожалению, в силу ограниченности ресурсов мобильной платформы ORM в настоящий момент в Android практически не применяется. Тем не менее, разумным подходом при разработке приложения будет инкапсуляция всех взаимодействий с СУБД в одном классе, методы которого будут предоставлять необходимые услуги остальным компонентам приложения. Хорошей практикой является создание вспомогательного класса, берущего на себя работу с СУБД. Данный класс обычно инкапсулирует взаимодействия с базой данных, предоставляя интуитивно понятный строго типизированный способ удаления, добавления и изменения объектов. Такой Адаптербазыданныхтакже должен обрабатывать запросы к БД и переопределять методы для открытия, закрытия и создания базы данных. Его также обычно используют как удобное место для хранения статических констант, относящихся к базе данных, таких, например, как имена таблиц и полей. Ниже показан пример каркаса для реализации подобного Адаптера: public class SampleDBAdapter { private static final String DATABASE_NAME= "SampleDatabase.db"; private static final String DATABASE_TABLE= "SampleTable"; private static final int DATABASE_VERSION= 1; // Имя поля индекса для public static final String KEY_ID= "_id"; // Название и номер п/п (индекс) каждого поля public static final String KEY_NAME= "name"; public static final int NAME_COLUMN= 1; // Для каждого поля опишите константы аналогичным образом... // SQL-запрос для создания БД private static final String DATABASE_CREATE= "create table " + DATABASE_TABLE+ " (" + KEY_ID + " integer primary key autoincrement, " + KEY_NAME + " text not null);"; // Переменная для хранения объекта БД private SQLiteDatabase db; // Контекст приложения для private final Context context; // Экземпляр вспомогательного класса для открытия и обновления БД private myDbHelper dbHelper; // Конструктор public SampleDBAdapter(Context _context) { context = _context; dbHelper = new myDbHelper(context, DATABASE_NAME, null, DATABASE_VERSION); } // «Открывашка» БД public SampleDBAdapter open() throws SQLException { try { db = dbHelper.getWritableDatabase(); } catch (SQLiteException e) { db = dbHelper.getReadableDatabase(); } return this; } // Метод для закрытия БД public void close() { db.close(); } // Метод для добавления данных, возвращает индекс // свежевставленного объекта public long insertEntry(SampleObject _SampleObject) { // Здесь создается объект ContentValues, содержащий // нужные поля и производится вставка return index; } // Метод для удаления строки таблицы по индексу public boolean removeEntry(long _rowIndex) { return db.delete(DATABASE_TABLE, KEY_ID+ "=" + _rowIndex, null) > 0; } // Метод для получения всех данных. // Возвращает курсор, который можно использовать для // привязки к адаптерам типа SimpleCursorAdapter public Cursor getAllEntries() { return db.query(DATABASE_TABLE, new String[] { KEY_ID, KEY_NAME}, null, null, null, null, null); } // Возвращает экземпляр объекта по индексу public SampleObject getEntry(long _rowIndex) { // Получите курсор, указывающий на нужные данные из БД // и создайте новый объект, используя этими данными // Если ничего не найдено, верните null return objectInstance; } // Изменяет объект по индексу // Увы, это не ORM :( public boolean updateEntry(long _rowIndex, SampleObject _SampleObject) { // Создайте объект ContentValues на основе свойств SampleObject // и используйте его для обновления строки в таблице return true; // Если удалось обновить, иначе false :) } // Вспомогательный класс для открытия и обновления БД private static class myDbHelper extends SQLiteOpenHelper { public myDbHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } // Вызывается при необходимости создания БД @Override public void onCreate(SQLiteDatabase _db) { _db.execSQL(DATABASE_CREATE); } // Вызывается для обновления БД, когда текущая версия БД // в приложении новее, чем у БД на диске @Override public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) { // Выдача сообщения в журнал, полезно при отладке Log.w("TaskDBAdapter", "Upgrading from version " + _oldVersion + " to " + _newVersion + ", which will destroy all old data"); // Обновляем БД до новой версии. // В простейшем случае убиваем старую БД // и заново создаем новую. // В реальной жизни стоит подумать о пользователях // вашего приложения и их реакцию на потерю старых данных. _db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE); onCreate(_db); } } } Работа с СУБД без адаптера При нежелании использовать класс SQLiteOpenHelper (и вообще адаптер БД) можно воспользоваться методом openOrCreateDatabase контекста приложения: private static final String DATABASE_NAME = "myDatabase.db"; private static final String DATABASE_TABLE = "mainTable"; private static final String DATABASE_CREATE = "create table " + DATABASE_TABLE + " ( _id integer primary key autoincrement," + "column_one text not null);"; SQLiteDatabase myDatabase; private void createDatabase() { myDatabase = openOrCreateDatabase(DATABASE_NAME, MODE_PRIVATE, null); myDatabase.execSQL(DATABASE_CREATE); } В этом случае можно работать с базой данных с помощью, например, метода execSQL экземпляра БД, как показано на примере выше. Особенности работы с БД в Android При работе с базами данных в Android следуетизбегатьхранения BLOB'ов с таблицах из- за резкого падения эффективности работы. Как показано в примере адаптера БД, для каждой таблицы рекомендуется создавать автоикрементное поле _id, которое будет уникальным индексом для строк. Если же планируется делегировать доступ к БД с помощью контент-провайдеров, такое поле является обязательным. Выполнение запросов для доступа к данным Для повышения эффективности использования ресурсов мобильной платформы запросы к БД возвращают объект типа Cursor, используемый в дальнейшем для навигации и получения значений полей. Выполнение запросов осуществляется с помощью метода query экземпляра БД, параметры которого позволяют гибко управлять критериями выборки: // Получить поля индекса, а также первое и третье из таблицы, // без дубликатов String[] result_columns = new String[] {KEY_ID, KEY_COL1, KEY_COL3}; Cursor allRows = myDatabase.query(true, DATABASE_TABLE, result_columns, null, null, null, null, null, null); // Получить все поля для строк, где третье поле равно требуемому // значению, // результат отсортировать по пятому полю String where = KEY_COL3 + "=" + requiredValue; String order = KEY_COL5; Cursor myResult = myDatabase.query(DATABASE_TABLE, null, where, null, null, null, order); Доступ к результатам с помощью курсора Для получения результатов запроса необходимо установить курсор на нужную строку с помощью методов вида moveToМестоположение, перечисленных выше. После этого используются типизированные методы getТип, получающие в качестве параметра индекс (номер) поля в строке. Как правило, эти значения являются статическими константами адаптера БД: String columnValue = myResult.getString(columnIndex); В примере ниже показано, как можно просуммировать все поля (типа float) из результатов выполнения запроса (и получить среднюю сумму счета): int KEY_AMOUNT = 4; Cursor myAccounts = myDatabase.query("my_bank_accounts", null, null, null, null, null, null); float totalAmount = 0f; // Убеждаемся, что курсор непустой if (myAccounts.moveToFirst()) { // Проходимся по каждой строке do { float amount = myAccounts.getFloat(KEY_AMOUNT); totalAmount += amount; } while(myAccounts.moveToNext()); } float averageAmount = totalAmount / myAccounts.getCount(); Изменение данных в БД В классе SQLiteDatabase, содержащем методы для работы с БД, имеются методы insert, update и delete, которые инкапсулируют операторы SQL, необходимые для выполнения соответствующих действий. Кроме того, метод execSQL позволяет выполнить любой допустимый код SQL(если вы, например, захотите увеличить долю ручного труда при создании приложения). Следует помнить, что при наличии активных курсоров после любого изменения данных следует вызывать метод refreshQuery для всех курсоров, которые имеют отношение к изменяемым данным (таблицам). Вставка строк Метод insert хочет получать (кроме других параметров) объект ContentValues, содержащий значения полей вставляемой строки и возвращает значение индекса: ContentValues newRow = new ContentValues(); // Выполним для каждого нужного поля в строке newRow.put(COLUMN_NAME, columnValue); db.insert(DATABASE_TABLE, null, newRow); Обновление строк Также используется ContentValues, содержащий подлежащие изменению поля, а для указания, какие именно строки нужно изменить, используется параметр where, имеющий стандартный для SQL вид: ContentValues updatedValues = new ContentValues(); // Повторяем для всех нужных полей updatedValues.put(COLUMN_NAME, newValue); // Указываем условие String where = COLUMN_NAME + "=" + "'Бармаглот'"; // Обновляем db.update(DATABASE_TABLE, updatedValues, where, null); Метод update возвращает количество измененных строк. Если в качестве параметра where передать null, будут изменены все строки таблицы. Удаление строк Выполняет похожим на update образом: db.delete(DATABASE_TABLE, KEY_ID + "=" + rowId, null); Лабораторная работа «работа с SQLite без класса-адаптера» Цель данной лабораторной работы – освоить взаимодействие с СУБД SQLite без применения специальных классов-адаптеров. Создайте новый проект SQLTest, главная Активность которого будет расширять класс ListActivity. В методе onCreate главной Активности сделайте открытие или создание БД, используя в качестве основы информацию из пункта «Работа с СУБД без адаптера» и метода onUpgrade вспомогательного класса из предшествующего ему пункта. После создания БД создайте таблицу с любым полем (плюс индекс), в которую добавьте 3..5 уникальных записей со строковым полем. Индекс должен инкрементироваться автоматически. В этом же методе сделайте запрос к таблице, получающий все строки и с помощью имеющегося курсора запишите данные в массив строк. С помощью дополнительной разметки и адаптера ArrayAdapterотобразите полученные из СУБД данные на экране Активности. Добавьте обработчик клика на элемент списка, чтобы при клике запрашивался из БД индекс элемента с этой строкой и отображался с помощью Toast. Добавьте контекстное меню к элементам списка, содержащее пункт «удалить» и реализуйте удаление. После удаления должны производится действия из п.п. 3 и 4 для изменения состава отображаемых данных. Использование SimpleCursorAdapter SimpleCursorAdapter позволяет привязать курсор к ListView. используя описание разметки для отображения строк и полей. Для однозначного определения того, в каких элементах разметки какие поля, получаемые через курсор, следует отображать, используются два массива: строковый с именами полей строк, и целочисленный с идентификаторами элементов разметки: Cursor cursor = [ . . . запрос к БД . . . ]; String[] fromColumns = new String[] {KEY_NAME, KEY_NUMBER}; int[] toLayoutIDs = new int[] { R.id.nameTextView, R.id.numberTextView}; SimpleCursorAdapter myAdapter; myAdapter = new SimpleCursorAdapter(this, R.layout.item_layout, cursor, fromColumns, toLayoutIDs); myListView.setAdapter(myAdapter); Лабораторная работа «работа с SQLite с классом-адаптером» Целью работы является создание простого приложения, позволяющего создавать, хранить и удалять короткие заметки. Создайте новый проект NotesSample. Для простоты использования все действия с записями будут производиться в рамках одной Активности, поэтому используйте максимально упрощенный интерфейс, как показано на следующей странице. Для реализации можно воспользоваться такой разметкой в файле res/layout/main.xml: "1.0" encoding="utf-8"?> android:orientation="vertical" > android:id="@+id/save_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:text="@android:string/ok"/> android:id="@+id/edit_text" android:layout_width="fill_parent" android:layout_toLeftOf="@id/save_button" android:layout_height="wrap_content" android:hint="Новаязапись"/> android:id="@+id/myListView" android:layout_width="fill_parent" android:layout_height="wrap_content"/> Реализуйте сохранение записей по нажатию на кнопку, а удаление через контекстное меню. Работа с СУБД должна осуществляться с использованием адаптера. Пример обработчика выбора пункта контекстного меню приведен в соответствующем разделе данного методического руководства. |