Освой программирование играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

SharedPreferences: Сохраняем настройки (Kotlin)

Девочка встретила котёнка

Введение

Очень часто требуется сохранить какие-то настройки. Простой пример - девочка с удовольствим пользовалась нашей программой Счётчик ворон и вертела головой в поисках очередной птицы. Но вот незадача - когда девочка вечером закрывала своё приложение, то на следующий день счётчик обнулялся. А так хотелось узнать, сколько ворон можно насчитать за месяц. Но однажды в дождливый осенний день она встретила рыжее чудо. Впрочем, это уже совсем другая история.

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

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

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

На самом деле нет необходимости изобретать свой велосипед и придумывать свою структуру для хранения данных. В Android существует класс SharedPreferences (Общие настройки), разработанный специально для этих целей. Приложение автоматически создаёт файл в своей папке и хранит простые данные в виде «ключ — значение». Весь процесс создания, открытия, чтения файла оптимизирован и избавляет вас от головной боли.

Общие настройки поддерживают базовые типы boolean, String, float, long и int, что делает их идеальным средством для быстрого сохранения значений по умолчанию, переменных экземпляра класса, текущего состояния UI и пользовательских настроек. Они чаще всего используются для обеспечения постоянства данных между пользовательскими сессиями и доступа к ним компонентов приложения.

Сохранение значений параметров

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

Для удобства создадим константу для имени файла настроек, например:


// имя файла настроек
val APP_PREFERENCES = "mysettings"

Создадим ключ - параметр, который мы хотим сохранять в настройках. Нас интересуют показания счётчика.


val APP_PREFERENCES_COUNTER = "counter"

Создаём переменную, представляющую экземпляр класса SharedPreferences, который отвечает за работу с настройками:


lateinit var pref: SharedPreferences

Внутри метода onCreate() инициализируем эту переменную::


pref = getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE)

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

Немного опередим события и представим, что приложение запущено, и мы считаем ворон весь день. Когда мы закрываем приложение, то нам необходимо сохранить информацию в Общих настройках. Обычно для этих целей используют методы onPause() или onStop().

Чтобы внести изменения в настройки, нужно использовать класс SharedPreferences.Editor. Получить объект Editor можно через вызов метода edit() объекта SharedPreferences. После того, как вы внесли все необходимые изменения, вызовите метод apply(), чтобы изменения вступили в силу.


override fun onPause() {
    super.onPause()

    // Запоминаем данные
    val editor = pref.edit()
    editor.putInt(APP_PREFERENCES_COUNTER, counter)
    editor.apply()
}

Теперь при закрытии программы значение счётчика автоматически запишется в файл. При повторном запуске приложения нам уже не нужно инициализировать счётчик со значением 0. Мы можем прочитать сохранённое значение и использовать его для счётчика, чтобы продолжить подсчёт. Сделаем это в методе onResume().


override fun onResume() {
    super.onResume()

    if (pref.contains(APP_PREFERENCES_COUNTER)) {
        // Получаем число из настроек
        counter = pref.getInt(APP_PREFERENCES_COUNTER, 0);
        // Выводим на экран данные из настроек
        text.setText("Я насчитал $counter ворон")
    }
}

Мы проверяем сначала наличие ключа APP_PREFERENCES_COUNTER, а затем извлекаем из ключа его значение.

Вот и всё. Небольшие изменения в коде сделали программу продвинутой. Теперь вы можете спокойно закрывать и открывать программу, ваши данные не будут потеряны. При желании вы можете добавить кнопку для сброса счётчика. Это вам в качестве домашнего задания.

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

Вместо послесловия

Не волнуйтесь, с котёнком всё в порядке. Девочка подобрала его и принесла домой. И добрая девочка по-прежнему пользуется нашей программой "Счётчик ворон". Наверное, биологом станет или ветеринаром.

При написании статьи использовались иллюстрации Рины З..

Исходный код

Разметка


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="46dp"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Код класса активности


// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.

package ru.alexanderklimov.counter

import android.content.SharedPreferences
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private lateinit var pref: SharedPreferences
    // имя файла настроек
    private val APP_PREFERENCES = "mysettings"
    private val APP_PREFERENCES_COUNTER = "counter"
    private var counter = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        title = "SharedPreferences"

        pref = getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE)

        button.setOnClickListener{
            counter++
        }
    }

    override fun onPause() {
        super.onPause()

        // Запоминаем данные
        val editor = pref.edit()
        editor.putInt(APP_PREFERENCES_COUNTER, counter)
        editor.apply()
    }

    override fun onResume() {
        super.onResume()

        if (pref.contains(APP_PREFERENCES_COUNTER)) {
            // Получаем число из настроек
            counter = pref.getInt(APP_PREFERENCES_COUNTER, 0);
            // Выводим на экран данные из настроек
            text.setText("Я насчитал $counter ворон")
        }
    }
}

Дополнительное чтение

Обсуждение урока на форуме

Реклама