Курс

"Мобильная разработка"

Жизненный цикл Activity

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

В методах обратного вызова жизненного цикла вы можете объявить, как поведет себя ваша Activity, когда пользователь закрывает и снова открывает приложение. Например: проигрыватель потокового видео, вы можете поставить видео на паузу и прервать сетевое соединение, когда пользователь переключится на другое приложение. Когда пользователь возвращается, вы можете снова подключиться к сети и позволить ему возобновить видео с того же самого места. Другими словами, каждый обратный вызов позволяет вам выполнять конкретную работу, соответствующую заданному изменению состояния. Выполнение нужной работы в нужное время и правильная обработка переходов делают ваше приложение более надежным и производительным. Например, хорошая реализация обратных вызовов жизненного цикла может помочь убедиться, что ваше приложение избегает:

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

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

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

  4. Сбой или потерю прогресса пользователя при повороте экрана между альбомной и книжной ориентацией

onCreate()

Вы должны реализовать этот обратный вызов, который срабатывает, когда система впервые создает активность. При создании активности, она переходит в состояние «Создано». В методе onCreate() выполняется базовую логика запуска приложения, которая должна происходить только один раз в течение всей жизни активности. Например, ваша реализация onCreate() может связывать данные со списками, ассоциировать активность с ViewModel, а также инициализировать некоторые переменные класса. Этот метод получает параметр savedInstanceState, который является объектом пакета, содержащим сохраненное ранее состояние активности. Если активности никогда ранее не существовало, то значение пакета равно null

 

onStart()

Когда активность переходит в состояние «Запущено», система вызывает этот обратный вызов. Вызов OnStart() делает активность видимой для пользователя, так как приложение готовится к тому, чтобы активность вышла на передний план и стала интерактивной. Например, в этом методе приложение инициализирует код, который поддерживает пользовательский интерфейс.

Метод onStart() завершается очень быстро, и, как и в случае с состоянием «Создано», деятельность не остается в этом состоянии. По окончании обратного вызова активность переходит в состояние «Возобновлено», и система вызывает метод onResume()

onResume()

Когда активность переходит в состояние «Возобновлено», она выходит на передний план, а затем система вызывает обратный вызов onResume(). Это состояние, в котором приложение взаимодействует с пользователем. Приложение остается в этом состоянии до тех пор, пока что-нибудь не переключит фокус от текущего приложения. Таким событием может быть, например, получение телефонного звонка, переход пользователя к другой деятельности или отключение экрана устройства

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

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

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

onPause()

Система вызывает этот метод, когда пользователь покидает вашу активность (хотя это не всегда означает, что она уничтожается); это указывает на то, что Activity больше не находится на переднем плане (хотя она все еще может быть видна, если пользователь находится в многооконном режиме). Используйте метод onPause() для приостановки или изменения операций, которые не должны продолжаться (или должны продолжаться в режиме ожидания), пока активность находится в состоянии паузы она ожидает возобновления в ближайшее время

Завершение работы метода onPause() не означает, что активность выходит из состояния «Приостановлено». Активность остается в этом состоянии до тех пор, пока либо не возобновится, либо не станет полностью невидимой для пользователя. Если активность возобновится, система снова вызовет функцию onResume(). Если активность переходит из состояния «Приостановлено» в состояние «Возобновлено», то система возвращает из памяти сохраненный экземпляр активности, который был записан когда вызвался метод onResume(). Если активность становится полностью невидимой, система вызывает onStop()

onStop()

Когда активность больше не видна пользователю, она переходит в состояние «Остановлено», и система делает обратный вызов onStop(). Это может произойти, например, когда новая запущенная активность охватывает весь экран. Система также может вызвать метод onStop(), когда приложение завершает работу.

Из состояния «Остановлено» активность либо возвращается к взаимодействию с пользователем, либо завершается. Если активность возвращается, то система вызывает onRestart(). Если работа завершена, система вызывает onDestroy()

onDestroy()

onDestroy() вызывается до того, как активность будет уничтожена. Система вызывает этот обратный вызов также потому, что: работа завершается из-за того, что пользователь полностью прекратил взаимодействие или система временно уничтожает активность в связи с изменением конфигурации (например, вращение устройства или многооконный режим)

Основы MVC

Шаг 1

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

Model (модель). Получает данные от контроллера, выполняет необходимые операции и передаёт их в вид

View (вид или представление). Получает данные от модели и выводит их для пользователя

Controller (контроллер). Обрабатывает действия пользователя, проверяет полученные данные и передаёт их модели

Может показаться, что это что-то запутанное, но на самом деле всё просто.

Лучше всего понять концепцию MVC можно на реальном примере — ресторане с фастфудом. В нём посетители (пользователи) заходят в ресторан (это вид) подходят к кассиру (одновременно вид и контроллер), видят меню и заказывают какое-нибудь блюдо.

Кассир проверяет, всё ли в порядке с заказом, и после оплаты передаёт нужные данные повару на кухню (модель). Повар готовит заказанное блюдо, хотя понятия не имеет о том, как выглядит посетитель, оплатил ли он заказ и так далее.

