Activity stub что это
Перейти к содержимому

Activity stub что это

  • автор:

Урок 29. Вызываем Activity и получаем результат. Метод startActivityForResult

Бывает необходимость вызвать Activity, выполнить на нем какое-либо действие и вернуться с результатом. Например – при создании SMS. Вы жмете кнопку «добавить адресата», система показывает экран со списком из адресной книги, вы выбираете нужного вам абонента и возвращаетесь в экран создания SMS. Т.е. вы вызвали экран выбора абонента, а он вернул вашему экрану результат.

Об этом можно почитать здесь.

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

Создадим проект:

Project name: P0291_SimpleActivityResult
Build Target: Android 2.3.3
Application name: SimpleActivityResult
Package name: ru.startandroid.develop.p0291simpleactivityresult
Create Activity: MainActivity

Открываем main.xml и нарисуем такой экран:

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

Кодим MainActivity.java:

package ru.startandroid.develop.p0291simpleactivityresult; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener < TextView tvName; Button btnName; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.main); tvName = (TextView) findViewById(R.id.tvName); btnName = (Button) findViewById(R.id.btnName); btnName.setOnClickListener(this); >@Override public void onClick(View v) < Intent intent = new Intent(this, NameActivity.class); startActivityForResult(intent, 1); >@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) < if (data == null) String name = data.getStringExtra("name"); tvName.setText("Your name is " + name); > >

Определяем TextView и кнопку, настраиваем обработчик. В методе обработчика onClick создаем Intent, указываем класс второго Acivity (которое создадим чуть позже, на ошибку не обращайте внимания). Для отправки используем startActivityForResult. Отличие от обычного startActivity в том, что MainActivity становится «родителем» для NameActivity. И когда NameActivity закрывается, вызывается метод onActivityResult в MainActivity, тем самым давая нам знать, что закрылось Activity, которое мы вызывали методом startActivityForResult.

В startActivityForResult в качестве параметров мы передаем Intent и requestCode. requestCode – необходим для идентификации. В этом уроке мы его укажем, но не будем использовать по назначению. В следующем же уроке разберемся подробнее, зачем он нужен.

В onActivityResult мы видим следующие параметры:
requestCode – тот же идентификатор, что и в startActivityForResult. По нему определяем, с какого Activity пришел результат.
resultCode – код возврата. Определяет успешно прошел вызов или нет.
data – Intent, в котором возвращаются данные

requestCode и resultCode мы пока использовать не будем, подробнее рассмотрим их на следующем уроке. А из data мы будем получать объект по имени name и выводить значение в TextView.

Если мы извлекаем из Intent объект с именем name, значит надо, чтобы кто-то его туда положил. Этим займется NameActivity.

Создадим экран name.xml:

В поле ввода будем вводить имя и жать кнопку OK.

Создаем класс NameActivity и прописываем его в манифесте:

package ru.startandroid.develop.p0291simpleactivityresult; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; public class NameActivity extends Activity implements OnClickListener < EditText etName; Button btnOK; @Override protected void onCreate(Bundle savedInstanceState) < // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.name); etName = (EditText) findViewById(R.id.etName); btnOK = (Button) findViewById(R.id.btnOK); btnOK.setOnClickListener(this); >@Override public void onClick(View v) < Intent intent = new Intent(); intent.putExtra("name", etName.getText().toString()); setResult(RESULT_OK, intent); finish(); >>

Определяем поле ввода и кнопку, прописываем обработчик. В методе onClick мы создаем Intent и помещаем в него данные из поля ввода под именем name. Обратите внимание, мы никак не адресуем этот Intent. Т.е. ни класс, ни action мы не указываем. И получается, что непонятно куда пойдет этот Intent. Но метод setResult знает, куда его адресовать — в «родительское» Activity, в котором был вызван метод startActivityForResult. Также в setResult мы передаем константу RESULT_OK, означающую успешное завершение вызова. И именно она передастся в параметр resultCode метода onActivityResult в MainActivity.java. Это мы подробнее разберем на следующем уроке. Далее методом finish мы завершаем работу NameActivity, чтобы результат ушел в MainActivity.

Все сохраним и запустим приложение.

Видим первый экран:

Жмем кнопку, чтобы попасть на экран ввода имени.

Вводим имя и жмем ОК

Снова первый экран, отобразивший полученные данные.

Попробуем подытожить. В MainActivity мы создали Intent с явным указанием на класс NameActivity. Запустили этот Intent с помощью метода startActivityForResult. NameActivity отобразилось, мы ввели имя и нажали кнопку. Создался Intent, в который поместилось введенное нами имя. Метод setResult знает, что Intent надо вернуть в Activity, которое выполнило вызов startActivityForResult, т.е. – MainActivity. В MainActivity за прием результатов с вызванных Activity отвечает метод onActivityResult. В нем мы распаковали Intent и отобразили полученные данные в TextView.

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

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

На следующем уроке:

— разбираемся, зачем нужны requestCode и resultCode в onActivityResult

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

Как работает Activity. Часть 2

В прошлой статье я описал, как стартует процесс нашего приложения, что такое ActivityStarter и как стартуют все Activity.

Во второй части расскажу, как показываем сплеш-скрин, что такое Window, что происходит перед первым показом Activity приложения, более подробно, как вызываются методы жизненных циклов Activity и что происходит с Activity при сворачивании и разворачивании.

Как показать сплеш-экран

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

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

Window на экране может быть несколько, например статус-бар рисуется в своем Window, наши Activity — в другом, NavigationBar — в третьем.

Процесс отрисовки View

Как отрисовать View: берем Activity, у которой есть Window. У Window есть корневая View, к которой прикрепляются уже все наши ViewGroup и View.Когда View нужно перерисоваться, она вызывает метод invalidate — по иерархии поднимается наверх.

Window вызывает метод lock у Surface, и тот в ответ выдает Canvas. Мы что-то рисуем на этом Canvas, затем Window вызывает метод unlockAndPost у Surface, и тот отправляет наш Canvas в буфер на прорисовку.

Отправка Canvas на прорисовку

Вернемся к сплешу. За его показ отвечает специальный класс Starting Surface Controller, который запускает ActivityStarter из прошлой части.

Сплеш работает по такой схеме: у системы всегда есть доступ к теме конкретной Activity, которую мы сейчас запускаем. Тему берем из специального класса ActivityInfo. Система вытаскивает эту тему, затем создает Window, после создает FrameLayout и запихивает его в только что созданную Window. Ну а после того, как показывается первая Activity, Window сплеша удаляется — и мы видим уже Window первой Activity.

fun addSplashScreenStartingWindow(/* . */) < /* . */ // replace with the default theme if the application didn't set val theme: Int = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo) context.setTheme(theme) /* . */ val rootLayout = FrameLayout(mSplashscreenContentDrawer.createViewContextWrapper(context)) rootLayout.fitsSystemWindows = false /* . */ /* Вот тут и показывается Splash путем добавления нового окна */ addWindow(taskId, appToken, rootLayout, display, params, suggestType) /* . */ >

Сплеш показывается в отдельном Window, поэтому важно работать с ним именно через настройки темы первой Activity. Если просто показать картинку в первой Activity, это не будет сплешем. В таком случае это будет просто windowBackground, который, скорее всего, белый, и выглядит это так себе.

Правильный сплеш получается так:

1️⃣ создается специальная тема с атрибутом windowBackground под нужный цвет или drawable;

2️⃣ эта тема устанавливается в манифесте;

3️⃣ после старта Activity в методе onCreate тема меняется на основную.

И вуаля, все будет работать красиво.

Начиная с Android 12 все стало немного проще. Google зарелизил специальное API для показа сплеша, которое позволяет прикрутить разное кастомное поведение, сделать его анимированным, подержать подольше. Правда, именно анимированным сплеш получится сделать только с Android 12.

Если не брать в расчет анимацию, то Splash API не делает какой-то магии. Что раньше, что сейчас, нужно правильно сконфигурировать тему для сплеш-экрана. Splash API предоставляет свою тему с более очевидными параметрами. Помимо этого, Splash API переключает тему сплеша на тему нашего приложения самостоятельно. Раньше это переключение делали руками, сейчас немного удобнее.

Старт первой Activity приложения

Привычно, что у приложений, которые мы пишем, нет статической функции main. Когда я впервые встретил проект под Android, был слегка потерян: непонятно, как контролировать процесс запуска без main.

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

А сейчас — внимание! — будет много кода и объяснения, как это все работает.

Метод main скрыт в классе ActivityThread — именно с него начинается любое приложение в системе. В этом методе инициализируется главный Looper, и потом вызывается метод attachApplication у ActivityManagerService. Этим мы сообщаем сервису, что стартанули приложения и нам сейчас нужна помощь с Activity.

class ActivityThread < /* . */ fun main(arg: Array) < /* . */ Looper.prepare() /* . */ attachApplication(applicationThread) >>

Процесс запуска первой Activity

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

class ActivityThread < private val applicationThread = ApplicationThread() inner class ApplicationThread : IApplicationThread.Stub < /* Это метод дергает ActivityManagerService */ fun bindApplication(data: Data) < val message = Message.obtain(); /* Отправляем сообщение в Looper главного потока */ Looper.getMainLooper().sendMessage(message) >> fun main(arg: Array) < /* . */ Looper.prepare() /* . */ attachApplication(applicationThread) >>

После настроек ActivityManagerService вызываем метод ApplicationThread, потому что при вызове метода attachApplication мы передали объект нашего ApplicationThread и его можно вызвать при помощи Binder.

ActivityThread отправляет сообщение BIND_APPLICATION в Looper главного потока и запускает этот самый Looper

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

/* Этот метод дергает ActivityManagerService */ fun bindApplication(data: Data) < val message = Message.obtain(); /* init message */ Looper.getMainLooper().sendMessage(BIND_APPLICATION, message) >

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

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

Как система вызывает методы жизненного цикла

Система напрямую не вызывает методы ЖЦ Activity, потому что они обязаны происходить в главном потоке. Единственный вариант, как вызывать методы ЖЦ из главного потока, — отправлять специальные сообщения в Lopper самого потока. Сообщения, в которых описывалось бы, что за Activity и какой метод ЖЦ нужно вызвать.

Система создает специальный объект — транзакцию, в которой есть вся информация, перечисленная выше. Такая транзакция существует для каждого метода жизненного цикла Activity. Затем транзакция через IPC отправляется в TransactionExecutor, который работает на стороне нашего приложения.

/* Объект транзакции для перевода Activity в статус onStop */ class StopActivityItem : ActivityLifecycleItem < override fun execute(/* . */) < /* . */ client.handleStopActivity() >>

TransactionExecutor — обработчик транзакций. Он получает транзакцию, затем на основе информации из транзакции создает сообщение, которое отправляет в Lopper главного потока. После чего уже Looper вызывает нужный метод нужной Activity.

Как вызвать методы жизненного цикла Activity

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

class ActivityThread < /* Дергает методы конкретных Activity */ private val transactionExecutor = TransactionExecutor() private val handler = ActivityThreadHandler(Looper.getMainLooper()) inner class ApplicationThread : IApplicationThread.Stub < /* . */ /* Этот метод дергают разные системные сервисы */ fun scheduleTransaction(data: Data) < val message = Message.obtain(); /* Собираем сообщение с транзакцией */ Looper.getMainLooper().sendMessage(EXECUTE_TRANSACTION, message) >> inner class ActivityThreadHandler : Handler < override fun handleMessage(msg: Message) < when (msg.what) < BIND_APPLICATION ->bindApp(msg.obj) EXECUTE_TRANSACTION -> transactionExecutor.execute(msg.obj) > > > >

И вот наше приложение получает первую транзакцию с информацией о том, какую Activity запустить. Если всегда было интересно, как именно создается объект Activity, — вот описание. Ничего сложного, из полученного Intent достается имя класса и через рефлексию создается объект.

Ну а дальше само приложение вызывает методы ЖЦ Activity в зависимости от статуса показа Activity. В большинстве случаев методы ЖЦ Activity берет само приложение, другими словами, они не вызываются системными сервисами. Например, когда приложение получает сигнал о том, что нужно свернуться, оно само должно вызвать методы onStop у тех Activity, что мы видим на экране.

Еще в системе есть куча различных сервисов, которые знают о наших Activity через ActivityRecord и решают, у какой Activity какой ЖЦ вызывать.

Activity во время сворачивания приложения

Есть специальный системный сервис, который слушает кнопку Home или жест смахивания. Когда это происходит, он анимирует сворачивание активного приложения и показывает Activity нашего лаунчера. Мы не выходим из приложения — просто наша Activity заменяется на Activity лаунчера.

С точки зрения вызовов ЖЦ ничего не меняется, вызываются они также через транзакции.

Анимацией смены окон занимается отдельный системный сервис. Именно поэтому, даже если, например, заблокировать при помощи Thread.sleep() вызов onStop, приложение все равно красиво свернется и это никак не заденет пользователя

