










Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Данный документ посвящен созданию класса-потомка SQLightOpenHelper для работы с БД в Android-приложении. А именно переопределению методов onCreate() для создания БД и onUpgrade() для обновления БД в случае изменений в схеме базы данных. Кроме того, рассматриваются методы getReadableDatabase() и getWriteableDatabase() для доступа к объекту SQLiteDatabase, а также создание, чтение, обновление и удаление данных в БД.
Typology: Cheat Sheet
1 / 18
This page cannot be seen from the preview
Don't miss anything!











Работа с базами данных. Использование SQLite. Создание запросов. Цель лабораторной работы: Приобретение навыков работы с базами данных в приложениях Android и использование SQLite. Создание запросов. Приобретение навыков создания контент-провайдеров. Задачи лабораторной работы: Создание приложения в Androidе с использованием базы данных Создание запросов Использование контент провайдеров Методические указания для выполнения работы SQLite — это база данных с открытыми исходными кодами, включаемая по умолчанию в состав Android. SQLite поддерживает стандартные возможности реляционных баз данных – синтаксис, транзакции и prepared statements. Кроме SQLite требует очень небольшого количества памяти для работы (примерно 250 кб). Для работы с БД в Android-приложении обычно создается класс-потомок SQLightOpenHelper. В этом классе следует переопределить методы onCreate() для создания БД и onUpgrade() для обновления БД в случае изменений в схеме базы данных. Оба метода работают с объектом SQLiteDatabase. SQLiteOpenHelper предоставляет методы getReadableDatabase () и getWriteableDatabase () для доступа к объекту SQLiteDatabase, который позволяет читать и писать в БД. Для первичного ключа БД всегда необходимо использовать идентификатор «_id», т.к. некоторые функции Android следуют этомуid», т.к. некоторые функции Android следуют этому стандарту. Класс SQLiteDatabase предоставляет методы insert (), update (), delete () и execSQL () (который дает возможность напрямую выполнять SQL-запросы). Объект ContentValues позволяет определить ключи/значения для вставки и обновления данных. Запросы могут быть созданы либо через метод rawQuery (), принимающий SQL, либо метод query (), предоставляющий интерфейс для указания динамических данных SQLiteQueryBuilder. SQLiteQueryBuilder схож с интерфейсом провайдера данных (сontent provider), поэтому обычно он используется для работы с провайдерами данных. Запрос к БД всегда возвращает «курсор», представляющий собой результат запроса. Чтобы получить число элементов в запросе, мы можем воспользоваться методом getCount(). Для перемещения между записями в результате используются методы moveToFirst() и moveToNext(). Метод isAfterLast() позволяет проверить, есть ли еще данные в результате. Метод выполнения запроса query() имеет параметры: String dbName, int[] columnNames, String whereClause, String[] valuesForWhereClause, String[] groupBy, String[] having, String[] orderBy.
Как видите, они соответствуют синтаксису SQL-запросов. Если вам нужно выбрать все данные, используйте null в условии where (null можно указать вместо любого из параметров запроса, например groupBy, orderBy и т.п.). Вообще условие where указывается без самого слова where, например: «_id», т.к. некоторые функции Android следуют этомуid = 19 and summary = ?». ». Возвращаемый курсор может быть использован напрямую с помощью SimpleCursorAdapter в ListViews. Мы создадим приложение «Список дел», которое позволяет пользователю вести список дел, которые нужно выполнить. Эти дела будут сохраняться в БД SQLite. Приложение будет состоять из двух активити, одно для просмотра всего списка дел и второе для создания конкретного дела. Связь между активити будет поддерживаться с помощью интентов. Результат будет выглядеть вот так: Рис. 7.1. Приложение «Список дел» Создайте проект «de.vogella.android.todos» с активити «TodosOverview» и добавьте еще одно активити«TodoDetails». Затем создайте пакет «de.vogella.android.todos.database», в котором будут содержаться классы для работы с базой данных. Начнем с создания следующего класса «TodoDatabaseHelper»: package de.vogella.android.todos.database; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class TodoDatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE_id», т.к. некоторые функции Android следуют этомуNAME = "applicationdata"; private static final int DATABASE_id», т.к. некоторые функции Android следуют этомуVERSION = 1; // запрос на создание базы данных
private SQLiteDatabase database; private TodoDatabaseHelper dbHelper; public TodoDbAdapter(Context context) { this.context = context; } public TodoDbAdapter open() throws SQLException { dbHelper = new TodoDatabaseHelper(context); database = dbHelper.getWritableDatabase(); return this; } public void close() { dbHelper.close(); } /** создать новый элемент списка дел. если создан успешно - возвращается номер строки rowId иначе - */ public long createTodo(String category, String summary, String description) { ContentValues initialValues = createContentValues(category, summary, description); return database.insert(DATABASE_id», т.к. некоторые функции Android следуют этомуTABLE, null, initialValues); } // обновить список public boolean updateTodo(long rowId, String category, String summary, String description) { ContentValues updateValues = createContentValues(category, summary, description); return database.update(DATABASE_id», т.к. некоторые функции Android следуют этомуTABLE, updateValues, KEY_id», т.к. некоторые функции Android следуют этомуROWID
Cursor mCursor = database.query(true, DATABASE_id», т.к. некоторые функции Android следуют этомуTABLE, new String[] { KEY_id», т.к. некоторые функции Android следуют этомуROWID, KEY_id», т.к. некоторые функции Android следуют этомуCATEGORY, KEY_id», т.к. некоторые функции Android следуют этомуSUMMARY, KEY_id», т.к. некоторые функции Android следуют этомуDESCRIPTION }, KEY_id», т.к. некоторые функции Android следуют этомуROWID + "=" + rowId, null, null, null, null, null); if (mCursor != null) { mCursor.moveToFirst(); } return mCursor; } private ContentValues createContentValues(String category, String summary, String description) { ContentValues values = new ContentValues(); values.put(KEY_id», т.к. некоторые функции Android следуют этомуCATEGORY, category); values.put(KEY_id», т.к. некоторые функции Android следуют этомуSUMMARY, summary); values.put(KEY_id», т.к. некоторые функции Android следуют этомуDESCRIPTION, description); return values; } } Добавим несколько ресурсов, которые мы будет использовать позже, в том числе listmenu.xml (в папке menu), который будет использоваться позже для создания новых дел в списке.
». xml version="1.0" encoding="utf-8"?». > Для списка приоритетов создадим массив строк. Добавьте такой файл priority.xml в папке /res/values. ». xml version="1.0" encoding="utf-8"?». >android:textSize="40px" android:layout_id», т.к. некоторые функции Android следуют этомуmarginTop="6px" android:layout_id», т.к. некоторые функции Android следуют этомуwidth="fill_id», т.к. некоторые функции Android следуют этомуparent" android:textColor="@color/black"> Создатим файл лэйаута «todo_id», т.к. некоторые функции Android следуют этомуedit». Этот лэйаут будет использован дальше для отображения и изменения отдельного элемента списка дел.
». xml version="1.0" encoding="utf-8"?». >import android.database.Cursor; import android.os.Bundle; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.ListView; import android.widget.SimpleCursorAdapter; import de.vogella.android.todos.database.TodoDbAdapter; public class TodosOverview extends ListActivity { private TodoDbAdapter dbHelper; private static final int ACTIVITY_id», т.к. некоторые функции Android следуют этомуCREATE = 0; private static final int ACTIVITY_id», т.к. некоторые функции Android следуют этомуEDIT = 1; private static final int DELETE_id», т.к. некоторые функции Android следуют этомуID = Menu.FIRST + 1; private Cursor cursor; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.todo_id», т.к. некоторые функции Android следуют этомуlist); this.getListView().setDividerHeight(2); dbHelper = new TodoDbAdapter(this); dbHelper.open(); fillData(); registerForContextMenu(getListView()); } // Создаем меню, основанное на XML-файле @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.listmenu, menu); return true; } // Реакция на выбор меню @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { switch (item.getItemId()) { case R.id.insert: createTodo(); return true; } return super.onMenuItemSelected(featureId, item); } @Override
SimpleCursorAdapter notes = new SimpleCursorAdapter(this, R.layout.todo_id», т.к. некоторые функции Android следуют этомуrow, cursor, from, to); setListAdapter(notes); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, DELETE_id», т.к. некоторые функции Android следуют этомуID, 0, R.string.menu_id», т.к. некоторые функции Android следуют этомуdelete); } @Override protected void onDestroy() { super.onDestroy(); if (dbHelper != null) { dbHelper.close(); } } } И затем «TodoDetails.java» package de.vogella.android.todos; import android.app.Activity; import android.database.Cursor; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import de.vogella.android.todos.database.TodoDbAdapter; public class TodoDetails extends Activity { private EditText mTitleText; private EditText mBodyText; private Long mRowId; private TodoDbAdapter mDbHelper; private Spinner mCategory; @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); mDbHelper = new TodoDbAdapter(this); mDbHelper.open(); setContentView(R.layout.todo_id», т.к. некоторые функции Android следуют этомуedit); mCategory = (Spinner) findViewById(R.id.category); mTitleText = (EditText) findViewById(R.id.todo_id», т.к. некоторые функции Android следуют этомуedit_id», т.к. некоторые функции Android следуют этомуsummary); mBodyText = (EditText) findViewById(R.id.todo_id», т.к. некоторые функции Android следуют этомуedit_id», т.к. некоторые функции Android следуют этомуdescription); Button confirmButton = (Button) findViewById(R.id.todo_id», т.к. некоторые функции Android следуют этомуedit_id», т.к. некоторые функции Android следуют этомуbutton); mRowId = null;
Bundle extras = getIntent().getExtras(); mRowId = (bundle == null) ?». null : (Long) bundle .getSerializable(TodoDbAdapter.KEY_id», т.к. некоторые функции Android следуют этомуROWID); if (extras != null) { mRowId = extras.getLong(TodoDbAdapter.KEY_id», т.к. некоторые функции Android следуют этомуROWID); } populateFields(); confirmButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { setResult(RESULT_id», т.к. некоторые функции Android следуют этомуOK); finish(); } }); } private void populateFields() { if (mRowId != null) { Cursor todo = mDbHelper.fetchTodo(mRowId); startManagingCursor(todo); String category = todo.getString(todo .getColumnIndexOrThrow(TodoDbAdapter.KEY_id», т.к. некоторые функции Android следуют этомуCATEGORY)); for (int i=0; i<mCategory.getCount();i++){ String s = (String) mCategory.getItemAtPosition(i); Log.e(null, s +" " + category); if (s.equalsIgnoreCase(category)){ mCategory.setSelection(i); }} mTitleText.setText(todo.getString(todo.getColumnIndexOrThrow(TodoDbAdapter.K EY_id», т.к. некоторые функции Android следуют этомуSUMMARY))); mBodyText.setText(todo.getString(todo.getColumnIndexOrThrow(TodoDbAdapter. KEY_id», т.к. некоторые функции Android следуют этомуDESCRIPTION))); } } protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); saveState(); outState.putSerializable(TodoDbAdapter.KEY_id», т.к. некоторые функции Android следуют этомуROWID, mRowId); } @Override protected void onPause() { super.onPause(); saveState(); } @Override protected void onResume() { super.onResume(); populateFields(); } private void saveState() { String category = (String) mCategory.getSelectedItem(); String summary = mTitleText.getText().toString();
соответствующие данные, принадлежащие другому приложению, в том числе данные стандартных БД Android. В общем случае, контент-провайдеры следует создавать только тогда, когда требуется предоставить другим приложениям доступ к данным вашего приложения. В остальных случаях рекомендуется использовать СУБД (SQLite). Тем не менее, иногда контент-провайдеры используются внутри одного приложения для поиска и обработки специфических запросов к данным. Для доступа к данным какого-либо контент-провайдера используется объект класса ContentResolver, который можно получить с помощью метода getContentResolver контекста приложения для связи с поставщиком в качестве клиента: ContentResolver cr = getApplicationContext().getContentResolver(); Объект ContentResolver взаимодействует с объектом контент-провайдера, отправляя ему запросы клиента. Контент-провайдер обрабатывает запросы и возвращает результаты обработки. Контент-провайдеры представляют свои данные потребителям в виде одной или нескольких таблиц подобно таблицам реляционных БД. Каждая строка при этом является отдельным «объектом» со свойствами, указанными в соответствующих именованных полях. Как правило, каждая строка имеет уникальный целочисленный индекс и именем «_id», т.к. некоторые функции Android следуют этомуid», который служит для однозначной идентификации требуемого объекта. Контент-провайдеры, обычно предоставляют минимум два URI для работы с данными: один для запросов, требующих все данные сразу, а другой – для обращения к конкретной «строке». В последнем случае в конце URI добавляется / (который совпадает с индексом «_id», т.к. некоторые функции Android следуют этомуid»). Запросы на получение данных похожи на запросы к БД, при этом используется метод query объекта ContentResolver. Ответ также приходит в виде курсора, «нацеленного» на результирующий набор данных (выбранные строки таблицы): ContentResolver cr = getContentResolver(); // получить данные всех контактов Cursor c = cr.query(ContactsContract.Contacts.CONTENT_id», т.к. некоторые функции Android следуют этомуURI, null, null, null, null); // получить все строки, где третье поле имеет конкретное // значение и отсортировать по пятому полю String where = KEY_id», т.к. некоторые функции Android следуют этомуCOL3 + "=" + requiredValue; String order = KEY_id», т.к. некоторые функции Android следуют этомуCOL5; Cursor someRows = cr.query(MyProvider.CONTENT_id», т.к. некоторые функции Android следуют этомуURI, null, where, null, order); Извлечение данных-результатов запроса с помощью курсора было рассмотрено ранее. Uri myRowUri = cr.insert(SampleProvider.CONTENT_id», т.к. некоторые функции Android следуют этомуURI, newRow); // Массовая вставка: ContentValues[] valueArray = new ContentValues[5]; // здесь заполняем массив
// делаем вставку int count = cr.bulkInsert(MyProvider.CONTENT_id», т.к. некоторые функции Android следуют этомуURI, valueArray); При вставке одного элемента метод insert возвращает URI вставленного элемента, а при массовой вставке возвращается количество вставленных элементов. Пример удаления: ContentResolver cr = getContentResolver(); // удаление конкретной строки cr.delete(myRowUri, null, null); // удаление нескольких строк String where = "_id», т.к. некоторые функции Android следуют этомуid < 5"; cr.delete(MyProvider.CONTENT_id», т.к. некоторые функции Android следуют этомуURI, where, null); Пример изменения: ContentValues newValues = new ContentValues(); newValues.put(COLUMN_id», т.к. некоторые функции Android следуют этомуNAME, newValue); String where = "_id», т.к. некоторые функции Android следуют этомуid < 5"; getContentResolver().update(MyProvider.CONTENT_id», т.к. некоторые функции Android следуют этомуURI, newValues, where, null); Для чтения информации о контактах используется контент-провайдер ContactsContract, точнее, один из его подклассов. Для этой лабораторной работы воспользуемся провайдером ContactsContract.Contacts. Для чтения контактов приложению требуются полномочия READ_id», т.к. некоторые функции Android следуют этомуCONTACTS.
private static final UriMatcher uriMatcher; // Заполнение UriMatcher'аа // Если URI оканчивается на /stations - это запрос про все станции // Если на stations/[ID] - про конкретную станцию static { uriMatcher = new UriMatcher(UriMatcher.NO_id», т.к. некоторые функции Android следуют этомуMATCH); uriMatcher.addURI("com.example.provider.metropicker", "stations", ALL_id», т.к. некоторые функции Android следуют этомуSTATIONS); uriMatcher.addURI("com.example.provider.metropicker", "stations/#", SINGLE_id», т.к. некоторые функции Android следуют этомуSTATION); } В дальнейшем полученный в запросе к контент-провайдеру URI проверяется в методах класса следующим образом: @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch (uriMatcher.match(uri)) { case ALL_id», т.к. некоторые функции Android следуют этомуSTATIONS: // Вернуть курсор, указывающий на выборку со // всеми станциями case SINGLE_id», т.к. некоторые функции Android следуют этомуSTATION: // Вытащить ID станции из URI: String _id», т.к. некоторые функции Android следуют этомуid = uri.getPathSegments().get(1); // Вернуть курсор, указывающий на выборку с одной // станцией. } return null; } При наполнении объекта UriMatcher шаблонами могут применять в «#» и «*» в качестве специальных символов: # в шаблоне совпадает с любым числом, а * - с любым текстом. Кроме уникальных URI, используемым создаваемым контент-провайдером, для определения клиентами типа возвращаемых по запросу данных используются уникальные типы MIME. Метод getType класса ContentProvider обычно возвращает один тип данных для массовой выборки, а другой – для одиночных записей. В нашем случае это могло бы выглядеть так: @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case ALL_id», т.к. некоторые функции Android следуют этомуSTATIONS: return "vnd.com.example.cursor.dir/station"; case SINGLE_id», т.к. некоторые функции Android следуют этомуSTATION: return "vnd.com.example.cursor.item/station"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } }
Для того, что бы Android знал о существовании и мог использовать (через ContentResolver) ваш контент-провайдер, его нужно описать в Манифесте приложения. Минимальное описание выглядит так: