Сегодня, как и было обещано в предыдущем посте о разработке графических элементов, речь пойдет о разработке баз данных для Android. В приложениях под Android используются базы данных SQLite, которые представляют собой один из пяти способов хранения данных в Android. Мы будем рассматривать только базы данных SQLite, поскольку это и есть основа построения рабочей и функциональной программы. После освоения этого поста вы сможете вставлять данные из таблиц в базы данных, и проводить их отбор.

Для этого проекта мы будем создавать генератор Random Quote, который будет вносить в базу данных цитаты или высказывания, введенные пользователем в текстовое поле. Также будет предусмотрена кнопка, при нажатии которой текстовое поле очистится и на экране появится сообщение о том, были ли введенные данные успешно внесены в базу данных. При нажатии второй кнопки из базы данных случайным образом выбирается какое-то высказывание и появляется на экране в виде toast-а.

Начнем с создания нового проекта под названием RandomQuotes. В первой части нашей серии статей об Android-разработках процесс создания нового проекта был детально описан, так что останавливаться на пошаговой инструкции снова мы не будем. Данные, которые необходимо внести в форму создания нового проекта следующие:

Project Name: RandomQuotes
Build Target: Android 1.5
Application Name: RandomQuotes
Package Name: com.gregjacobs.randomquotes
Create Activity: QuotesMain
Min SDK Version: 3
Настройки нового проекта

После введения данных и нажатия на кнопку Finish приступим к созданию файла класса в нашем пакете под названием com.gregjacobs.randomquotes. Для этого нужно нажать правой кнопкой мыши на пакете, выбрать в появившемся меню New, затем Class. В появившемся окне нужно заполнить только поле Name, введя в него DBAdapter. Далее жмем Finish и получаем базовый файл для класса, который нам предстоит немного видоизменить. В этом разделе я буду действовать также, как и в предыдущем: сначала приведу код, а потом объясню основные его части, назначение главных функций. Вдобавок в этот раз я приведу текстовые файлы, так чтобы вы могли из загрузить и сравнить со своим вариантом. Начнем с файла DBAdapter.java:

DBAdapter.java, часть 1 DBAdapter.java, часть 2 DBAdapter.java, часть 3 DBAdapter.java, часть 4

view plaincopy to clipboardprint?
package com.gregjacobs.randomquotes; 
   
import java.util.Random; 
   
import android.content.ContentValues; 
import android.content.Context; 
import android.database.Cursor; 
import android.database.SQLException; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteOpenHelper; 
import android.util.Log; 
Начнем с импорта всех инструментов, которые понадобятся для создания и функционирования нашей базы данных SQLite. Возможно, профессиональным программистам не понадобятся дальнейшие пояснения, что означает каждая строка, но для начинающих они будут полезными. Итак, СontentValues дает возможность хранить набор значений для операторов insert (оператор вставки), Context, как уже было сказано в предыдущем посте, дает доступ к среде приложения. Cursor - наверное, самый нужный импорт из всех. Курсор позволяет иметь доступ к данным, полученным из БД. SQLException позволяет выбрасывать SQL исключения при появлении ошибки. Эти сообщения позволяют понять, чем конкретно вызвана ошибка. SQLiteDatabase дает возможность управлять базой данных SQLite, используя методы. SQLiteOpenHelper представляет собой класс-помощник в управлении БД. Log будет логировать вывод данных в случае возникновения ошибки.

view plaincopy to clipboardprint?
public class DBAdapter 

    int id = 0; 
    public static final String KEY_ROWID = "_id"; 
    public static final String KEY_QUOTE = "Quote"; 
    private static final String TAG = "DBAdapter"; 
 
    private static final String DATABASE_NAME = "Random"; 
    private static final String DATABASE_TABLE = "tblRandomQuotes"; 
    private static final int DATABASE_VERSION = 1; 
 
    private static final String DATABASE_CREATE = 
        "create table tblRandomQuotes (_id integer primary key autoincrement, " 
        + "Quote text not null );"; 
 
    private final Context context; 
 
    private DatabaseHelper DBHelper; 
    private SQLiteDatabase db; 
Здесь мы определяем все нужные нам переменные, начиная с названия базы данных и заканчивая запросом создания самой базы данных. Мы используем final переменные т.к. их значения не будут меняться в ходе работы программы, но при этом, выносим переменные вроде имён таблицы в отдельные переменные, чтобы облегчить модификацию программы в дальнейшем.

view plaincopy to clipboardprint?
public DBAdapter(Context ctx) 

    this.context = ctx; 
    DBHelper = new DatabaseHelper(context); 

 