За показ старта приложения отвечает Starting Surface Controller. Интересен этот класс тем, что помимо показа сплеша при старте приложения он запоминает, как выглядит приложение при сворачивании, чтобы потом показать его в Recents.

У ActivityRecord для этого есть специальный метод. Работает это так: система понимает, что нужно свернуть приложение, достает ActivityRecord той Activity, которая сейчас есть на экране, и вызывает специальный метод, чтобы ActivityRecord при помощи Starting Surface Controller запомнил, как выглядела Activity до сворачивания.

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

Вместо заключения

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

Концепции, которые используются в Android, не уникальны и взяты из других систем. Например, легко догадаться, откуда взяли Window. Идея аналогичная, у нас есть абстракция, которая позволяет управлять размером окна приложения и количеством этих окон, например, для такой фичи, как Split Screen.

Создание процесса реализовано при помощи идей из Unix. Концепции Looper MessageQueue и Handler перетекли из Java-фреймворка Swing, который работает по аналогичной схеме. Binder базируются на идеях System V IPC, который также идет из Unix.

Знание фундаментальных концепций помогает быстро разбираться в том, как устроен тот или иной фреймворк или библиотека. Фреймворки и библиотеки появляются, взлетают и также быстро деприкейтятся (привет, Google!), а концепции очень медленно устаревают и редко придумываются действительно уникальные.

Если вам понравилась статья и иллюстрации к ней, подписывайтесь на мой телеграм-канал. Я пишу про Android-разработку и Computer Science, на канале есть больше интересных постов про разработку под Android. Еще у нас есть ютуб-канал, где мы публикуем интересное про мобильную разработку.

  • android
  • разработка под android
  • androdev
  • разработка
  • Блог компании TINKOFF
  • Разработка под Android

What is a «Stub»?

So, carrying on with my new years resolution to get more in to TDD, I am now starting to work more with Rhino Mocks. One thing I am keen to do is to make sure I really grok what I am getting into, so I wanted to check my understanding of what I have seen so far (and I thought it would be good to get it up here as a resource).

What is a «Stub»?

23.5k 7 7 gold badges 31 31 silver badges 54 54 bronze badges
asked Jan 20, 2009 at 21:49
Rob Cooper Rob Cooper
28.6k 26 26 gold badges 103 103 silver badges 142 142 bronze badges
first you ask a question, and then you give the answer?
Jan 20, 2009 at 21:55

As stated in the question — I am looking for verification of my understanding being correct, I wanted the answer to be here (be it mine or not) to provide a resource on this much-asked-but-rarely-well-answered question 🙂

Jan 20, 2009 at 21:57

Also answering ones own question is indicated in the FAQ as a valid way of using the site — it should be encouraged, in fact.

Jan 20, 2009 at 21:58

It’s fine to answer your own questions, though the question itself has a bit too much noise (autobiography), and shows no effort to find an answer, no inclination of what the person has done to try to find the answer.

Mar 28, 2015 at 12:16

6 Answers 6

Martin Fowler wrote an excellent article on this subject. From that article:

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
  • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it ‘sent’, or maybe only how many messages it ‘sent’.
  • Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

To put it in my own words: mock objects «expect» certain methods to be called on them, and typically cause a unit test to fail if their expectations aren’t met. Stub objects provide canned responses (and can be autogenerated by helper libraries), but typically do not directly cause the unit test to fail. They are typically just used so that the object you’re testing gets the data it needs to do its work.

answered Jan 20, 2009 at 21:57
9,668 8 8 gold badges 36 36 silver badges 35 35 bronze badges

Then what’s the difference between fake objects and sutbs? As you stated, they have the same role to be implemented, isn’t it?

May 31, 2016 at 5:35

@LKM a fake object is an actual object that implements real functionality and does things. Stubs are just «canned answers» eg. hard-coded JSON results that mimic what would be returned from the web server.

Feb 13, 2017 at 2:22

Dummy, fake, stub, mock, double, pretend, phantom, puppet, sentinel. New word after new word, until hell of new words.

Apr 1, 2022 at 4:27

A «stub» is an implementation of an interface that exists to provide data/a response of some sort. For example:

  • a DataSet
  • list of Users
  • an Xml File

Normally this would be provided by another service (be it Web Service, another application, a database) but in order to improve the testability of the code, the results are «faked».