Когда модель закончит свою работу, она отправит результат в вид — обратно кассиру, который, в свою очередь, отдаст готовое блюдо посетителю.

Стоит также отметить, что реализация паттерна MVC может отличаться в зависимости от задачи. Например, в веб-разработке модель и вид взаимодействуют друг с другом через контроллер (как в примере с рестораном), а в приложениях модель может сама уведомлять вид, что нужно что-то изменить.


 

MVC на примере Android Studio

Не существует уникального паттерна MVC. 

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

Модель: что визуализировать

Представление: как сделать

Контроллер: события, пользовательский ввод

В Android в качестве представления мы используем XML разметку и все элементы пользовательского интерфейса.

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

Модель представляют собой классы, описывающие данные в приложении, которые используются для отображения на активности, также модель – это все классы и группы классов, отвечающих за работу с базой данных, взаимодействием с сервером и т.д. Если обобщенно, то модель – это бизнес-логика, которая скрыта от пользователя и выполнятся внутри приложения

Рассмотрим паттерн MVC на простом примере в Android Studio

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

Создадим проект, после того как проект создастся, у нас автоматически провялятся класс активности MainActivity – наш контроллер и разметка экрана activity_main.xml – наше представление.

Модели нет т.к. модель — это логика или бизнес логика приложения. Модель создается отдельно исходя из требований к функционалу приложения

 

Для начала доработаем разметку нашего приложения или view (представление).

Для этого добавим несколько элементов на экран:

Кнопку, поле ввода, текстовый блок и элемент List View для списка заметок:

<?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">

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="600dp"
        android:ems="10"
        android:inputType="text"
        android:text=""
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.496"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="60dp"
        android:text="Напишите заметку"
        app:layout_constraintBottom_toTopOf="@+id/editTextTextPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="536dp"
        android:text="Сохранить"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editTextTextPersonName"
        app:layout_constraintVertical_bias="0.059" />

    <ListView
        android:id="@+id/list"
        android:layout_width="361dp"
        android:layout_height="200dp"
        android:layout_marginTop="52dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button"
        app:layout_constraintVertical_bias="0.186"
        tools:ignore="MissingConstraints"
        tools:layout_editor_absoluteX="25dp" />

    </androidx.constraintlayout.widget.ConstraintLayout>

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

После того как сделали представление, нам необходимо настроить взаимодействие представления и контроллера. Для этого в классе контроллере MainActivity необходимо вывести разметку экрана – это делается автоматически в методе setContentView() и сделать доступными элементы разметки для этого определим их как объекты:

 

public class MainActivity extends AppCompatActivity {

 

    Button button;

    EditText editText;

    ListView listView;

 

    Note note;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

 

        button = (Button) findViewById(R.id.button);

        editText = (EditText) findViewById(R.id.editText);

        listView = (ListView) findViewById(R.id.list);

 

    }

 

}

 

Теперь наш контроллер может взаимодействовать с представлением

Основы MVC

Шаг 2

Продолжаем изучать паттерн MVC в Android Studio. В прошлом уроке мы разобрали общие понятия данного паттерна, создали представление (xml разметку экрана приложения) и практически полностью описали контроллер, связав его с представлением

Осталось описать модель и связать её с контроллером

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

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

Структура класса Note:

package com.example.myapplication;

import java.util.ArrayList;

public class Note {
    ArrayList<String> arrayList;
    static int count = 0;

    public Note(){
        this.arrayList = new ArrayList<String>();
    }

    public void addNote(String s){
        this.arrayList.add(++count + "-" + s);
    }

    public String[] getNotes(){
        return (String[]) this.arrayList.toArray(new String[arrayList.size()]);
    }
}

 

Теперь необходимо настроить взаимодействие контроллера с моделью.

В методе onCreate() создадим объект класса Note:

 

note = new Note();

 

Создадим отдельный метод, позволяющий связать модель и контроллер:

 

public void buttonClick(){}

 

В данном методе будем получать данные, введенные пользователем в поле EditText:

 

String tmpText = editText.getText().toString();


 

После этого, полученные данные будем передавать модели при помощи метода addNote():

 

note.addNote(tmpText);

 

Для получения списка всех записей будем использовать метод getNotes():

 

String[] arrNotes = note.getNotes();

 

Для того, чтобы полученные данные вывести в виде списка, создадим адаптер с вызовем его для поля listView:

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arrNotes);

        listView.setAdapter(adapter);

 

По итогу получим следующий метод:

public void buttonClick(){

        String tmpText = editText.getText().toString();

        note.addNote(tmpText);

 

        String[] arrNotes = note.getNotes();

 

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arrNotes);

        listView.setAdapter(adapter);

    }


 

Метод buttonClick() будем вызывать в момент, когда пользователь нажмет кнопка «Сохранить», для этого создадим в методе onCreate() обработчик нажатия:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        buttonClick();
    }
});

 

После всех действий наш класс будет выглядеть следующим образом:

package com.example.myapplication;

 

import androidx.appcompat.app.AppCompatActivity;

 