vate static class DatabaseHelper extends SQLiteOpenHelper 

    DatabaseHelper(Context context) 
    { 
        super(context, DATABASE_NAME, null, DATABASE_VERSION); 
    } 
 
    @Override 
    public void onCreate(SQLiteDatabase db) 
    { 
        db.execSQL(DATABASE_CREATE); 
    } 
 
    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, 
                          int newVersion) 
    { 
        Log.w(TAG, "Upgrading database from version " + oldVersion 
              + " to " 
              + newVersion + ", which will destroy all old data"); 
        db.execSQL("DROP TABLE IF EXISTS tblRandomQuotes"); 
        onCreate(db); 
    } 

В вышеуказанном фрагменте кода мы определяем конструктор, который будет передавать контекст предложения нашему помощнику, DatabaseHelper. Класс DatabaseHelper расширяет возмошности нашего SQLiteOpenHelper, который улучшает функционал управления базой данных SQLite. Главная функция onCreate, которую нам еще предстоит использовать далее, позволяет выполнить SQL-запрос по созданию базы данных.

view plaincopy to clipboardprint?
//---открывает базу данных--- 
public DBAdapter open() throws SQLException 

    db = DBHelper.getWritableDatabase(); 
    return this; 

 
//---закрывает базу данных--- 
public void close() 

    DBHelper.close(); 

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

view plaincopy to clipboardprint?
//---вставляем заголовок в базу данных--- 
public long insertQuote(String Quote) 

    ContentValues initialValues = new ContentValues(); 
    initialValues.put(KEY_QUOTE, Quote); 
    return db.insert(DATABASE_TABLE, null, initialValues); 

Вышеописанная функция обрабатывает наши цитаты, когда мы вызываем их в главном .java файле. Также эта функция подготавливает цитаты для ввода в БД, помещая строку Quote в ContentValues под названием initialValues, которые затем вставляются в таблицу БД.

view plaincopy to clipboardprint?
public int getAllEntries() 

    Cursor cursor = db.rawQuery( 
                "SELECT COUNT(Quote) FROM tblRandomQuotes", null); 
            if(cursor.moveToFirst()) { 
                return cursor.getInt(0); 
            } 
            return cursor.getInt(0); 
 

Эта функция будет выполнять запрос на количество введенных в БД цитат для передачи этого значения в качестве максимально возможного генератору случайных чисел. Таким удастся избежать ошибки (выбранный номер цитаты никогда не будет превышать максимально возможный). Мы в основном используем в работе тип запроса rawQuery, потому что лично мне не очень нравится как Android обрабатывает свои запросы, но я впечатлен возможностью использования полнофункциональных SQL-запросов. Условие 'if' заставит указатель принять первый результат (если будет найдено несколько результатов). Если условие 'if' не действительно, все равно за результат примется значение первой позиции.

view plaincopy to clipboardprint?
    public String getRandomEntry() 
    { 
   
        id = getAllEntries(); 
        Random random = new Random(); 
        int rand = random.nextInt(getAllEntries()); 
        if(rand == 0) 
            ++rand; 
        Cursor cursor = db.rawQuery( 
                    "SELECT Quote FROM tblRandomQuotes WHERE _id = " + rand, null); 
                if(cursor.moveToFirst()) { 
                    return cursor.getString(0); 
                } 
                return cursor.getString(0); 
   
    } 
   

Эта функция будет вызываться главной .java программой для возвращения случайного результата, основываясь на числе записей в нашей базе дынных. Для получения числа записей используется функция getAllEntries. Мы делаем так, чтобы наша случайная переменная не превышала значение id. В операторе 'select' мы реализовываем поиск цитаты под номером по заданному случайному идентификатору ID, WHERE _id = rand.После компиляции этого файла класса получаем полностью пригодный к работе адаптер базы данных, готовый вставлять цитаты в БД. Сейчас остановимся на обоих XML-файлах нашего проекта. Не будем слишком вдаваться в подробности, так как многое повторяется из предыдущего поста. Это main.xml:

main.xml

view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    > 
<TextView 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/Quote" 
/> 
<EditText 
android:id="@+id/Quote" 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
/> 
<Button 
android:id="@+id/go" 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="@string/press" 
/> 
<Button 
android:id="@+id/genRan" 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="@string/genRan" 
/> 
</LinearLayout> 
Это файл strings.xml:

strings.xml

view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <string name="Quote">Please Enter A Quote:</string> 
    <string name="app_name">Random Quotes</string> 
    <string name="press">Press Me!</string> 
    <string name="genRan">Generate Random Quote!</string> 
</resources> 
Единственная разница между этими файлами и файлами из предыдущего поста - дополнительный узел string в strings.xml и дополнительная кнопка в main.xml. Теперь у нас есть макет со всеми элементами, которые нам нужны, все на своих местах. Теперь необходимо прописать код файла QuotesMain.java. Этот файл будет регистрировать две наши кнопки и подключит из к одному обработчику событий, используя оператор 'switch'. Это код файла QuotesMain.java:

QuotesMain.java: раз QuotesMain.java: два QuotesMain.java: три

view plaincopy to clipboardprint?
package com.gregjacobs.randomquotes; 
   
import android.app.Activity; 
import android.content.Context; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.EditText; 
import android.widget.Toast; 
Здесь мы импортируем все требуемые элементы, чтобы собрать проект воедино. Все вышеуказанные импорты должны быть знакомы вам из инструкции по графическим элементам.

view plaincopy to clipboardprint?
public class QuotesMain extends Activity { 
    DBAdapter db = new DBAdapter(this); 
    EditText Quote; 
    /** Вызывается, когда оператор создается впервые. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        // Захватывает кнопку из макета 
        Button setButton = (Button)findViewById(R.id.go); 
        Button getButton = (Button)findViewById(R.id.genRan); 
        // Регистрирует приемник OnClick 
        setButton.setOnClickListener(mAddListener); 
        getButton.setOnClickListener(mAddListener); 
    } 
Теперь у нас имеется две кнопки, на которые ссылаются по ID, это кнопки типа getButton (захватывает информацию из текстового поля и вставляет ее в базу данных). Обе кнопки обрабатываются одним обработчиком событий, процесс обработки действий с кнопками описан ниже:

view plaincopy to clipboardprint?
// Создает анонимную реализацию OnClickListener 
private OnClickListener mAddListener = new OnClickListener() 

    public void onClick(View v) 
    { 
        switch(v.getId()) 
        { 
        case R.id.go: 
db.open(); 
long id = 0; 
// Действие при нажатии кнопки 
try 

    Quote = (EditText)findViewById(R.id.Quote); 
    db.insertQuote(Quote.getText().toString()); 
 
    id = db.getAllEntries(); 
 
    Context context = getApplicationContext(); 
    CharSequence text = "The quote '" + Quote.getText() + "' was added successfully!\nQuotes Total = " + id; 
    int duration = Toast.LENGTH_LONG; 
 
    Toast toast = Toast.makeText(context, text, duration); 
    toast.show(); 
    Quote.setText(""); 

catch (Exception ex) 

    Context context = getApplicationContext(); 
    CharSequence text = ex.toString() + "ID = " + id; 
    int duration = Toast.LENGTH_LONG; 
 
    Toast toast = Toast.makeText(context, text, duration); 
    toast.show(); 

db.close(); 
break; 
В описании оператора case, приведенном выше, четко видна последовательность действий: захват текста из текстового поля и вставка данных в БД с помощью db.insertQuote из DBAdapter java-класса. После успешной вставки будет показано текстовое оповещение (toast), которое даст знать, что цитата была введена успешно и каково число цитат в БД на текущий момент.

view plaincopy to clipboardprint?
            case R.id.genRan: 
                db.open(); 
                //long id1 = 0; 
                // do something when the button is clicked 
                try 
                { 
                    String quote = ""; 
                    quote = db.getRandomEntry(); 
                    Context context = getApplicationContext(); 
                    CharSequence text = quote; 
                    int duration = Toast.LENGTH_LONG; 
   
                    Toast toast = Toast.makeText(context, text, duration); 
                    toast.show(); 
                } 
                catch (Exception ex) 
                { 
                    Context context = getApplicationContext(); 
                    CharSequence text = ex.toString(); 
                    int duration = Toast.LENGTH_LONG; 
   
                    Toast toast = Toast.makeText(context, text, duration); 
                    toast.show(); 
                } 
                db.close(); 
            } 
        } 
    }; 

Этот оператор case использует переменную типа string для ссылки на случайную запись, которую мы выделяем из БД с помощью db.getRandomEntry. Далее мы отображаем данные в toast-е, чтобы показать, что информация действительно была извлечена.Все фрагменты кода, собранные вместе, в итоге должны дать во такое изображение на экране Android-аппарата:

Ввод текста:

Ввод текста

Отображение случайных записей:

Отображение случайных записей Отображение случайных записей

Освоив базы данных, вы можете создавать приложения для Android, требующие хранилища данных. Базы данных в Android дают массу других возможностей и функций, многие из которых будут описаны в следующем посте. К примеру, такие функции как обновление базы данных, удаление записей и умение разбираться в DDMS (Dalvik Debug Monitor Service) представляют собой важнейшую часть Android-программирования. Если не можете дождаться следующей части, изучите статьи по DDMS и Обновлению и Удалению. Как всегда, если возникнут проблемы, вопросы или споры, оставляйте свои комментарии к посту. До следующего поста!