T.M. SoftStudio

feci quod potui, faciant meliora potentes

Купить полную версию курса "Программирование мобильных приложений для платформы Android" (русскоязычная версия, лекции, тесты, лабораторные работы)

Программирование мобильных приложений для платформы Android

Лекция 19

Управление данными. Часть 1

Мобильные устройства могут создавать и манипулировать большими объемами данных.

Android предоставляет ряд классов, которые позволяют управлять данными между несколькими сессиями приложения.

В сегодняшнем уроке я расскажу о некоторых из этих классов.

Начнем с класса SharedPreferences, который позволяет приложениям хранить и управлять небольшим количеством примитивных данных.

Далее, я буду говорить о записи файлов на внутренние и внешние устройства хранения данных.

И, наконец, я закончу обсуждением создания и использования сложных SQLite баз данных.

Ваши приложения обычно использует SharedPreferences класс, если вы хотите хранить небольшие количества примитивных данных, таких как имя пользователя.

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

Ваши приложения обычно используют внешние хранилища данных устройства, если вы хотите хранить большие объемы открытых данных, таких как аудио или видео файлы.

И ваше приложение будет использовать базы данных, когда вы собираетесь хранить малые и большие количества закрытых структурированных данных.

Предпочтения SharedPreferences являются, по существу, таблицами или картами данных.

И как любая карта, они содержат пары ключ-значение простых типов данных, таких как строки и числа.

И предпочтения SharedPreferences автоматически сохраняются между сеансами приложения.

И это позволяет пользователю создать информацию в течение одного использования приложения, закрыть приложение, перезапустить его позже, и по прежнему иметь доступ к информации, которую пользователь создал ранее.

SharedPreferences часто используются для долгосрочного хранения настраиваемых данных приложения, таких как имя пользователя, WiFi сети, опции, специфичные для пользователя, предпочтения.

Чтобы получить объект SharedPreferences, связанный с данной активностью, вы можете использовать метод Activity.getPreferences, передавая ему режим доступа в качестве параметра.

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

Если вы хотите создать объект SharedPreferences, который не связан с конкретной активностью, тогда вы можете использовать метод Context.getSharedPreferences, передавая имя объекта SharedPreferences и режим доступа, например, MODE_PRIVATE.

После того, как вы получили объект SharedPreferences, вы можете редактировать этот объект, вызывая метод SharedPreferences.edit, и этот метод возвращает экземпляр класса SharedPreferences.Еditor.

Вы можете добавлять или изменять значения SharedPreferences объекта с помощью таких методов, как putInt, putString и remove.

И после того как вы сделали все изменения, вы можете сохранить эти изменения с помощью вызова метода SharedPreferences.Editor.commit.

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

Если приложение позже захочет считать эти значения, оно может получить SharedPreferences объект снова и затем использовать различные методы для чтения значений, которые хранятся в объекте.

Например, оно может вызвать метод getAll для получения значений SharedPrefences.

Приложение может вызвать метод getBoolean, чтобы получить определенное логическое значение или может вызвать метод getString, чтобы получить определенное строковое значение.

Наш первый пример приложения в этом уроке называется DataManagementSharedPreferences.

Это приложение имеет кнопку с надписью Play, и когда пользователь нажимает кнопку воспроизведения Play, приложение отображает случайное число.

Приложение отслеживает самое большое число и сохраняет это число в различных пользовательских сеансах.

Давайте запустим это приложение.

Давайте посмотрим на исходный код для этого приложения.

Откроем основную активность.

В методе оnCreate, код получает SharedPreferences объект для этой активности.

Далее, когда пользователь нажимает кнопку Play, код генерирует новое число, которое он хранит в переменной с именем val.

После этого код вызывает метод getInt для SharedPreferences объекта для получения текущего наибольшего числа и если val больше, чем текущее наибольшее число, тогда мы должны обновить его.

Так код вызывает метод редактирования для SharedPreferences объекта, который возвращает объект SharedPreferences.Editor.