import android.os.Bundle;

import android.view.View;

import android.widget.ArrayAdapter;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ListView;

import android.widget.TextView;

 

public class MainActivity extends AppCompatActivity {

 

    Button button;

    EditText editText;

    ListView listView;

 

    Note note;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

 

        button = (Button) findViewById(R.id.button);

        editText = (EditText) findViewById(R.id.editText);

        listView = (ListView) findViewById(R.id.list);

 

        note = new Note(); 

 

        button.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                buttonClick();

            }

        });

 

    }

 

    public void buttonClick(){

        String tmpText = editText.getText().toString();

        note.addNote(tmpText);

 

        String[] arrNotes = note.getNotes();

 

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arrNotes);

        listView.setAdapter(adapter);

    }

 

}

Обработка нажатий

Кнопка - один из самых распространенных элементов управления в программировании. Наследуется от TextView и является базовым классом для класса СompoundButton

От класса CompoundButton в свою очередь наследуются такие элементы как CheckBox, ToggleButton и RadioButton

 

В Android для кнопки используется класс android.widget.Button. На кнопке располагается текст и на кнопку нужно нажать, чтобы получить результат. Альтернативой ей может служить компонент ImageButton (android.widget.ImageButton), у которого вместо текста используется изображение

В студии кнопка представлена компонентом Button в разделе Widgets

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

Если вы растягиваете кнопку по всей ширине экрана (android:layout_width="match_parent"), то дополнительно рекомендую использовать атрибут android:layout_margin (или родственные ему layout_marginRight и layout_marginLeft) для создания отступов от краев экрана (веб-мастера знакомы с этими терминами)

Так как кнопка является наследником TextView, то использует многие знакомые атрибуты: textColor, textSize и др.

Три способа обработки событий нажатий на кнопку

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

Первый способ - атрибут onClick

Относительно новый способ, специально разработанный для Android - использовать атрибут onClick:

android:onClick="buttonClick"

 

Данный атрибут указывается в XML разметке экрана приложения

Имя для события можно выбрать произвольное, но лучше не выпендриваться. Далее нужно прописать в классе активности придуманное вами имя метода, который будет обрабатывать нажатие. Метод должен быть открытым (public) и с одним параметром, использующим объект View

public void buttonClick (View view)  

{  

    // выводим сообщение

    Toast.makeText(this, "Вы нажали на кнопку", Toast.LENGTH_SHORT).show();  

}  

Когда пользователь нажимает на кнопку, то вызывается метод buttonClick(), который в свою очередь генерирует всплывающее сообщение

Обратите внимание, что при подобном подходе вам не придётся даже объявлять кнопку через конструкцию (Button) findViewById(R.id.button1), так как Android сама поймёт, что к чему. Данный способ применим не только к кнопке, но и к другим элементам и позволяет сократить количество строк кода.

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

Второй способ - метод setOnClickListener()

Более традиционный способ в Java - через метод setOnClickListener(), который прослушивает нажатия на кнопку. 

Предположим на экране есть кнопка button. В коде объявляете её обычным способом:

Button button = (Button) findViewById(R.id.button)

Следующий шаг - написание метода для нажатия:

button.setOnClickListener()

Курсор будет находиться внутри скобок и появится подсказка OnClickListener l. Начинайте набирать new OnClickListener. Здесь также не обязательно набирать имя полностью. Набрав слово Oncl, вы увидете нужный вариант и снова нажимайте Enter. В результате вы получите готовую заготовку для обработки нажатия кнопки:

button.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

        

    }

});

Теперь у вас есть рабочая заготовка и сразу внутри фигурных скобок метода onClick() вы можете писать свой код

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

OnClickListener myButtonClickListener = new OnClickListener() {

@Override

public void onClick(View v) {

 

}

};

button.setOnClickListener(myButtonClickListener);

 

Третий способ - интерфейс OnClickListener

Третий способ является родственным второму способу и также является традиционным для Java. Кнопка присваивает себе обработчика с помощью метода setOnClickListener (View.OnClickListener l), т.е. подойдет любой объект с интерфейсом View.OnClickListener. Мы можем указать, что наш класс Activity будет использовать интерфейс View.OnClickListener

Для этого после слов extends AppCompatActivity дописываем слова implements OnClickListener. При появлении подсказки не ошибитесь. Обычно первым идёт интерфейс для диалогов, а вторым нужный нам View.OnClickListener.

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

 

@Override

public void onClick(View v) {

}

 

Метод будет реализован не в отдельном объекте-обработчике, а в Activity, который и будет выступать обработчиком. В методе onCreate() присвоим обработчик кнопке. Это будет объект this, т.е. текущий объект нашей активности.

 

button.setOnClickListener(this);

 

Анонимные классы

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

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

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

Появляется вопрос, а зачем создавать анонимные классы? 

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

Вернемся к нашему примеру с OnClickListener. 

 

        button.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

               // код обпработчика.

            }

        });

 

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

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

Эта тема не содержит тестов или других форм контроля знаний

Понравился курс?

Подпишись на странице обсуждений и стань участником курса