Освой Android играючи

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

Шкодим

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

Сохраняем настройки

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

Введение

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

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

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

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

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

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

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

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

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


private lateinit var prefs: SharedPreferences

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    prefs =
        getSharedPreferences("settings", Context.MODE_PRIVATE)
}

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

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

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


override fun onPause() {
    super.onPause()

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

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


override fun onResume() {
    super.onResume()

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

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

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

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

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

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

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

Исходный код (Kotlin)

Разметка


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">

    <Button
        android:id="@+id/crowButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Считаем ворон"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/infoTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="0"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/crowButton" />

</androidx.constraintlayout.widget.ConstraintLayout>

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


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

package ru.alexanderklimov.counter

import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private lateinit var prefs: SharedPreferences
    private val APP_PREFERENCES_COUNTER = "counter"
    private var counter: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        prefs =
            getSharedPreferences("settings", Context.MODE_PRIVATE)

        crowButton.setOnClickListener {
            counter = ++counter
            infoTextView.text = "Я насчитал $counter ворон"
        }
    }

    override fun onPause() {
        super.onPause()

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

    override fun onResume() {
        super.onResume()

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

Исходный код (Java)

Разметка


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/buttonCrowsCounter"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Считаем ворон" />

    <TextView
        android:id="@+id/textViewInfo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="" />

</LinearLayout>

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


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

package ru.alexanderklimov.counter;

import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {

    private int mCounter;
    private TextView mInfoTextView;

    // имя файла настройки
    public static final String APP_PREFERENCES = "mysettings";
    public static final String APP_PREFERENCES_COUNTER = "counter";
    private SharedPreferences mSettings;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mSettings = getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE);
        mInfoTextView = (TextView) findViewById(R.id.textViewInfo);
    }

    public void onClick(View v) {
        // Выводим на экран
        mInfoTextView.setText("Я насчитал " + ++mCounter + " ворон");
    }

    @Override
    protected void onResume() {
        super.onResume();

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

    @Override
    protected void onPause() {
        super.onPause();
        // Запоминаем данные
        SharedPreferences.Editor editor = mSettings.edit();
        editor.putInt(APP_PREFERENCES_COUNTER, mCounter);
        editor.apply();
    }
}

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

Теория

Сохраняем объекты в настройках SharedPreferences

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

Реклама