A major benefit of this is that it allows assertions to be made in unit tests based on the data expected. If errors arise due to data errors, then tests can easily be added, a new stub created (replicating the data error) and code produced to correct the error.

Stubs differ to Mocks in that they are used to represent and test the state of an object, whereas a Mock tests its interaction.

answered Jan 20, 2009 at 21:51
Rob Cooper Rob Cooper
28.6k 26 26 gold badges 103 103 silver badges 142 142 bronze badges
thanks for your answer, but i still do not understand: «an implementation of an interface»??
Mar 17, 2016 at 22:03

It’s like subclassing from a NetworkManager class and overwriting the methods you only care about so it can return back fixed data (instead of the code using the regular method). An example is overwriting a fetch request method that returns back a fixed data object instead of the test making an actual network call through the regular fetch request.

Jun 29, 2021 at 14:50

I faced the question recently and recognised that this comparison between Stub and Driver is really clear and helpful:

Basically, stubs and drivers are routines that don’t actually do anything except declare themselves and the parameters they accept. The rest of the code can then take these parameters and use them as inputs.

+---------+-------------------------------+-------------------------------+ | | Stub | Driver | +---------+-------------------------------+-------------------------------+ | Type | Dummy codes | Dummy codes | +---------+-------------------------------+-------------------------------+ | Used in | Top Down Integration | Bottom Up Integration | +---------+-------------------------------+-------------------------------+ | Purpose | To allow testing of the upper | To allow testing of the lower | | | levels of the code, when the | levels of the code, when the | | | lower levels of the code are | upper levels of the code are | | | not yet developed. | not yet developed. | +---------+-------------------------------+-------------------------------+ | Example | A and B are components. | A and B are components. | | | A ---> B | A ---> B | | | | | | | A has been developed. | A still needs to be developed.| | | B still needs to be developed.| B has been developed. | | | Therefore, stub is used | Therefore, driver is used | | | in place of B to imitate it. | in place of A to imitate it | | | | | | | A ---> Stub | Driver ---> B | +---------+-------------------------------+-------------------------------+

Жизненный цикл фрагментов

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

Жизненный цикл фрагментов

Список дополнительных методов жизненного цикла фрагментов, которых нет у активности:

onAttach(Activity) Вызывается, когда фрагмент связывается с активностью. С этого момента мы можем получить ссылку на активность через метод getActivity() onCreate() В этом методе можно сделать работу, не связанную с интерфейсом. Например, подготовить адаптер. onCreateView(LayoutInflater, ViewGroup, Bundle) Вызывается для создания компонентов внутри фрагмента onViewCreated() Вызывается сразу после onCreateView() onActivityCreated(Bundle) Вызывается, когда отработает метод активности onCreate(), а значит фрагмент может обратиться к компонентам активности onStart() Вызывается, когда фрагмент видим для пользователя и сразу же срабатывает onStart() активности onDestroyView() onResume() Вызываем после onResume() активности Вызывается, когда набор компонентов удаляется из фрагмента onDetach() Вызывается, когда фрагмент отвязывается от активности

Одноимённые с методами активности методы фрагментов выполняют аналогичные функции. К примеру, метод onResume() вызывается, когда фрагмент вновь становится видимым.

Метод onStart() вызывается, когда фрагмент становится видимым после запуска такого же метода в родительской активности.

Фрагмент всегда связан с активностью. Отдельно фрагмент от активности существовать не может.

Если активность останавливается, то её фрагменты также останавливаются. Если активность уничтожается, то её фрагменты также уничтожаются.

Метод onCreateView() вызывается один раз, когда фрагмент должен загрузить на экран свой интерфейс. В этом методе вы можете «надуть» (inflate) разметку фрагмента через метод inflate() объекта Inflater, который задан в параметре метода. В фрагментах без интерфейса вы можете пропустить надувание.

Метод onActivityCreated() вызывается после метода onCreateView(), когда создаётся активность-хозяйка для фрагмента. Здесь можно объявить объекты, необходимые для Context.

Фрагменты не являются подклассами Context, вам следует использовать метод getActivity(), чтобы получить родительскую активность.

Создадим несколько фрагментов и с помощью логов посмотрим на срабатывание методов. Первоначально пример писался на Java, будет приведён аналог на Kotlin только для первого фрагмента, остальное придётся написать самостоятельно.

