Hur man använder aktivitetens livscykel och tillstånd, Hur man använder aktivitetens livscykel och tillstånd

1. Välkommen

Detta praktiska codelab är en del av Unit 1: Kom igång med Android Developer Fundamentals (Version 2)-kursen. Du kommer att få ut det mesta av den här kursen om du arbetar genom kodlabben i följd:

  • För den fullständiga listan över codelabs i kursen, se Codelabs for Android Developer Fundamentals (V2).
  • För detaljer om kursen, inklusive länkar till alla konceptkapitel, appar och bilder, se Android Developer Fundamentals (version 2).

Introduktion

I den här praktiken lär du dig mer om aktivitetens livscykel. Livscykeln är den uppsättning tillstånd en aktivitet kan vara i under hela sin livstid, från när den skapas till att den förstörs och systemet återtar sina resurser. När en användare navigerar mellan aktiviteter i din app (liksom in i och ut ur din app), övergår aktiviteter mellan olika tillstånd i deras livscykler.

Dubbelt problem

Varje steg i en aktivitets livscykel har en motsvarande callback-metod: onCreate(), onStart(), onPause() och så vidare. När en aktivitet ändrar tillstånd anropas den associerade återuppringningsmetoden. Du har redan sett en av dessa metoder: onCreate(). Genom att åsidosätta någon av livscykelåteruppringningsmetoderna i dina aktivitetsklasser kan du ändra aktivitetens standardbeteende som svar på användar- eller systemåtgärder.

Aktivitetstillståndet kan också ändras som svar på enhetskonfigurationsändringar, till exempel när användaren roterar enheten från stående till liggande. När dessa konfigurationsändringar sker förstörs aktiviteten och återskapas i dess standardläge, och användaren kan förlora information som de har angett i aktiviteten. För att undvika att förvirra dina användare är det viktigt att du utvecklar din app för att förhindra oväntad dataförlust. Senare i den här praktiken experimenterar du med konfigurationsändringar och lär dig hur du bevarar en aktivitets tillstånd som svar på enhetskonfigurationsändringar och andra aktivitetslivscykelhändelser.

I den här praktiska övningen lägger du till loggningssatser i TwoActivities-appen och observerar förändringar i aktivitetslivscykeln när du använder appen. Du börjar sedan arbeta med dessa förändringar och utforskar hur du hanterar användarinmatning under dessa förhållanden.

Förutsättningar

Du bör kunna:

  • Skapa och kör ett appprojekt i Android Studio .
  • Lägg till loggsatser i din app och visa loggarna i Logcat-rutan.
  • Förstå och arbeta med en aktivitet och en avsikt och vara bekväm att interagera med dem.

Vad du kommer att lära dig

  • Hur aktivitetens livscykel fungerar.
  • När en aktivitet startar, pausar, stoppar och förstörs.
  • Om återuppringningsmetoderna i livscykeln som är kopplade till aktivitetsändringar.
  • Effekten av åtgärder (som konfigurationsändringar) som kan resultera i aktivitetslivscykelhändelser.
  • Hur man behåller aktivitetstillstånd över livscykelhändelser.

Vad du ska göra

  • Lägg till kod till TwoActivities -appen från föregående praktiska för att implementera de olika aktivitetslivscykelåterkallelserna för att inkludera loggningssatser.
  • Observera tillståndsändringarna när din app körs och när du interagerar med varje aktivitet i din app.
  • Ändra din app för att behålla instanstillståndet för en aktivitet som oväntat återskapas som svar på användarbeteende eller konfigurationsändringar på enheten.

2. Appöversikt

I detta praktiska lägger du till TwoActivities -appen. Appen ser ut och beter sig ungefär som den gjorde i förra codelab. Den innehåller två Activity-implementeringar och ger användaren möjlighet att skicka mellan dem. Ändringarna du gör i appen i detta praktiska kommer inte att påverka dess synliga användarbeteende.

3. 3. Uppgift 1: Lägg till livscykeluppringningar till TwoActivities

I den här uppgiften kommer du att implementera alla återuppringningsmetoder för aktivitetens livscykel för att skriva ut meddelanden till logcat när dessa metoder anropas. Dessa loggmeddelanden låter dig se när aktivitetens livscykel ändrar tillstånd och hur dessa livscykeltillståndsändringar påverkar din app när den körs.

1.1 (Valfritt) Kopiera TwoActivities-projektet

För uppgifterna i den här praktiken kommer du att modifiera det befintliga TwoActivities -projektet som du byggde i den senaste praktiken. Om du föredrar att behålla det tidigare TwoActivities-projektet intakt, följ stegen i Bilaga: Verktyg för att göra en kopia av projektet.

1.2 Implementera callbacks i MainActivity

  1. Öppna TwoActivities-projektet i Android Studio och öppna MainActivity i rutan Projekt > Android.
  2. I metoden onCreate() lägger du till följande loggsatser:
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
  1. Lägg till en åsidosättning för onStart()-återuppringningen, med en sats i loggen för den händelsen:
@Override
public void onStart(){
    super.onStart();
    Log.d(LOG_TAG, "onStart");
}

För en genväg, välj Kod > Åsidosätt metoder i Android Studio. En dialogruta visas med alla möjliga metoder som du kan åsidosätta i din klass. Genom att välja en eller flera återuppringningsmetoder från listan infogas en komplett mall för dessa metoder, inklusive det nödvändiga anropet till superklassen.

  1. Använd metoden onStart() som en mall för att implementera livscykelanropen onPause(), onRestart(), onResume(), onStop() och onDestroy().

Alla återuppringningsmetoder har samma signaturer (förutom namnet). Om du kopierar och klistra in onStart() för att skapa dessa andra callback-metoder, glöm inte att uppdatera innehållet för att anropa rätt metod i superklassen och logga rätt metod.

  1. Kör din app.
  2. Klicka på fliken Logcat längst ned i Android Studio för att visa Logcat-rutan. Du bör se tre loggmeddelanden som visar de tre livscykeltillstånden som aktiviteten har gått igenom när den startade:
D/MainActivity: -------
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onResume

1.3 Implementera livscykelåteruppringningar i SecondActivity

Nu när du har implementerat livscykelåteruppringningsmetoderna för MainActivity gör du samma sak för SecondActivity.

  1. Öppna SecondActivity.
  2. Överst i klassen lägger du till en konstant för variabeln LOG_TAG:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
  1. Lägg till livscykeluppringningar och loggsatser till den andra aktiviteten. (Du kan kopiera och klistra in återuppringningsmetoderna från MainActivity.)
  2. Lägg till en loggsats till metoden returnReply() precis före finish()-metoden:
Log.d(LOG_TAG, "End SecondActivity");

1.4 Observera loggen när appen körs**

  1. Kör din app.
  2. Klicka på fliken Logcat längst ned i Android Studio för att visa Logcat-rutan.
  3. Ange aktivitet i sökrutan. Android logcat kan vara väldigt lång och rörig. Eftersom variabeln LOG_TAG i varje klass innehåller antingen orden MainActivity eller SecondActivity, låter det här nyckelordet dig filtrera loggen efter bara de saker du är intresserad av.

Dubbelt problem

Experimentera med din app och notera att livscykelhändelserna som inträffar som svar på olika åtgärder. Prova särskilt dessa saker:

  • Använd appen normalt (skicka ett meddelande, svara med ett annat meddelande).
  • Använd tillbakaknappen för att gå tillbaka från den andra aktiviteten till huvudaktiviteten.
  • Använd uppåtpilen i appfältet för att gå tillbaka från den andra aktiviteten till huvudaktiviteten.
  • Rotera enheten på både huvud- och andraaktiviteten vid olika tidpunkter i din app och observera vad som händer i * loggen och på skärmen.
  • Tryck på översiktsknappen (den fyrkantiga knappen till höger om Hem) och stäng appen (tryck på X).
  • Återgå till startskärmen och starta om appen.

TIPS: Om du kör din app i en emulator kan du simulera rotation med Control+F11 eller Control+Function+F11.

Lösningskod för uppgift 1

Följande kodsnuttar visar lösningskoden för den första uppgiften.

MainActivity

Följande kodavsnitt visar den tillagda koden i MainActivity, men inte hela klassen.

Metoden 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);
}

De andra livscykelmetoderna:

@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

Följande kodavsnitt visar den tillagda koden i SecondActivity, men inte hela klassen.

Överst i SecondActivity-klassen:

private static final String LOG_TAG = SecondActivity.class.getSimpleName();

ReturnReply()-metoden:

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();
}

De andra livscykelmetoderna:

Samma som för MainActivity, ovan.

4. 4. Uppgift 2: Spara och återställ tillståndet för aktivitetsinstansen

Beroende på systemresurser och användarbeteende kan varje aktivitet i din app förstöras och rekonstrueras mycket oftare än du kanske tror.

Du kanske har märkt detta beteende i det sista avsnittet när du roterade enheten eller emulatorn. Att rotera enheten är ett exempel på en enhetskonfigurationsändring. Även om rotation är den vanligaste, leder alla konfigurationsändringar till att den aktuella aktiviteten förstörs och återskapas som om den vore ny. Om du inte tar hänsyn till detta beteende i din kod, när en konfigurationsändring inträffar, kan din aktivitetslayout återgå till dess standardutseende och initiala värden, och dina användare kan förlora sin plats, sina data eller status för deras framsteg i din app.

Tillståndet för varje aktivitet lagras som en uppsättning nyckel/värdepar i ett Bundle-objekt som kallas aktivitetsinstanstillståndet. Systemet sparar standardtillståndsinformation till instanstillståndspaketet precis innan aktiviteten stoppas, och skickar det paketet till den nya aktivitetsinstansen för att återställa.

För att undvika att förlora data i en aktivitet när den oväntat förstörs och återskapas måste du implementera onSaveInstanceState()-metoden. Systemet anropar denna metod på din aktivitet (mellan onPause() och onStop()) när det finns en möjlighet att aktiviteten kan förstöras och återskapas.

Datan du sparar i instanstillståndet är specifik för endast denna instans av denna specifika aktivitet under den aktuella appsessionen. När du stoppar och startar om en ny appsession förloras aktivitetsinstansens tillstånd och aktiviteten återgår till sitt standardutseende. Om du behöver spara användardata mellan appsessioner, använd delade inställningar eller en databas. Du lär dig om båda dessa i en senare praktik.

2.1 Spara aktivitetsinstanstillståndet med onSaveInstanceState()

Du kanske har märkt att rotation av enheten inte alls påverkar tillståndet för den andra aktiviteten. Detta beror på att den andra aktivitetslayouten och tillståndet genereras från layouten och avsikten som aktiverade den. Även om aktiviteten återskapas finns avsikten kvar och data i den avsikten används fortfarande varje gång metoden onCreate() i den andra aktiviteten anropas.

Dessutom kan du märka att i varje aktivitet behålls all text du skrivit i meddelandet eller svars-EditText-element även när enheten roteras. Detta beror på att statusinformationen för vissa av View-elementen i din layout sparas automatiskt över konfigurationsändringar, och det aktuella värdet för en EditText är ett av dessa fall.

Så det enda aktivitetstillståndet du är intresserad av är TextView-elementen för svarshuvudet och svarstexten i huvudaktiviteten. Båda TextView-elementen är osynliga som standard; de visas bara när du skickar ett meddelande tillbaka till huvudaktiviteten från den andra aktiviteten.

I den här uppgiften lägger du till kod för att bevara instanstillståndet för dessa två TextView-element med hjälp av onSaveInstanceState().

  1. Öppna MainActivity.
  2. Lägg till denna skelettimplementering av onSaveInstanceState() till aktiviteten, eller använd Kod > Åsidosätt metoder för att infoga en åsidosättning av skelett.
@Override
public void onSaveInstanceState(Bundle outState) {
          super.onSaveInstanceState(outState);
}
  1. Kontrollera om rubriken för närvarande är synlig, och i så fall lägg in det synlighetstillståndet i tillståndspaketet med putBoolean()-metoden och nyckeln "reply_visible".
 if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
        outState.putBoolean("reply_visible", true);
    }

Kom ihåg att svarshuvudet och texten är markerade som osynliga tills det kommer ett svar från den andra aktiviteten. Om rubriken är synlig finns det svarsdata som måste sparas. Observera att vi bara är intresserade av det synlighetstillståndet - själva texten i rubriken behöver inte sparas, eftersom den texten aldrig ändras.

  1. Inuti samma bock lägger du till svarstexten i paketet.
outState.putString("reply_text",mReplyTextView.getText().toString());

Om rubriken är synlig kan du anta att själva svarsmeddelandet också är synligt. Du behöver inte testa eller spara det aktuella synlighetstillståndet för svarsmeddelandet. Endast själva texten i meddelandet går in i tillståndspaketet med nyckeln "reply_text".

Du sparar endast tillståndet för de View-element som kan ändras efter att aktiviteten har skapats. De andra View-elementen i din app (EditText, knappen) kan återskapas från standardlayouten när som helst.

Observera att systemet kommer att spara tillståndet för vissa View-element, såsom innehållet i EditText.

2.2 Återställ tillståndet Activity-instans i onCreate()

När du har sparat tillståndet för aktivitetsinstansen måste du också återställa det när aktiviteten återskapas. Du kan göra detta antingen i onCreate() eller genom att implementera onRestoreInstanceState() callback, som anropas efter onStart() efter att aktiviteten har skapats.