Далее, код вызывает метод putInt для объекта редактора, чтобы обновить наибольшее число.

И, наконец, код вызывает метод commit для редактора, чтобы сохранить текущее наибольшее число.

Как я уже сказал ранее, класс SharedPreferences используется для хранения пользовательских настроек приложения.

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

Наш следующий пример приложения называется DataManagementPreferenceFragment.

Это приложение использует объект PreferenceFragment для отображения и изменения пользовательских настроек приложения.

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

Давайте взглянем на это приложение.

Когда это приложение запускается, оно отображает одну кнопку Посмотреть имя пользователя.

Когда я нажимаю на эту кнопку, PreferenceFragment появляется, позволяя мне посмотреть мое текущее имя пользователя и изменить его, если я захочу.

Как вы видите, мое имя пользователя пока не установлено, поэтому я нажму на эту область.

А теперь появится всплывающее диалоговое окно, которое просит меня ввести мое имя пользователя.

Позвольте мне сделать это сейчас.

И теперь, я нажму на кнопку Отправить, чтобы установить это новое имя пользователя.

Диалоговое окно закроется.

И здесь вы можете видеть, что мое имя пользователя это сейчас имя, которое я только что ввел.

Теперь я закрою приложение и перезапущу его.

Я снова нажму на кнопку Просмотр имени пользователя.

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

Давайте посмотрим на исходный код для этого приложения.

Откроем активность ViewAndUpdatePreferences.

Эта активность является той, которая запускается, когда пользователь нажимает кнопку Просмотр имени пользователя.

Метод оnCreate во-первых вызывает метод setContentView, передавая XML файл user_prefs_fragment.xml.

И этот файл макета создает и отображает экземпляр класса UserPreferenceFragment, который определяется в этом файле.

Давайте посмотрим на этот класс сейчас.

Метод оnCreate этого класса сначала вызывает метод addPreferencesFromResource, передавая файл user_prefs.xml каталога res/xml.

Давайте откроем этот файл.

Как вы можете видеть, в этом файле определяется ресурс PreferenceScreen.

Этот PreferenceScreen содержит одно предпочтение, которое отображается в текстовом поле ввода, и это предпочтение имеет ключ, строку uname, и отображается с заголовком User Name.

Когда пользователь нажимает на текстовое поле, чтобы изменить имя пользователя, появиляется всплывающее диалоговое окно с названием Change User Name и сообщением Введите имя пользователя и двумя кнопками Cancel и Submit.

В дополнение к предпочтениям, Android поддерживает использование файлов.

File это класс, который представляет единицу файловой системы, которая идентифицируется путем.

В Android, области хранения классифицируются как внутреннее или внешнее хранилище.

Исторически это различие возникло как разница между внутренней флэш-памятью на устройстве и съемной внешней картой памяти, вставленной в устройство.

В настоящее время, однако, не все внешние карты памяти являются съемными.

Внутренняя память, как правило, используется для небольших наборов данных, которые являются приватными для приложения.

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

Примеры приложений, которые мы рассмотрим, и которые используют файлы для хранения информации, применяют два метода, это метод openFileOutput, открывающий приватный файл для записи и создающий физический файл, если он еще не существует. И есть также метод openFileInput, открывающий приватный файл для чтения.

И есть, конечно, многие другие методы, связанные с файлами, так что пожалуйста, посмотрите документацию для дополнительной информации.

Следующий пример приложения называется DataManagementFileInternalMemory.

Когда это приложение запускается, оно проверяет, существует ли определенный текстовый файл, и если это не так, приложение создает этот файл и записывает текст в него.

После этого приложение открывает этот файл, считывает текст из него, и отображает этот текст на дисплее.

Давайте взглянем на это приложение.

Как вы можете видеть, приложение отображает текст на экране.

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

Откроем основную активность этого приложения.

В методе оnCreate, сначала код получает FileStreamPath, связанный с именем файла, testfile.txt.

А если этот файл не существует, тогда код вызывает метод writeFile.

Этот метод вызывает метод openFileOutput, который возвращает объект FileOutputSteam.

Затем код пишет три строки текста для этого текстового файла и, наконец, он закрывает файл.

В оnCreate методе код продолжается вызовом метода readFile, передавая ему компоновку, с помощью которой будет отображаться текст.

Метод readFile открывает текстовый файл, а потом начинает читать каждую строку из файла.

И каждая из этих строк помещается в TextView и добавляется к линейной компоновке.

Управление данными. Часть 2

Как я уже говорил, Android также позволяет приложениям записывать данные во внешнюю память.

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

Например, когда внешняя память обеспечивается SD картой, и поэтому медиа может появиться или исчезнуть без предупреждения.

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

Это можно сделать с помощью метода getExternalStorageState класса Environment.

Этот метод будет возвращать значение, показывающее текущее состояние внешней памяти устройства, и некоторые из этих значений приведены здесь.

Например, MEDIA_MOUNTED означает, что медиа представлено и установлено для чтения и записи.

MEDIA_MOUNTED_READ_ONLY означает, что медиа присутствует и установлено, но доступ есть только для чтения.

И MEDIA_REMOVED указывает на то, что внешней памяти нет.

И, конечно, есть также несколько других значений.

Если ваше приложение хочет записать во внешнюю память, необходимо запросить соответствующее разрешение.

В частности, вы должны будете добавить разрешение WRITE_EXTERNAL_STORAGE в файл приложения AndroidManifest.xml.

Наш следующий пример приложения называется DataManagementFileExternalMemory.

Это приложение считывает файлы изображений из res/raw каталога и копирует эти файлы на внешнее устройство хранения.

Оно считает данные изображения из этого внешнего файла и затем отображает изображение на экране.

Давайте взглянем на это приложение прямо сейчас.

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

Откроем основную активность.

В методе оnCreate, код проверяет состояние внешнего хранения, чтобы убедиться, что медиа установлено для чтения и записи.

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

И код создает новый объект File в этом каталоге.

Далее, код проверяет, существует ли на самом деле файл во внешней памяти.

Затем код вызывает метод copyImageDataToMemory.

Давайте посмотрим на этот метод.

Этот метод начинается с создания нового выходной поток, который будет использоваться для записи в файл во внешней памяти.

Далее, код создает входной поток, так чтобы читать данные изображения из res/raw каталога.

И, наконец, код копирует данные из входного потока в выходной поток.

Когда этот метод завершается, там будет новый файл во внешней памяти.

Теперь, возвращаясь к методу оnCreate, код получает ссылку на ImageView, а затем устанавливает URI файла, который был только что создан во внешней памяти.

Если ваше приложение создает временные файлы, тогда вы можете рассмотреть их запись в кэш-каталоги.

Файлы кэша являются временными файлами, которые могут быть удалены системой при заполнении кэша.

И эти файлы удаляются, когда приложение удаляется.

Вы можете получить доступ к каталогу кэша с помощью метода getCacheDir класса Context.

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

Кроме того, вы также можете использовать метод getExternalCacheDir класса Context, который возвращает объект File, представляющий каталог для кэш-файлов внешнего хранилища.

Когда приложение считывает и использует большое количество структурированных и сложных данных, вы можете хранить эти данные в базе данных.

Android-обеспечивает реализацию SQLite, которая позволяет приложениям создавать и использовать реляционные базы данных.

SQLite разработана для работы в памяти очень небольшого размера, говорят, менее чем в 300 килобайт памяти.

SQLite, однако, является полноценной реляционной базой данных.

Она реализует большинство стандартов SQL92 и поддерживает высокую надежность ACID транзакций, это означает, что транзакции являются атомными, согласованными, изолированными и надежными.

Чтобы использовать базу данных SQL в приложениях, Android рекомендует использовать вспомогательный класс SQLiteOpenHelper.

Для этого вы создадите подкласс этого класса SQLiteOpenHelper.

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

Далее, вы должны будете переопределить метод оnCreate и метод onUpgrade также.

Как правило, в методе оnCreate, вы выполняете одну или более команд CREATE TABLE, которые определяют структуру базы данных и компоновку.

И после этого, вы будете использовать методы SQLiteOpenHelper, для того, чтобы открыть БД SQLite.

Далее, вы будете выполнять операции с базой данных.

Наш последний пример приложения называется DataManagementSQL.

Это приложение создает базу данных SQLite и затем вставляет несколько записей, некоторые с ошибками, в базу данных.

Приложение будет также отображать кнопку с надписью Fix.

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

И затем будет повторное обновление записей базы данных на экране.

Давайте взглянем на это приложение.

Как вы можете видеть, приложение отображает четыре записи базы данных, каждая из которых содержит идентификатор записи и имя художника.

В нижней части экрана, есть также кнопка Fix, о которой я упоминал ранее.

Когда я нажимаю эту кнопку, запись номер два, Lady Gaga, будет удалена.

И запись номер три, Jawny Cash, обновится с правильным именем художника.

Позвольте мне нажать кнопку Fix сейчас.

Давайте посмотрим на исходный код для этого приложения.

Откроем основную активность.

Здесь в методе оnCreate, мы начнем с создания нового экземпляра DatabaseOpenHelper.

Этот класс является подклассом класса SQLiteOpenHelper.

Давайте взглянем на этот класс.

Конструктор этого класса вызывает конструктор суперкласса, передавая информацию, такую ​​как имя базы данных и номер версии.

Теперь, метод оnCreate этого класса получает объект SQLite базы данных и затем вызывает его метод ExecSQL, передавая строку, которая соответствует фактической команде SQL, создающей таблицу с именем artists.

И эта таблица будет содержать два столбца, целые числа ID и строки для имен художников.

Этот класс также имеет метод deleteDatabase, который просто удаляет базу данных.

Теперь, возвращаясь к основной активности, оnCreate метод продолжает, получая ссылку на базу данных, которая может быть использована для чтения и записи.

Далее, код вызывает метод clearAll, который просто удаляет все записи в базе данных.

После этого, код вызывает метод insertArtist, который вставляет записи в базу данных.

Этот метод сначала создает объект ContentValues, и затем помещает информацию в объект, соответствующий имени художника.

Далее, код вставляет запись в таблицу artist с помощью метода insert.

Теперь, так как поле ID генерируется автоматически в базе данных, приложению не нужно включать его здесь.

Далее, код очищает объект values, и затем добавляет другую запись.

Теперь, возвращаясь к методу оnCreate, код вызывает метод readArtists, который считывает все записи в базе данных и возвращает объект Cursor.

Курсор, по существу, является итератором над множеством записей, возвращаемых операцией запроса.

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

И, наконец, код устанавливает слушателя для кнопки Fix.

Когда пользователь нажимает эту кнопку, код вызывает метод fix.

Давайте посмотрим этот метод.

Этот метод сначала вызывает метод delete, который находит запись с именем Lady Gaga, а затем удаляет ее.

И после этого, метод создает ContentValues ​​объект с правильным написанием имени Джонни.

Затем код выполняет операцию обновления, которая сначала находит запись с неправильным именем, а затем заменяет ее на правильное написание.

Если вам нужно отладить приложение с базой данных, есть несколько вещей, которые вы, возможно, должны знать.

Во-первых, файлы базы данных хранятся в каталоге data/data/<package name>/databases.

И вы можете проверить эти файлы, сначала открыв оболочку эмулятора или устройство с помощью ADB.

Например, если имя вашего эмулятора является emulator-5554, тогда вы можете использовать команду, чтобы открыть оболочку.

И как только оболочка открыта, вы можете использовать sqlite3 команду, чтобы открыть подключение к самой базе данных.

И тогда, вы можете ввести, например, команду .help, чтобы получить более подробную информацию о конкретных командах этой программы.