Сохранение настроек приложения android studio
В этом уроке мы научимся выполнять сохранение данных нашего приложения для того, чтобы при следующих его запусках воссоздать сохраненное, а не вводить заново. Самый, наверное, распространенный и полезный способ от такого сохранения - это сохранение логина и пароля к чему либо: аккаунту, архиву и многое другое. Хотя на паролях все возможности далеко не исчерпываются, но мы рассмотрим пример использования стандартного интерфейса Shared Preferences на примере сохранения текстовых данных. Мы сделаем Android приложение, в котором будет два поля для ввода логина и пароля (любых двух слов) и две кнопки, сохранить и загрузить. Если заполнить поля ввода, но не нажать кнопку сохранить - введенные данные будут теряться, если же перед выходом пользователь нажмет на кнопку "Сохранить" то при следующем запуске он нажмет кнопку "Загрузить" и поля автоматически заполнятся введенными и сохраненными ранее символами.
Сохранение данных с помощью Shared Preferences происходит благодаря тому, что при сохранении данных таким образом, приложение создает специальный xml файл, где будет храниться нужная информация, и при нужде получить сохраненные данные, программа будет обращаться к этому xml файлу. Файл храниться в системе устройства по адресу /data/data/НАЗВАНИЕ_ПРОЕКТА/shared_prefs.
Интерфейс для сохранения настроек Shared Preferences довольно прост в использовании. Для этого вызывается специальный метод под названием getPreferences(). По отношению к введенным данным можно выбрать 3 уровня доступности:
- MODE_PRIVATE - только это приложение может читать настройки с xml файла;
- MODE_WORLD_READABLE - все приложения могут читать с xml файла;
- MODE_WORLD_WRITEABLE - все приложения могут выполнять запись в xml файл.
После объявления метода работы с Shared Preferences нужно создать объект Editor, который нужен для создания пар имя-значение, которые будут записаны в xml файл для сохранения с помощью метода put() . Для успешного внесения данных в файл сохранения в конце нужно выполнить команду commit.
Для того, чтобы извлечь сохраненные данные, нужно обратиться к ним с помощью команды get (), ссылаясь на необходимые пары-значения.
Итак, начнем делать наше приложение, описанное вначале. Создаем новый проект, выбираем Blank Activity. Интерфейс программы будет иметь два поля для ввода текста и две кнопки. Открываем файл activity_main.xml и добавляем в него следующее:
Теперь открываем MainActivity.java и добавляем этот код (объяснения там же):
Теперь осталось только протестировать наше приложение:
Похоже на чудо, но оно работает!
Таким образом, в этом уроке мы научились как в Android программировании выполнять сохранение данных приложения с помощью стандартного интерфейса под названием Shared Preferences. Надеюсь у вас все получилось, удачи в начинаниях.
Хватит об Intent и Activity. Поговорим о хранении данных. В Android есть несколько способов хранения данных:
Preferences - в качестве аналогии можно привести виндовые INI-файлы
SQLite - база данных, таблицы
обычные файлы - внутренние и внешние (на SD карте)
Мы начнем с самого простого – Preferences. Значения сохраняются в виде пары: имя, значение. Так же, как и например extras в Intent.
Разработаем приложение. В нем будет поле для ввода текста и две кнопки – Save и Load. По нажатию на Save мы будем сохранять значение из поля, по нажатию на Load – загружать.
Project name: P0331_SharedPreferences
Build Target: Android 2.3.3
Application name: SharedPreferences
Package name: ru.startandroid.develop.p0331sharedpreferences
Create Activity: MainActivity
Откроем main.xml и создадим такой экран:
Поле ввода и две кнопки.
Теперь пишем код в MainActivity.java:
Определение элементов экрана, присвоение обработчиков и реализация onClick – тут все понятно и как обычно. Нам интересны методы, которые мы вызываем в onClick
Все сохраняем, запускаем приложение.
Для начала, давайте убедимся, что сохранение в принципе нужно. Введите какой-нить текст в поле ввода
Теперь найдите приложение в общем списке приложений эмулятора
и запустите снова.
Поле ввода пустое. То, что мы вводили – пропало при закрытии программы. Нажатие на Load тоже ничего не даст – мы ничего не сохраняли.
Давайте попробуем сохранять. Снова введите значение и нажмите Save.
Значение сохранилось в системе.
Давайте сделаем так, чтобы сохранение и загрузка происходили автоматически при закрытии и открытии приложения и не надо было жать кнопки. Для этого метод loadText будем вызывать в onCreate.
(Добавляете только строку 8)
а метод saveText - в onDestroy
Все сохраним, запустим. Теперь можно вводить данные, закрывать приложение, снова открывать и данные не потеряются. Кнопки Save и Load также работают. В какой момент сохранять данные в ваших приложениях – решать только вам. По нажатию кнопки, при закрытии программы или еще по какому-либо событию. Главное – теперь вы это умеете.
Еще немного слов по этой теме.
Preferences-данные сохраняются в файлы и вы можете посмотреть их. Для этого в Eclipse откройте меню Window > Show View > Other и выберите Android > File Explorer. Отобразилась файловая система эмулятора.
Открываем папку data/data/ru.startandroid.develop.p0331sharedpreferences/shared_prefs и видим там файл MainActivity.xml. Если его выгрузить на комп и открыть - увидим следующее:
Все верно, имя - saved_text и значение - abcdefg.
Обратите внимание, что в пути к файлу используется наш package.
Теперь разберемся, откуда взялось наименование файла MainActivity.xml. Кроме метода getPreferences, который мы использовали, есть метод getSharedPreferences. Он выполняет абсолютно те же функции, но позволяет указать имя файла для хранения данных. Т.е., например, если бы мы в saveText использовали для получение SharedPreferences такой код:
То данные сохранились бы в файле MyPref.xml, а не в MainActivity.xml.
Теперь если мы посмотрим исходники метода getPreferences, то видим следующее:
Используется метод getSharedPreferences, а в качестве имени файла берется имя класса текущего Activity. Отсюда и появилось имя файла MainActivity.xml.
- используете getPreferences, если работаете с данными для текущего Activity и не хотите выдумывать имя файла.
- используете getSharedPreferences, если сохраняете, например, данные - общие для нескольких Activity и сами выбираете имя файла для сохранения.
Кстати, в File Explorer вы можете видеть юниксовые rwx-права доступа к файлу. Попробуйте при сохранении данных использовать не MODE_PRIVATE, а MODE_WORLD_READABLE или MODE_WORLD_WRITEABLE и посмотрите, как будут меняться права.
Полный код MainActivity.java:
На следующем уроке:
- хранение данных с помощью SQLite
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
- новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Когда доходит до сохранения данных приложений локально, разработчики Android определённо избалованы выбором. Помимо прямого доступа к внутренним и внешним областям хранения Android-устройства, платформа Android предлагает базы данных SQLite для хранения реляционных данных и специальные файлы для хранения пар ключ-значение. Более того, приложения для Android также могут использовать сторонние базы данных, которые предлагают поддержку NoSQL.
В этом уроке я покажу вам, как использовать все эти параметры хранения в приложениях для Android. Я также помогу вам понять, как выбрать наиболее подходящий вариант хранения данных.
Вам проще учиться по видео? Почему бы не посмотреть наш курс:
1. Хранение пар ключ-значение
Если вы ищете быстрый способ хранения нескольких строк или номеров, вам следует рассмотреть возможность использования файла настроек (preferences). Активити и службы Android могут использовать метод getDefaultSharedPreferences() класса PreferenceManager , чтобы ссылаться на объект SharedPreferences , который может быть использован и для чтения, и для записи в файл настроек по умолчанию.
Чтобы начать запись в файл настроек, вы должны вызвать метод edit() объекта SharedPreferences , который возвращает объект SharedPreferences.Editor .
Объект SharedPreferences.Editor имеет несколько интуитивных методов, которые можно использовать для хранения новых пар ключ-значение в файле настроек. Например, вы можете использовать метод putString() , чтобы поместить пару ключ-значение со значением типа String . Аналогично, вы можете использовать метод putFloat() , чтобы поместить пару ключ-значение, чьё значение типа float . Следующий пример кода создаёт три пары ключ-значение:
После того, как вы добавили все пары, вы должны вызвать метод commit() объекта SharedPreferences.Editor , чтобы сохранить их.
Чтение из объекта SharedPreferences гораздо проще. Всё, что вам нужно сделать, так это вызвать соответствующий метод get*() . Например, чтобы получить пару ключ-значение, чьё значение является String , вы должны вызывать метод getString() . Вот фрагмент кода, который извлекает все значения, которые мы добавили ранее:
Как вы видите из кода выше, второй параметр всех методов get*() ожидает значение по-умолчанию, которое является значением, которое должно быть возвращено, если ключ не существует и файле настроек.
Обратите внимание, что файлы настроек ограничены только строками и примитивными типами данных. Если вы хотите сохранить более сложные типы данных или двоичные данные, вы должны выбрать другой вариант хранения.
2. Использование базы данных SQLite
Каждое приложение для Android может создавать и использовать базы данных SQLite для хранения больших объемов структурированных данных. Как вы уже знаете, SQLite не только лёгкая, но и очень быстрая. Если у вас есть опыт работы с системами управления реляционными базами данных и вы знакомы как с SQL, что является сокращением для Structured Query Language, и JDBC, что является сокращением для Java Database Connectivity, это может быть предпочтительным вариантом хранения.
Чтобы создать новую базу данных SQLite или открыть уже существующую, вы можете использовать метод openOrCreateDatabase() внутри своей активити или службы. В качестве аргументов вы должны передать имя своей базы данных и режим, в котором вы хотите её открыть. Наиболее часто используемый режим MODE_PRIVATE , который гарантирует, что база данных доступна только для вашего приложения. Например, вот как вы можете открыть или создать базу данных с именем my.db:
После создания базы данных вы можете использовать метод execSQL() для запуска SQL-инструкций. В следующем коде показано, как использовать оператор SQL CREATE TABLE для создания таблицы названной user, которая имеет три столбца:
Хотя можно вставить новые строки в таблицу с помощью метода execSQL() , лучше использовать метод insert() . Метод insert() ожидает объект ContentValues , содержащий значения для каждого столбца таблицы. Объект ContentValues очень похож на объект Map и содержит пары ключ-значение.
Вот два объекта ContentValues , которые вы можете использовать с таблицей user :
Как вы могли догадаться, ключи, которые вы передаете методу put() , должны соответствовать именам столбцов в таблице.
Когда объекты ContentValues готовы, вы можете передать их методу insert() вместе с именем таблицы.
Объект Cursor может содержать ноль или несколько строк. Самый простой способ перебрать все его строки, так это вызвать метод moveToNext() внутри цикла while .
Чтобы получить значение отдельного столбца, вы должны использовать такие методы, как getString() и getInt() , которые ожидают индекс столбца. Например, вот как вы получите все значения, вставленные в таблице user :
После того, как вы получите все результаты вашего запроса, убедитесь, что вы вызвали метод close() для объекта Cursor , чтобы освободить все ресурсы, которые он хранит.
Аналогичным образом, когда вы закончили все операции с базой данных, не забудьте вызвать метод close() для объекта SQLiteDatabase .
3. Использование внутреннего хранилища
Каждое приложение Android имеет свой собственный внутренний каталог хранения, взаимодействующий с ним, в котором приложение может хранить текстовые и двоичные файлы. Файлы внутри этого каталога недоступны для пользователя или других приложений, установленных на устройстве пользователя. Они также автоматически удаляются, когда пользователь удаляет приложение.
Прежде чем вы сможете использовать внутренний каталог хранилища, вы должны определить его местоположение. Для этого вы можете вызвать метод getFilesDir() , который доступен как для активити, так и для служб.
Чтобы получить ссылку на файл внутри каталога, вы можете передать имя файла вместе с указанным вами расположением. Например, вот как вы получите ссылку на файл alice.csv:
С этого момента вы можете использовать свои знания о классах и методах I/O (ввода/вывода) Java для чтения или записи в файл. В следующем фрагменте кода показано, как использовать объект FileOutputStream и метод write() для записи в файл:
4 . Использование внешнего хранилища
Поскольку внутреннее хранилище устройств Android обычно фиксировано и часто довольно ограничено, некоторые устройства Android поддерживают внешние носители данных, такие как съемные микро SD-карты. Я рекомендую использовать этот вариант хранения для больших файлов, таких как фотографии и видео.
В отличие от внутреннего хранилища, внешнее хранилище может не всегда быть доступно. Поэтому, перед его использованием, вы должны всегда проверять, смонтировано ли оно. Для этого используйте метод getExternalStorageState() класса Environment .
Как только вы убедитесь, что внешнее хранилище доступно, вы можете получить путь к файлу внешнего хранилища для своего приложения, вызвав метод getExternalFilesDir() и передав null в качестве аргумента. Затем вы можете использовать путь для ссылки на файлы внутри каталога. Например, вот как вы можете ссылаться на файл с именем bob.jpg в каталоге внешнего хранилища приложения:
Попросив пользователя предоставить вам разрешение WRITE_EXTERNAL_STORAGE , вы можете получить доступ для чтения/записи ко всей файловой системе во внешнем хранилище. Затем вы можете использовать известные общедоступные каталоги для хранения ваших фотографий, фильмов и других мультимедийных файлов. Класс Environment предлагает метод getExternalStoragePublicDirectory() для определения путей этих общих каталогов.
Например, передав этому методу значение Environment.DIRECTORY_PICTURES , вы можете определить путь к общедоступному каталогу, в котором вы можете хранить фотографии. Аналогично, если вы передадите этому методу значение Environment.DIRECTORY_MOVIES , вы получите путь к общедоступному каталогу, в котором могут быть сохранены фильмы.
Вот как вы можете ссылаться на файл bob.jpg в каталоге общих изображений:
Когда у вас есть объект File , вы можете снова использовать классы FileInputStream и FileOutputStream для чтения или записи на него.
Заключение
Теперь вы знаете, как максимально использовать возможности локального хранилища, предоставляемые Android SDK. Независимо от выбранного вами варианта хранения операции чтения/записи могут потребовать много времени, если задействованы большие объемы данных. Поэтому, чтобы убедиться, что основной поток пользовательского интерфейса всегда остается отзывчивым, вы должны рассмотреть возможность запуска операций в другом потоке.
Чтобы узнать больше о сохранении данных приложения локально, обратитесь к официальному руководству API хранения данных.
При разработке мобильных приложений зачастую возникает необходимость хранить настройки приложения (выбранную пользователем цветовую тему, пароль доступа к приложению, параметры синхронизации с сервером и т.д.). Поскольку такие задачи возникают довольно часто, в Google позаботились о разработчиках и создали механизм для быстрой и легкой реализации данного функционала. Этот механизм позволяет легко создавать, сохранять, отображать и производить разные манипуляции с настройками приложения. Мало того он позволяет автоматически создавать пользовательский интерфейс. При описании типа параметра настроек — автоматически генерируется часть пользовательского интерфейса в зависимости от типа параметра (boolean — чекбокс, String — поле ввода, . ) без написания кода. Звучит неплохо для быстрой разработки, не так ли?
Первым шагом в реализации механизма настроек приложения является наследования активности экрана вашего приложения от класса PreferenceActivity. Этот класс наследован от класса ListActivity
и позволяет создавать пользовательский интерфейс по описанию в XML файле ресурсов. Мало того, этот класс позволяет автоматически сохранять настройки в SharedPreferences незаметно для вас. SharedPreferences — интерфейс позволяющий обращаться и манипулировать данными настройки приложения вручную с помощью вызова метода getSharedPreferences из вашего Activity (подробнее Android Essentials: Application Preferences). Для того что бы увязать вместе наш класс унаследованный от PreferenceActivity и XML файл описывающий параметры конфигурации используется метод addPreferencesFromResource:
- .
- @Override
- public void onCreate ( Bundle savedInstanceState )
- super . onCreate ( savedInstanceState ) ;
- addPreferencesFromResource ( R. xml . preferences ) ;
- >
- .
-
: чекбокс, представляет параметры типа boolean. : позволяет выбирать рингтон из доступных на устройстве. URI выбранного файла рингтона будет представлен в виде строки. : строка ввода текста, представляет параметры типа String. : показывает список возможных значений параметра в виде диалога, представляет параметры типа String.
Теперь опишем, что же мы хотим видеть в нашем XML описании. У нас будет две категории. В первой разместим CheckBoxPreference который будет активировать/дезактивировать синхронизацию данных нашего гипотетического приложения и ListPreference в которым будем задавать частоту синхронизации данных. Как вы уже наверняка заметили, между этими двумя параметрами есть зависимость, если первый не выбран, то второй нужно дезактивировать. Это достигается с помощью использования атрибута android:dependency. Во второй категории мы разместим EditTextPreference с помощью которого будем задавать текст приветствия. Представим все это в виде XML:
- <?xml version = "1.0" encoding = "utf-8" ?>
- <PreferenceScreen xmlns:android = "schemas.android.com/apk/res/android" >
- <PreferenceCategory
- android:title = "First Category"
- android:key = "first_category" >
- <CheckBoxPreference
- android:key = "perform_updates"
- android:summary = "Enable or disable data updates"
- android:title = "Enable updates"
- android:defaultValue = "true"
- />
- <ListPreference
- android:key = "updates_interval"
- android:title = "Updates interval"
- android:summary = "Define how often updates will be performed"
- android:defaultValue = "1000"
- android:entries = "@array/updateInterval"
- android:entryValues = "@array/updateIntervalValues"
- android:dependency = "perform_updates"
- />
- </PreferenceCategory >
- <PreferenceCategory
- android:title = "Second Category"
- android:key = "second_category" >
- <EditTextPreference
- android:key = "welcome_message"
- android:title = "Welcome Message"
- android:summary = "Define the Welcome message to be shown"
- android:dialogTitle = "Welcome Message"
- android:dialogMessage = "Provide a message"
- android:defaultValue = "Default welcome message" />
- </PreferenceCategory >
- </PreferenceScreen >
Заметьте, что для ListPreference мы указали атрибут android:entries который указывает на место хранения возможных значений списка. Эти значения хранятся в XML файле res/values/arrays.xml. Значения “updateInterval” and “updateIntervalValue” хранятся в этом файле. На самом деле это просто пары ключ-значение, ключи хранятся в первом массиве, а значения — во втором:
- <?xml version = "1.0" encoding = "utf-8" ?>
- <resources >
- <string-array name = "updateInterval" >
- <item name = "1000" > Every 1 second </item >
- <item name = "5000" > Every 5 seconds </item >
- <item name = "30000" > Every 30 seconds </item >
- <item name = "60000" > Every 1 minute </item >
- <item name = "300000" > Every 5 minutes </item >
- </string-array >
- <string-array name = "updateIntervalValues" >
- <item name = "1000" > 1000 </item >
- <item name = "5000" > 5000 </item >
- <item name = "30000" > 30000 </item >
- <item name = "60000" > 60000 </item >
- <item name = "300000" > 300000 </item >
- </string-array >
- </resources >
Добавлю еще один полезный момент, который может пригодиться. Очень часто необходимо ограничить доступ к приложению с помощью пароля или PIN кода который задается в конфигурации приложения. Очевидно что для этих целей используется EditTextPreference. Но было бы неплохо в случае пароля или PIN-кода скрывать введенные символы, а для кода еще и ограничить ввод только цифрами. Для этого можно использовать атрибуты android:password и android:inputType соответственно:
- <EditTextPreference
- android:key = "pin"
- android:title = "PIN code"
- android:summary = "If login screen is enabled"
- android:dialogTitle = "PIN code"
- android:dialogMessage = "Provide a PIN"
- android:defaultValue = ""
- android:inputType = "number"
- android:password = "true"
- android:dependency = "runLoginScreen"
- />
Как говорилось выше, основную часть работы берет на себя сам andriod-фреймворк. Для того чтобы показать как читать уже заданные параметры мы создадим еще одну активность экрана, которая будет запускаться из нашей главной активности. Но вначале посмотрим на код главной:
- package com.javacodegeeks.android.preferences ;
- import android.content.Intent ;
- import android.os.Bundle ;
- import android.preference.PreferenceActivity ;
- import android.view.Menu ;
- import android.view.MenuItem ;
- public class QuickPrefsActivity extends PreferenceActivity
- @Override
- public void onCreate ( Bundle savedInstanceState )
- super . onCreate ( savedInstanceState ) ;
- addPreferencesFromResource ( R. xml . preferences ) ;
- >
- @Override
- public boolean onCreateOptionsMenu ( Menu menu )
- menu. add ( Menu . NONE , 0 , 0 , "Show current settings" ) ;
- return super . onCreateOptionsMenu ( menu ) ;
- >
- @Override
- public boolean onOptionsItemSelected ( MenuItem item )
- switch ( item. getItemId ( ) )
- case 0 :
- startActivity ( new Intent ( this , ShowSettingsActivity. class ) ) ;
- return true ;
- >
- return false ;
- >
- >
Мы создали опционное меню с одним элементом MenuItem с помощью метода onCreateOptionsMenu. Когда пользователь кликает на элемент меню, мы обрабатываем это событие в методе onOptionsItemSelected и запускаем новую активность используя метод startActivity. (подробнее Using options menus and customized dialogs for user interaction, Launching new activities with intents).
Теперь создадим вторую активность ShowSettingsActivity для отображения параметров конфигурации приложения. Но вначале мы должны описать новую активность в manifest файле AndroidManifest.xml:
- <?xml version = "1.0" encoding = "utf-8" ?>
- <manifest xmlns:android = "schemas.android.com/apk/res/android"
- package = "com.javacodegeeks.android.preferences"
- android:versionCode = "1"
- android:versionName = "1.0" >
- <application android:icon = "@drawable/icon" android:label = "@string/app_name" >
- <activity android:name = ".QuickPrefsActivity" android:label = "@string/app_name" >
- <intent-filter >
- <action android:name = "android.intent.action.MAIN" />
- <category android:name = "android.intent.category.LAUNCHER" />
- </intent-filter >
- </activity >
- <activity android:name = ".ShowSettingsActivity" />
- </application >
- <uses-sdk android:minSdkVersion = "3" />
- </manifest >
- package com.javacodegeeks.android.preferences ;
- import android.app.Activity ;
- import android.content.SharedPreferences ;
- import android.os.Bundle ;
- import android.preference.PreferenceManager ;
- import android.widget.TextView ;
- public class ShowSettingsActivity extends Activity
- @Override
- protected void onCreate ( Bundle savedInstanceState )
- super . onCreate ( savedInstanceState ) ;
- setContentView ( R. layout . show_settings_layout ) ;
- SharedPreferences sharedPrefs = PreferenceManager. getDefaultSharedPreferences ( this ) ;
- StringBuilder builder = new StringBuilder ( ) ;
- builder. append ( "n" + sharedPrefs. getBoolean ( "perform_updates" , false ) ) ;
- builder. append ( "n" + sharedPrefs. getString ( "updates_interval" , "-1" ) ) ;
- builder. append ( "n" + sharedPrefs. getString ( "welcome_message" , "NULL" ) ) ;
- TextView settingsTextView = ( TextView ) findViewById ( R. id . settings_text_view ) ;
- settingsTextView. setText ( builder. toString ( ) ) ;
- >
- >
Здесь мы извлекаем параметры конфигурации приложения в виде класса SharedPreferences с помощью статического метода getDefaultSharedPreferences класса PreferenceManager. Далее, в зависимости от типа данных параметра, мы используем соответствующий метод извлечения данных (например getBoolean или getString). Второй аргумент в методе извлечения данных — значение по умолчанию, на тот случай если параметр с таким именем еще не был задан. В качестве имен параметров мы используем ключи заданные в XML файле preferences.xml. Значения параметров конкатенируются и выводятся в TextView.
Вот описание простого пользовательского интерфейса для второй активности:
- <?xml version = "1.0" encoding = "utf-8" ?>
- <LinearLayout
- xmlns:android = "schemas.android.com/apk/res/android"
- android:orientation = "vertical"
- android:layout_width = "fill_parent"
- android:layout_height = "fill_parent"
- >
- <TextView
- android:id = "@+id/settings_text_view"
- android:layout_width = "fill_parent"
- android:layout_height = "wrap_content"
- />
- </LinearLayout >
Давайте запустим приложение, мы увидим список наших параметров:
Кликаем на “Updates Interval” и видим список возможных значений:
Кликаем на “Welcome Message” и видим редактирование текста приветствия:
Посмотрим на заданные параметры. Кликнете на кнопке меню и выберете единственный элемент “Show current settings”. Запуститься вторая активность в которой мы увидим значения наших параметров конфигурации:
Читайте также: