В этой серии мы узнаем о разработке Android SDK с нуля. Мы уже познакомились со структурой и основными элементами приложения Android, включая ресурсы, манифест и пользовательский интерфейс. Как только вы начнете разрабатывать функциональные приложения для Android, вам нужно будет хранить данные того или иного вида. Платформа Android предлагает ряд вариантов хранения данных в ваших приложениях, которые мы рассмотрим в этом руководстве.
Вступление
Вообще говоря, существует пять основных вариантов хранения данных в приложениях Android: путем сохранения данных в приложении Shared Preferences, во внутреннее хранилище (которое является частным для вашего приложения), во внешнее хранилище (которое является общедоступным для устройства), в базы данных и к веб-ресурсам, доступ к которым осуществляется через интернет-соединение устройства. Мы не будем в полной мере демонстрировать каждый из этих вариантов, но рассмотрим основы каждого из подходов, давая вам представление о том, как выбрать вариант хранения каждый раз, когда вам нужны постоянные данные.
1. Общие настройки
Шаг 1
Общие предпочтения позволяют хранить примитивные типы данных в парах ключ-значение. Файл общих настроек приложения, как правило, является самым простым вариантом хранения данных, но он по сути ограничен с точки зрения того, что вы можете хранить. Вы можете сохранить примитивные числа типов (int, long и float), логические значения и текстовые строки. Вы присваиваете имя каждому сохраняемому значению и извлекаете его каждый раз при запуске приложения. Поскольку вы, скорее всего, будете использовать общие настройки в первых созданных вами приложениях, мы пройдем этот процесс чуть более подробно, чем другие варианты, которые вы можете использовать по мере необходимости.
Вы можете попробовать этот код в своем основном классе Activity и протестировать его, когда мы запустим приложение позже в этой серии. Общие настройки идеально подходят для параметров конфигурации пользователей ваших приложений, например, для выбора настроек внешнего вида. Помните, мы создали простую кнопку, которая, когда пользователь нажимал на нее, отображаемый на ней текст менялся на «Ой». Давайте представим, что как только пользователь нажал кнопку один раз, мы хотим, чтобы кнопка поддерживала текст «Ой», даже при последующих запусках приложения. Это означает, что исходный текст отображается только до тех пор, пока пользователь не нажмет кнопку в первый раз.
Давайте добавим несколько общих настроек в приложение. В верхней части класса, перед методом onCreate , давайте выберем имя для общих настроек:
1
|
public static final String MY_APP_PREFS = "MyAppPrefs";
|
Использование модификаторов «public static» позволяет нам получить доступ к этой переменной в любом классе в приложении, поэтому нам нужно только сохранить здесь имя предпочтения String. Мы используем верхний регистр, потому что переменная является константой, отсюда и модификатор final. Каждый раз, когда вы извлекаете или устанавливаете элемент данных в настройках приложения, вы должны использовать одно и то же имя.
Шаг 2
Давайте напишем общие настройки сейчас. В вашем методе onClick , после строки, в которой вы устанавливаете текст «Ouch» на кнопке, извлекайте Shared Preferences, используя их имя:
1
|
SharedPreferences thePrefs = getSharedPreferences(MY_APP_PREFS, 0);
|
Вам необходимо добавить импорт для класса «android.content.SharedPreferences». Наведите указатель мыши на текст «SharedPreferences» и используйте для этого подсказки Eclipse. Первый параметр — это имя предпочтения, которое мы определили, второй — режим, который мы используем для параметра по умолчанию.
Теперь вам нужно получить редактор для общих настроек, если вы хотите установить значения:
1
|
SharedPreferences.Editor prefsEd = thePrefs.edit();
|
Теперь мы можем записать значение в Shared Preferences:
1
|
prefsEd.putBoolean("btnPressed", true);
|
Мы используем логический тип, так как пользователь нажал кнопку или нет. Редактор предоставляет специальные методы для различных типов, которые можно сохранить в общих настройках, причем каждый метод принимает параметры имени и значения. Наконец, нам нужно зафиксировать правки:
1
|
prefsEd.commit();
|
Шаг 3
Давайте теперь используем сохраненное значение, чтобы определить, что должно отображаться, когда пользователь запускает приложение. В onCreate , после существующего кода, получите Shared Preferences:
1
|
SharedPreferences thePrefs = getSharedPreferences(MY_APP_PREFS, 0);
|
На этот раз нам не нужен редактор, так как мы только получаем значение:
1
|
boolean pressed = thePrefs.getBoolean("btnPressed", false);
|
Мы извлекаем значение, используя то же имя, с которым мы его установили, читая результат в переменную. Если значение еще не установлено, возвращается второй параметр. Это значение по умолчанию, которое является ложным. Теперь давайте используем значение:
1
|
if(pressed) theButton.setText("Ouch");
|
Если пользователь нажал кнопку, когда приложение запускалось раньше, кнопка сразу отобразит «Ой». Вы можете увидеть это в действии, когда мы запустим приложение позже в этой серии. Это тривиальный пример, демонстрирующий процесс использования общих настроек. Вы найдете их наиболее полезными в ваших собственных приложениях для пользовательских предпочтений с точки зрения внешнего вида и поведения приложения.
2. Личные внутренние файлы
Шаг 1
Вы можете сохранять файлы как во внутреннем, так и во внешнем хранилище на устройстве пользователя. Если вы сохраняете файл во внутреннем хранилище, Android рассматривает это как личное для вашего приложения. Такие файлы, по сути, являются частью вашего приложения в том, что касается устройства, прямого доступа к ним за пределами приложения нет, и они удаляются, если само приложение удаляется.
Вы можете создать файл во внутреннем хранилище, используя поток вывода следующим образом:
1
|
FileOutputStream fileOut = openFileOutput("my_file", Context.MODE_PRIVATE);
|
Вам необходимо добавить импорт для класса «java.io.FileOutputStream». Мы передаем имя файла и режим, выбирая приватный режим, чтобы файл мог использоваться только приложением. Если вы попытаетесь добавить этот код в свою активность сейчас, например, в методе onClick , Eclipse укажет на ошибку. Это потому, что когда вы выполняете операции ввода / вывода, ваше приложение может столкнуться с ошибками, которые вам необходимо предпринять, чтобы справиться. Если ваша операция ввода / вывода не может завершить ее, возникает исключение, и ваше приложение перестает работать. Чтобы приложение продолжало работать, в таком случае мы заключаем наш код ввода / вывода в блок try :
1
2
3
4
5
6
|
try{
FileOutputStream fileOut = openFileOutput("my_file", Context.MODE_PRIVATE);
}
catch(IOException ioe){
Log.e("APP_TAG", "IO Exception", ioe);
}
|
Если операция ввода / вывода вызывает исключение, выполняется код внутри блока catch, который просто записывает подробности ошибки в журнал. Вы будете часто использовать класс Log в своих приложениях (импорт «android.util.Log»), так как он позволяет вам видеть, что происходит во время выполнения кода. Вы можете определить переменную класса для тега String, который мы используем здесь в качестве первого параметра. Тогда вы можете увидеть детали исключения в Android LogCat, если что-то пойдет не так.
Шаг 2
Вернувшись в блок try , после создания потока вывода файла вы можете попытаться записать файл:
1
2
|
String fileContent = "my data file content";
fileOut.write(fileContent.getBytes());
|
После того, как вы написали, что вам нужно в файл, вы должны закрыть его:
1
|
fileOut.close();
|
Шаг 3
Когда вам нужно получить содержимое внутренних файлов, вы можете использовать следующий процесс:
1
2
3
4
5
6
7
|
try{
FileInputStream fileIn = openFileInput("my_file");
//read the file
}
catch(IOException ioe){
Log.e("APP_TAG", "IO Exception", ioe);
}
|
Внутри блока try прочитайте содержимое файла с помощью Buffered Reader:
01
02
03
04
05
06
07
08
09
10
|
InputStreamReader streamIn = new InputStreamReader(fileIn);
BufferedReader fileRead = new BufferedReader(streamIn);
StringBuilder fileBuild = new StringBuilder("");
String fileLine=fileRead.readLine();
while(fileLine!=null){
fileBuild.append(fileLine+"\n");
fileLine=fileRead.readLine();
}
String fileText = fileBuild.toString();
streamIn.close();
|
Не пугайтесь количества различных вовлеченных объектов, это стандарт для ввода / вывода Java. Цикл while выполняется для каждой строки в файле. После выполнения переменная fileText сохраняет содержимое файла в виде строки, которую затем можно использовать.
3. Публичные внешние файлы
Шаг 1
Ваши приложения могут сохранять файлы во внешнем хранилище, если на пользовательском устройстве их есть. Внешним хранилищем может быть SD-карта или другой съемный носитель, или это может быть внутреннее хранилище, которое пользователь не может удалить, но которое система считает внешним. Когда вы сохраняете файл во внешнем хранилище, он становится общедоступным, и вы не можете остановить доступ пользователя или другого приложения к нему.
Прежде чем пытаться сохранить данные во внешнем хранилище, вы должны сначала проверить, есть ли некоторые из них, так как это не всегда так:
1
|
String extStorageState = Environment.getExternalStorageState();
|
Система возвращает информацию в виде строки, которую затем можно проанализировать, сравнив ее с полями класса Environment для различных состояний внешнего хранилища:
1
2
3
4
5
6
7
8
9
|
if(Environment.MEDIA_MOUNTED.equals(extStorageState)){
//ok to go ahead and read/ write to external storage
}
else if(Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)){
//can only read
}
else{
//cannot read or write
}
|
Даже если на устройстве есть внешнее хранилище, мы не можем предполагать, что приложение может записывать на него данные.
Шаг 2
После проверки того, что вы можете выполнять запись во внешнее хранилище, вам необходимо получить местоположение каталога, в котором будут сохраняться ваши файлы. Следующее относится к приложениям, ориентированным на уровни API 8 и выше:
1
|
File myFile = new File(getExternalFilesDir(null), "MyFile.txt");
|
Затем вы можете писать и читать из файла. Вам также понадобится следующее разрешение в вашем файле манифеста:
1
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
Когда ваши приложения станут более продвинутыми, вы можете захотеть сохранить их в файлах, которые используются другими приложениями. В таких случаях вы можете использовать различные общедоступные каталоги для общих элементов, таких как изображения и музыкальные файлы.
4. Базы данных
Как только ваши приложения требуют более сложных структурированных данных, чем вы можете разумно хранить в общих настройках или внутренних / внешних файлах, вам следует рассмотреть возможность использования баз данных. Android обеспечивает поддержку для создания и доступа к базам данных SQLite в ваших приложениях. Когда вы создаете базу данных, она является частной для вашего приложения.
Рекомендуемый способ использования баз данных SQLite в ваших приложениях для Android — использовать класс, расширяющий SQLiteOpenHelper . Внутри этого класса определите свойства базы данных, создав переменные класса, в которых вы определите имена таблиц базы данных и их строки создания SQL, как в следующем примере:
1
2
3
|
private static final String NOTE_TABLE_CREATE =
"CREATE TABLE Note (noteID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"noteTxt TEXT);";
|
В этом примере представлена очень простая таблица с двумя столбцами, один для идентификатора, а другой для текста, где каждая запись содержит примечание пользователя. Внутри вашего класса SQLiteOpenHelper вы можете переопределить метод onCreate для создания своей базы данных. В другом месте приложения, как и в классе Activity, вы можете получить доступ к базе данных через SQLiteOpenHelper , используя метод getWritableDatabase для вставки новых записей и метод getReadableDatabase для запроса существующих записей, которые затем можно отобразить в пользовательском интерфейсе приложения.
При переборе результатов запроса ваши приложения будут использовать класс Cursor, который по очереди ссылается на каждую строку в вашем наборе результатов.
5. Интернет-данные
Многие приложения используют интернет-ресурсы данных, причем приложение иногда по существу содержит интерфейс к веб-источнику данных. Вы можете использовать интернет-соединение на пользовательском устройстве для хранения и извлечения данных из Интернета, когда у пользователя есть работающее интернет-соединение. Для этого вам нужно добавить разрешение «android.permission.INTERNET» в свой манифест.
Если вы хотите получать данные из Интернета в своих приложениях, вам необходимо убедиться, что вы выполняете эту обработку из основного потока пользовательского интерфейса приложения. Используя AsyncTask , вы можете извлекать данные из Интернета в фоновом режиме, записывать результаты в пользовательский интерфейс, когда они поступают, и позволить интерфейсу продолжать функционировать, пока они выбираются.
Вы можете добавить внутренний класс AsyncTask внутри класса Activity, а затем создать экземпляр AsyncTask в Activity, когда хотите получить данные. Включив в AsyncTask методы doInBackground и onPostExecute , вы можете получить результаты выборки данных в Activity, что позволит вам записать их в пользовательский интерфейс.
Извлечение веб-данных — промежуточная задача, которую лучше оставить до тех пор, пока вы не освоите основы разработки под Android, используя данные на устройстве. Тем не менее, вы можете найти его полезным для многих приложений, поскольку он позволяет вам использовать возможности подключения пользовательских устройств. Java и Android предоставляют утилиты для обработки возвращенных структурированных данных, таких как каналы JSON.
Вывод
Это руководство дало вам обзор ваших вариантов хранения данных при разработке приложений для Android. Какой бы вариант вы ни выбрали, определяется подробностями приложения, над которым вы работаете, так как методы каждого варианта соответствуют конкретным потребностям. В следующей части этой серии мы рассмотрим подключение физических устройств к вашей установке Eclipse, а также создание виртуальных устройств. После этого мы рассмотрим запущенные приложения на любом типе устройства. В конце серии мы рассмотрим общие классы и жизненный цикл Android Activity, чтобы подготовить вас к работе над своими собственными приложениями.