FirstFragment

 // Kotlin package ru.alexanderklimov.fragmentdemo import android.content.Context import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment class Fragment1 : Fragment() < override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) Log.d("Fragment1", "onCreate") >override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? < val rootView = inflater.inflate(R.layout.fragment1, container, false) Log.d("Fragment1", "onCreateView") return rootView >override fun onAttach(context: Context) < super.onAttach(context) Log.d("Fragment1", "onAttach") >override fun onStart() < super.onStart() Log.d("Fragment1", "onStart") >override fun onResume() < super.onResume() Log.d("Fragment1", "onResume") >override fun onStop() < Log.d("Fragment1", "onStop") super.onStop() >override fun onDestroyView() < Log.d("Fragment1", "onDestroyView") super.onDestroyView() >override fun onDestroy() < Log.d("Fragment1", "onDestroy") super.onDestroy() >override fun onDetach() < Log.d("Fragment1", "onDetach") super.onDetach() >> 
// Java package ru.alexanderklimov.fragmentdemo; import android.app.Activity; import android.net.Uri; import android.os.Bundle; import android.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class FirstFragment extends Fragment < public static FirstFragment newInstance(String param1, String param2) < FirstFragment fragment = new FirstFragment(); // Bundle args = new Bundle(); return fragment; >public FirstFragment() < // Required empty public constructor >@Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); Log.d("Fragment 1", "onCreate"); >@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) < View rootView = inflater.inflate(R.layout.fragment_first, container, false); Log.d("Fragment 1", "onCreateView"); return rootView; >@Override public void onAttach(Activity activity) < super.onAttach(activity); Log.d("Fragment 1", "onAttach"); >@Override public void onActivityCreated(Bundle savedInstanceState) < super.onActivityCreated(savedInstanceState); Log.d("Fragment 1", "onActivityCreated"); >@Override public void onStart() < super.onStart(); Log.d("Fragment 1", "onStart"); >@Override public void onResume() < super.onResume(); Log.d("Fragment 1", "onResume"); >@Override public void onPause() < super.onPause(); Log.d("Fragment 1", "onPause"); >@Override public void onStop() < super.onStop(); Log.d("Fragment 1", "onStop"); >@Override public void onDestroyView() < super.onDestroyView(); Log.d("Fragment 1", "onDestroyView"); >@Override public void onDestroy() < super.onDestroy(); Log.d("Fragment 1", "onDestroy"); >@Override public void onDetach() < super.onDetach(); //mListener = null; Log.d("Fragment 1", "onDetach"); >>

Разметка для первого фрагмента

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

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

activity_main.xml

MainActivity.java

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

 package ru.alexanderklimov.fragmentdemo; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; public class MainActivity extends ActionBarActivity < Button fragment1Button, fragment2Button; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fragment1Button = (Button) findViewById(R.id.buttonFragment1); fragment2Button = (Button) findViewById(R.id.buttonFragment2); // get an instance of FragmentTransaction from your Activity FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); //add a fragment FirstFragment firstFragment = new FirstFragment(); fragmentTransaction.add(R.id.container, firstFragment); fragmentTransaction.commit(); fragment1Button.setOnClickListener(onButtonClickListener); fragment2Button.setOnClickListener(onButtonClickListener); >Button.OnClickListener onButtonClickListener = new Button.OnClickListener() < @Override public void onClick(View v) < // TODO Auto-generated method stub Fragment newFragment = null; // Create new fragment if (v == fragment1Button) < newFragment = new FirstFragment(); >else < newFragment = new SecondFragment(); >// Create new transaction FragmentTransaction transaction = getFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack transaction.replace(R.id.container, newFragment); transaction.addToBackStack(null); // Commit the transaction transaction.commit(); > >; > 

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

onAttach
onCreate
onCreateView
onActivityCreated
onStart
onResume

Метод onActivityCreated() теперь считается устаревшим.

Нажимаем на кнопку «Home»:

Запустим из списка недавно запущенных программ:

Выходим из программы через кнопку «Back»:

onPause
onStop
onDestroyView
onDestroy
onDetach

При замещении первого фрагмента вторым почти как при закрытии, только нет методов onDestroy и onDetach, а затем повторяются те же методы при старте фрагмента:

// Первый фрагмент onPause
onStop
onDestroyView
// Второй фрагмент onAttach
onCreate
onCreateView
onActivityCreated
onStart
onResume

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

xxv/android-lifecycle — схема жизненного цикла в SVG, PDF, PNG.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *