1. Добре дошли
Тази практическа кодова лаборатория е част от Раздел 1: Първи стъпки в курса Основи на разработчиците на Android (Версия 2). Ще получите най-голяма полза от този курс, ако работите през кодовите лаборатории последователно:
- За пълния списък с кодови лаборатории в курса вижте Codelabs за Android Developer Fundamentals (V2).
- За подробности относно курса, включително връзки към всички концептуални глави, приложения и слайдове, вижте Android Developer Fundamentals (Версия 2).
Въведение
В тази практика научавате повече за жизнения цикъл на дейността. Жизненият цикъл е набор от състояния, в които една дейност може да бъде през целия си живот, от момента, в който е създадена, до момента, в който е унищожена и системата си възстановява ресурсите. Докато потребителят навигира между дейностите във вашето приложение (както и във и извън вашето приложение), дейностите преминават между различни състояния в техните жизнени цикли.
Всеки етап от жизнения цикъл на дадена дейност има съответен метод за обратно извикване: onCreate(), onStart(), onPause() и т.н. Когато дадена дейност промени състоянието, се извиква свързаният метод за обратно извикване. Вече сте виждали един от тези методи: onCreate(). Като замените някой от методите за обратно извикване на жизнения цикъл във вашите класове на дейност, можете да промените поведението по подразбиране на дейността в отговор на потребителски или системни действия.
Състоянието на активност може също да се промени в отговор на промени в конфигурацията на устройството, например когато потребителят завърти устройството от портретна към пейзажна. Когато се случат тези промени в конфигурацията, дейността се унищожава и се създава отново в състоянието й по подразбиране и потребителят може да загуби информацията, която е въвел в дейността. За да избегнете объркване на потребителите си, важно е да разработите приложението си, за да предотвратите неочаквана загуба на данни. По-късно в тази практика експериментирате с промени в конфигурацията и научавате как да запазите състоянието на дейност в отговор на промени в конфигурацията на устройството и други събития от жизнения цикъл на дейността.
В тази практика вие добавяте извлечения за регистриране към приложението TwoActivities и наблюдавате промените в жизнения цикъл на дейността, докато използвате приложението. След това започвате да работите с тези промени и да проучвате как да обработвате въвеждането на потребители при тези условия.
Предпоставки
Трябва да можете да:
- Създайте и стартирайте проект за приложение в Android Studio .
- Добавете отчети за регистрационни файлове към вашето приложение и прегледайте тези регистрационни файлове в панела Logcat.
- Разбирайте и работете с дейност и намерение и се чувствайте удобно да взаимодействате с тях.
Какво ще научите
- Как работи жизненият цикъл на активността.
- Когато дадено действие започне, спира, спира и се унищожава.
- Относно методите за обратно извикване на жизнения цикъл, свързани с промените в дейността.
- Ефектът от действия (като промени в конфигурацията), които могат да доведат до събития от жизнения цикъл на дейността.
- Как да запазите състоянието на активност през събития от жизнения цикъл.
Какво ще направиш
- Добавете код към приложението TwoActivities от предишната практическа работа, за да приложите различните обратни извиквания на жизнения цикъл на дейността, за да включите отчети за регистриране.
- Наблюдавайте промените в състоянието, докато приложението ви работи и докато взаимодействате с всяка дейност в приложението ви.
- Променете приложението си, за да запазите състоянието на екземпляра на дейност, която е неочаквано пресъздадена в отговор на поведението на потребителя или промяната на конфигурацията на устройството.
2. Преглед на приложението
В тази практика добавяте към приложението TwoActivities . Приложението изглежда и се държи приблизително по същия начин, както в последната кодова лаборатория. Той съдържа две реализации на дейност и дава на потребителя възможността да изпраща между тях. Промените, които правите в приложението в тази практика, няма да повлияят на видимото му потребителско поведение.
3. 3. Задача 1: Добавете обратни извиквания на жизнения цикъл към TwoActivities
В тази задача ще приложите всички методи за обратно извикване на жизнения цикъл на дейността, за да отпечатвате съобщения в logcat, когато тези методи се извикват. Тези регистрационни съобщения ще ви позволят да видите кога жизненият цикъл на дейността променя състоянието и как тези промени в състоянието на жизнения цикъл засягат вашето приложение, докато работи.
1.1 (По избор) Копирайте проекта TwoActivities
За задачите в това упражнение ще модифицирате съществуващия проект TwoActivities , който сте създали в последното упражнение. Ако предпочитате да запазите предишния проект TwoActivities непокътнат, следвайте стъпките в Приложение: Помощни програми, за да направите копие на проекта.
1.2 Внедрете обратни извиквания в MainActivity
- Отворете проекта TwoActivities в Android Studio и отворете MainActivity в панела Project > Android.
- В метода onCreate() добавете следните отчети за журнал:
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
- Добавете отмяна за обратното извикване onStart() с изявление към дневника за това събитие:
@Override
public void onStart(){
super.onStart();
Log.d(LOG_TAG, "onStart");
}
За пряк път изберете Code > Override Methods в Android Studio. Появява се диалогов прозорец с всички възможни методи, които можете да замените във вашия клас. Избирането на един или повече методи за обратно извикване от списъка вмъква пълен шаблон за тези методи, включително необходимото извикване на суперкласа.
- Използвайте метода onStart() като шаблон за внедряване на обратните извиквания на жизнения цикъл onPause(), onRestart(), onResume(), onStop() и onDestroy().
Всички методи за обратно извикване имат еднакви подписи (с изключение на името). Ако копирате и поставите onStart(), за да създадете тези други методи за обратно извикване, не забравяйте да актуализирате съдържанието, за да извикате правилния метод в суперкласа и да регистрирате правилния метод.
- Стартирайте приложението си.
- Щракнете върху раздела Logcat в долната част на Android Studio, за да покажете панела Logcat. Трябва да видите три регистрационни съобщения, показващи трите състояния на жизнения цикъл, през които дейността е преминала, когато е започнала:
D/MainActivity: -------
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onResume
1.3 Внедрете обратни извиквания на жизнения цикъл във SecondActivity
Сега, след като сте внедрили методите за обратно извикване на жизнения цикъл за MainActivity, направете същото за SecondActivity.
- Отворете SecondActivity.
- В горната част на класа добавете константа за променливата LOG_TAG:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
- Добавете обратните извиквания на жизнения цикъл и отчетите за регистрационни файлове към втората дейност. (Можете да копирате и поставите методите за обратно извикване от MainActivity.)
- Добавете лог оператор към метода returnReply() точно преди метода finish():
Log.d(LOG_TAG, "End SecondActivity");
1.4 Наблюдавайте дневника, докато приложението работи**
- Стартирайте приложението си.
- Щракнете върху раздела Logcat в долната част на Android Studio, за да покажете панела Logcat.
- Въведете Activity в полето за търсене. Android logcat може да бъде много дълъг и претрупан. Тъй като променливата LOG_TAG във всеки клас съдържа или думите MainActivity, или SecondActivity, тази ключова дума ви позволява да филтрирате дневника само за нещата, които ви интересуват.
Експериментирайте с приложението си и имайте предвид, че събитията от жизнения цикъл, които възникват в отговор на различни действия. По-специално опитайте тези неща:
- Използвайте приложението нормално (изпратете съобщение, отговорете с друго съобщение).
- Използвайте бутона Назад, за да се върнете от втората дейност към основната дейност.
- Използвайте стрелката нагоре в лентата на приложението, за да се върнете от втората дейност към основната дейност.
- Завъртете устройството както на основната, така и на втората дейност по различно време в приложението си и наблюдавайте какво се случва в * дневника и на екрана.
- Натиснете бутона за общ преглед (квадратния бутон вдясно от Начало) и затворете приложението (докоснете X).
- Върнете се на началния екран и рестартирайте приложението си.
СЪВЕТ: Ако изпълнявате приложението си в емулатор, можете да симулирате ротация с Control+F11 или Control+Function+F11.
Код на решението на задача 1
Следните кодови фрагменти показват кода на решението за първата задача.
Основна дейност
Следните кодови фрагменти показват добавения код в MainActivity, но не и целия клас.
Методът onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Log the start of the onCreate() method.
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
}
Другите методи на жизнения цикъл:
@Override
protected void onStart() {
super.onStart();
Log.d(LOG_TAG, "onStart");
}
@Override
protected void onPause() {
super.onPause();
Log.d(LOG_TAG, "onPause");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(LOG_TAG, "onRestart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(LOG_TAG, "onResume");
}
@Override
protected void onStop() {
super.onStop();
Log.d(LOG_TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "onDestroy");
}
SecondActivity
Следните кодови фрагменти показват добавения код във SecondActivity, но не и целия клас.
В горната част на класа SecondActivity:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
Методът returnReply():
public void returnReply(View view) {
String reply = mReply.getText().toString();
Intent replyIntent = new Intent();
replyIntent.putExtra(EXTRA_REPLY, reply);
setResult(RESULT_OK, replyIntent);
Log.d(LOG_TAG, "End SecondActivity");
finish();
}
Другите методи на жизнения цикъл:
Същото като за MainActivity по-горе.
4. 4. Задача 2: Запазете и възстановете състоянието на екземпляр на Activity
В зависимост от системните ресурси и поведението на потребителя, всяка дейност във вашето приложение може да бъде унищожена и реконструирана много по-често, отколкото си мислите.
Може да сте забелязали това поведение в последния раздел, когато сте завъртели устройството или емулатора. Завъртането на устройството е един пример за промяна на конфигурацията на устройството. Въпреки че ротацията е най-често срещаната, всички промени в конфигурацията водят до унищожаване и пресъздаване на текущата дейност, сякаш е нова. Ако не отчитате това поведение в кода си, когато настъпи промяна в конфигурацията, оформлението на вашата дейност може да се върне към външния си вид по подразбиране и първоначалните си стойности и вашите потребители може да загубят мястото си, данните си или състоянието на напредъка си в вашето приложение.
Състоянието на всяка дейност се съхранява като набор от двойки ключ/стойност в обект Bundle, наречен състояние на екземпляр на дейност. Системата записва информация за състоянието по подразбиране в пакета за състояние на екземпляр точно преди дейността да бъде спряна и предава този пакет на новия екземпляр на дейност за възстановяване.
За да не загубите данни в дейност, когато тя бъде неочаквано унищожена и пресъздадена, трябва да внедрите метода onSaveInstanceState(). Системата извиква този метод на вашата дейност (между onPause() и onStop()), когато има възможност активността да бъде унищожена и пресъздадена.
Данните, които запазвате в състоянието на екземпляра, са специфични само за този екземпляр на тази конкретна дейност по време на текущата сесия на приложението. Когато спрете и рестартирате нова сесия на приложение, състоянието на екземпляра на дейността се губи и активността се връща към външния си вид по подразбиране. Ако трябва да запазите потребителски данни между сесиите на приложението, използвайте споделени предпочитания или база данни. Ще научите и за двете в по-късен практически курс.
2.1 Запазете състоянието на екземпляра на Activity с onSaveInstanceState()
Може би сте забелязали, че завъртането на устройството изобщо не влияе на състоянието на втората активност. Това е така, защото второто оформление и състояние на дейност се генерират от оформлението и намерението, което го е активирало. Дори ако дейността е пресъздадена, намерението все още е там и данните в това намерение все още се използват всеки път, когато се извика методът onCreate() във втората дейност.
Освен това може да забележите, че във всяка дейност всеки текст, който сте въвели в елементите на съобщението или отговора EditText, се запазва дори когато устройството се завърти. Това е така, защото информацията за състоянието на някои от елементите на View във вашето оформление се запазва автоматично при промени в конфигурацията и текущата стойност на EditText е един от тези случаи.
Така че единственото състояние на дейност, което ви интересува, са елементите на TextView за заглавката на отговора и текста на отговора в основната дейност. И двата елемента TextView са невидими по подразбиране; те се появяват само след като изпратите съобщение обратно към основната дейност от втората дейност.
В тази задача вие добавяте код за запазване на състоянието на екземпляра на тези два TextView елемента с помощта на onSaveInstanceState().
- Отворете MainActivity.
- Добавете тази скелетна реализация на onSaveInstanceState() към дейността или използвайте Code > Override Methods, за да вмъкнете skeleton override.
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
- Проверете дали заглавката е видима в момента и ако е така, поставете това състояние на видимост в пакета на състоянието с метода putBoolean() и ключа „reply_visible“.
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
}
Не забравяйте, че заглавката и текстът на отговора са маркирани като невидими, докато няма отговор от второто действие. Ако заглавката е видима, тогава има данни за отговор, които трябва да бъдат запазени. Обърнете внимание, че се интересуваме само от това състояние на видимост — действителният текст на заглавката не трябва да се запазва, защото този текст никога не се променя.
- В рамките на същата проверка добавете текста на отговора в пакета.
outState.putString("reply_text",mReplyTextView.getText().toString());
Ако заглавката е видима, можете да предположите, че самото съобщение за отговор също е видимо. Не е необходимо да тествате или да запазвате текущото състояние на видимост на съобщението за отговор. Само действителният текст на съобщението отива в състоянието Bundle с ключа "reply_text".
Вие запазвате състоянието само на онези елементи на View, които може да се променят след създаването на Activity. Другите елементи на View във вашето приложение (EditText, бутонът) могат да бъдат пресъздадени от оформлението по подразбиране по всяко време.
Имайте предвид, че системата ще запази състоянието на някои елементи на View, като съдържанието на EditText.
2.2 Възстановете състоянието на екземпляра на Activity в onCreate()
След като сте запазили състоянието на екземпляра на дейността, трябва също да го възстановите, когато дейността бъде създадена отново. Можете да направите това или в onCreate(), или като имплементирате обратното извикване onRestoreInstanceState(), което се извиква след onStart() след създаването на Activity.
През повечето време по-доброто място за възстановяване на състоянието на активност е в onCreate(), за да се гарантира, че потребителският интерфейс, включително състоянието, е достъпен възможно най-скоро. Понякога е удобно да го направите в onRestoreInstanceState(), след като цялата инициализация е направена, или да позволите на подкласовете да решат дали да използват вашата реализация по подразбиране.
- В метода onCreate(), след като променливите View се инициализират с findViewById(), добавете тест, за да се уверите, че savedInstanceState не е null.
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the state.
if (savedInstanceState != null) {
}
Когато вашата дейност е създадена, системата предава състоянието Bundle на onCreate() като единствен аргумент. Първият път, когато onCreate() се извика и вашето приложение стартира, Bundle е null — няма съществуващо състояние при първото стартиране на вашето приложение. Последващите извиквания на onCreate() имат пакет, попълнен с данните, които сте съхранили в onSaveInstanceState().
- Вътре в тази проверка вземете текущата видимост (истина или невярно) от пакета с ключа "reply_visible".
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
}
- Добавете тест под предишния ред за променливата isVisible.
if (isVisible) {
}
Ако има ключ reply_visible в пакета състояние (и следователно isVisible е true), ще трябва да възстановите състоянието.
- Вътре в теста isVisible направете заглавката видима.
mReplyHeadTextView.setVisibility(View.VISIBLE);
- Вземете съобщението с текстов отговор от пакета с ключа "reply_text" и задайте TextView на отговора да показва този низ.
mReplyTextView.setText(savedInstanceState.getString("reply_text"));
- Направете и отговора TextView видим:
mReplyTextView.setVisibility(View.VISIBLE);
- Стартирайте приложението. Опитайте да завъртите устройството или емулатора, за да се уверите, че съобщението за отговор (ако има такова) остава на екрана след повторното създаване на дейността.
Код на решението на задача 2
Следните кодови фрагменти показват кода на решението за тази задача.
Основна дейност
Следните кодови фрагменти показват добавения код в MainActivity, но не и целия клас.
Методът onSaveInstanceState():
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// If the heading is visible, message needs to be saved.
// Otherwise we're still using default layout.
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
outState.putString("reply_text",
mReplyTextView.getText().toString());
}
}
Методът onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the saved state.
// See onSaveInstanceState() for what gets saved.
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
// Show both the header and the message views. If isVisible is
// false or missing from the bundle, use the default layout.
if (isVisible) {
mReplyHeadTextView.setVisibility(View.VISIBLE);
mReplyTextView.setText(savedInstanceState
.getString("reply_text"));
mReplyTextView.setVisibility(View.VISIBLE);
}
}
}
Пълният проект:
Проект на Android Studio: TwoActivitiesLifecycle
5. Кодиране
Предизвикателство: Създайте просто приложение за списък за пазаруване с основна дейност за списъка, който потребителят създава, и втора дейност за списък с обичайни артикули за пазаруване.
- Основната дейност трябва да съдържа списъка за изграждане, който трябва да бъде съставен от десет празни TextView елемента.
- Бутон за добавяне на артикул в основната дейност стартира втора дейност, която съдържа списък с често срещани артикули за пазаруване (сирене, ориз, ябълки и т.н.). Използвайте бутонни елементи за показване на елементите.
- Избирането на елемент връща потребителя към основната дейност и актуализира празен TextView, за да включи избрания елемент.
Използвайте намерение за предаване на информация от една дейност към друга. Уверете се, че текущото състояние на списъка за пазаруване се запазва, когато потребителят завърти устройството.
6. Обобщение
- Жизненият цикъл на дейността е набор от състояния, през които една дейност мигрира, започвайки, когато е създадена за първи път, и завършва, когато системата Android си върне ресурсите за тази дейност.
- Докато потребителят навигира от една дейност към друга и във и извън вашето приложение, всяка дейност се движи между състояния в жизнения цикъл на дейността.
- Всяко състояние в жизнения цикъл на Activity има съответен метод за обратно извикване, който можете да замените във вашия клас Activity.
- Методите на жизнения цикъл са onCreate(), onStart(), onPause(), onRestart(), onResume(), onStop(), onDestroy().
- Замяната на метод за обратно извикване на жизнения цикъл ви позволява да добавите поведение, което се случва, когато вашата активност премине в това състояние.
- Можете да добавите скелетни методи за замяна към вашите класове в Android Studio с Code > Override.
- Промените в конфигурацията на устройството, като ротация, водят до унищожаване на дейността и пресъздаване, сякаш е нова.
- Част от състоянието на активността се запазва при промяна на конфигурацията, включително текущите стойности на елементите EditText. За всички други данни трябва изрично да запазите тези данни сами.
- Запазване на състоянието на екземпляр на дейност в метода onSaveInstanceState().
- Данните за състоянието на екземпляра се съхраняват като прости двойки ключ/стойност в пакет. Използвайте методите на Bundle, за да поставите данни в и да получите обратно данни от Bundle.
- Възстановете състоянието на екземпляра в onCreate(), което е предпочитаният начин, или onRestoreInstanceState(). Назад