För det mesta är det bättre stället att återställa aktivitetstillståndet i onCreate(), för att säkerställa att användargränssnittet, inklusive tillståndet, är tillgängligt så snart som möjligt. Det är ibland bekvämt att göra det i onRestoreInstanceState() efter att all initiering har gjorts, eller att tillåta underklasser att bestämma om de ska använda din standardimplementering.

  1. I onCreate()-metoden, efter att View-variablerna har initierats med findViewById(), lägg till ett test för att säkerställa att savedInstanceState inte är 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) {
}

När din aktivitet har skapats skickar systemet tillståndspaketet till onCreate() som dess enda argument. Första gången onCreate() anropas och din app startar, är paketet null – det finns inget befintligt tillstånd första gången din app startar. Efterföljande anrop till onCreate() har en bunt fylld med data som du lagrat i onSaveInstanceState().

  1. Inuti den kontrollen, ta ut den aktuella synligheten (sant eller falsk) ur paketet med nyckeln "reply_visible".
if (savedInstanceState != null) {
    boolean isVisible = 
                     savedInstanceState.getBoolean("reply_visible");
}
  1. Lägg till ett test under den föregående raden för variabeln isVisible.
if (isVisible) {
}

Om det finns en reply_visible-nyckel i tillståndspaketet (och isVisible är därför sant), måste du återställa tillståndet.

  1. Inuti isVisible-testet gör du rubriken synlig.
mReplyHeadTextView.setVisibility(View.VISIBLE);
  1. Hämta textsvarsmeddelandet från paketet med nyckeln "reply_text", och ställ in reply TextView för att visa den strängen.
mReplyTextView.setText(savedInstanceState.getString("reply_text"));
  1. Gör även svaret TextView synligt:
mReplyTextView.setVisibility(View.VISIBLE);
  1. Kör appen. Prova att rotera enheten eller emulatorn för att säkerställa att svarsmeddelandet (om det finns ett) finns kvar på skärmen efter att aktiviteten har återskapats.

Lösningskod för uppgift 2

Följande kodavsnitt visar lösningskoden för denna uppgift.

MainActivity

Följande kodavsnitt visar den tillagda koden i MainActivity, men inte hela klassen.

Metoden 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());
   }
}

Metoden 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);
       }
   }
}

Hela projektet:

Android Studio Project: TwoActivitiesLifecycle

5. Kodning

Utmaning: Skapa en enkel inköpslista-app med en huvudaktivitet för listan som användaren bygger och en andra aktivitet för en lista med vanliga shoppingartiklar.

  • Huvudaktiviteten bör innehålla listan som ska byggas, som ska bestå av tio tomma TextView-element.
  • En Lägg till objekt-knapp på huvudaktiviteten startar en andra aktivitet som innehåller en lista över vanliga shoppingartiklar (ost, ris, äpplen och så vidare). Använd knappelement för att visa objekten.
  • Genom att välja ett objekt återgår användaren till huvudaktiviteten och uppdaterar en tom TextView för att inkludera det valda objektet.

Använd en avsikt för att överföra information från en aktivitet till en annan. Se till att det aktuella läget för inköpslistan sparas när användaren roterar enheten.

6. Sammanfattning

  • Aktivitetslivscykeln är en uppsättning tillstånd som en aktivitet migrerar genom, som börjar när den först skapas och slutar när Android-systemet återtar resurserna för den aktiviteten.
  • När användaren navigerar från en aktivitet till en annan, och i och utanför din app, flyttas varje aktivitet mellan tillstånden i aktivitetens livscykel.
  • Varje tillstånd i aktivitetens livscykel har en motsvarande återuppringningsmetod som du kan åsidosätta i din aktivitetsklass.
  • Livscykelmetoderna är onCreate(), onStart(), onPause(), onRestart(), onResume(), onStop(), onDestroy().
  • Genom att åsidosätta en livscykelåteruppringningsmetod kan du lägga till beteende som uppstår när din aktivitet övergår till det tillståndet.
  • Du kan lägga till skelettöverstyrningsmetoder till dina klasser i Android Studio med Kod > Åsidosätt.
  • Ändringar av enhetskonfiguration, såsom rotation, resulterar i att aktiviteten förstörs och återskapas som om den vore ny.
  • En del av aktivitetstillståndet bevaras vid en konfigurationsändring, inklusive de aktuella värdena för EditText-element. För all annan data måste du explicit spara den informationen själv.
  • Save Activity-instanstillstånd i metoden onSaveInstanceState().
  • Instanstillståndsdata lagras som enkla nyckel/värdepar i ett paket. Använd paketmetoderna för att lägga in data i och få tillbaka data ur paketet.
  • Återställ instanstillståndet i onCreate(), vilket är det föredragna sättet, eller onRestoreInstanceState(). Tillbaka