Not
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Aktiviteter är en grundläggande byggsten för Android-program och de kan finnas i ett antal olika tillstånd. Aktivitetslivscykeln börjar med instansiering och slutar med förstörelse och innehåller många tillstånd däremellan. När en aktivitet ändrar tillstånd anropas lämplig livscykelhändelsemetod, meddelar aktiviteten om den förestående tillståndsändringen och låter den köra kod för att anpassa sig till den ändringen. Den här artikeln undersöker livscykeln för aktiviteter och förklarar det ansvar en aktivitet har under var och en av dessa tillståndsförändringar för att vara en del av ett korrekt fungerande och tillförlitligt program.
Översikt över aktivitetslivscykel
Aktiviteter är ett ovanligt programmeringskoncept som är specifikt för Android. I traditionell programutveckling finns det vanligtvis en statisk huvudmetod som körs för att starta programmet. Med Android är dock saker annorlunda; Android-program kan startas via alla registrerade aktiviteter i ett program. I praktiken har de flesta program bara en specifik aktivitet som anges som startpunkt för programmet. Men om ett program kraschar eller avslutas av operativsystemet kan operativsystemet försöka starta om programmet vid den senaste öppna aktiviteten eller någon annanstans i den tidigare aktivitetsstacken. Dessutom kan operativsystemet pausa aktiviteter när de inte är aktiva och frigöra dem om det har ont om minne. Noggrant övervägande måste göras för att programmet ska kunna återställa sitt tillstånd korrekt om en aktivitet startas om, särskilt om den aktiviteten är beroende av data från tidigare aktiviteter.
Aktivitetslivscykeln implementeras som en samling metoder som operativsystemet anropar under en aktivitets livscykel. Med de här metoderna kan utvecklare implementera de funktioner som krävs för att uppfylla kraven på tillstånds- och resurshantering i sina program.
Det är mycket viktigt för programutvecklaren att analysera kraven för varje aktivitet för att avgöra vilka metoder som exponeras av aktivitetslivscykeln måste implementeras. Om du inte gör det kan det leda till instabilitet i programmet, krascher, resurssvällning och eventuellt även underliggande os-instabilitet.
I det här kapitlet går vi igenom aktivitetslivscykeln i detalj, bland annat:
- Aktivitetstillstånd
- Livscykelmetoder
- Bibehålla tillståndet för en applikation
Det här avsnittet innehåller också en genomgång som innehåller praktiska exempel på hur du effektivt sparar tillstånd under aktivitetslivscykeln. I slutet av det här kapitlet bör du ha en förståelse för aktivitetslivscykeln och hur du stöder den i ett Android-program.
Aktivitetslivscykel
Android-aktivitetslivscykeln består av en samling metoder som exponeras i klassen Aktivitet som ger utvecklaren ett resurshanteringsramverk. Det här ramverket gör det möjligt för utvecklare att uppfylla de unika tillståndshanteringskraven för varje aktivitet i ett program och hantera resurshantering korrekt.
Aktivitetstillstånd
Android OS hanterar aktiviteter baserat på deras tillstånd. Detta hjälper Android att identifiera aktiviteter som inte längre används, vilket gör att operativsystemet kan frigöra minne och resurser. Följande diagram illustrerar de tillstånd som en aktivitet kan gå igenom under sin livslängd:
Dessa tillstånd kan delas in i 4 huvudgrupper på följande sätt:
Aktiv eller Körs – Aktiviteter betraktas som aktiva eller körs om de är i förgrunden, även kallat överst i aktivitetsstacken. Detta anses vara den högsta prioritetsaktiviteten i Android, och därför kommer endast att avlivas av operativsystemet i extrema situationer, till exempel om aktiviteten försöker använda mer minne än vad som är tillgängligt på enheten eftersom detta kan leda till att användargränssnittet slutar svara.
Pausad – När enheten går i viloläge, eller om en aktivitet fortfarande är synlig men delvis dold av en ny, icke-helskärms eller transparent aktivitet, betraktas aktiviteten som pausad. Pausade aktiviteter är fortfarande aktiva, det vill säga de behåller all information om tillstånd och medlemmar och förblir kopplade till fönsterhanteraren. Detta anses vara den näst högsta prioritetsaktiviteten i Android och kommer därför endast att avslutas av operativsystemet om avslutandet av den här aktiviteten uppfyller de resurskrav som krävs för att hålla den aktiva/körande aktiviteten stabil och lyhörd.
Stoppade/bakgrund – Aktiviteter som är helt dolda av en annan aktivitet anses vara stoppade eller i bakgrunden. Stoppade aktiviteter försöker fortfarande behålla sin medlemsstats- och medlemsinformation så länge som möjligt, men stoppade aktiviteter anses vara den lägsta prioriteten för de tre staterna och därför kommer operativsystemet att döda aktiviteter i det här tillståndet först för att uppfylla resurskraven för aktiviteter med högre prioritet.
Startas om – Det är möjligt att en aktivitet som är var som helst från pausad till stoppad i livscykeln tas bort från minnet av Android. Om användaren går tillbaka till aktiviteten måste den startas om, återställas till dess tidigare sparade tillstånd och sedan visas för användaren.
Aktivitet Re-Creation som svar på konfigurationsändringar
För att göra saken mer komplicerad ställer Android till det ytterligare med något som kallas Konfigurationsändringar. Konfigurationsändringar är snabba aktivitetsdestruktions-/återskapandecykler som inträffar när konfigurationen av en aktivitet ändras, till exempel när enheten roteras (och aktiviteten måste byggas om i liggande eller stående läge), när tangentbordet visas (och aktiviteten ges möjlighet att ändra storlek på sig själv), eller när enheten placeras i en docka, bland annat.
Konfigurationsändringar orsakar fortfarande samma aktivitetstillståndsändringar som skulle inträffa när en aktivitet stoppades och startades om. Men för att säkerställa att ett program känns responsivt och fungerar bra under konfigurationsändringar är det viktigt att de hanteras så snabbt som möjligt. Därför har Android ett specifikt API som kan användas för att bevara tillståndet under konfigurationsändringar. Vi går igenom detta senare i avsnittet Hantera tillstånd under hela livscykeln .
Livscykelmetoder för aktivitet
Android SDK och, i tillägg, Xamarin.Android-ramverket tillhandahåller en kraftfull modell för att hantera tillståndet för aktiviteter i ett program. När en aktivitets tillstånd ändras meddelas aktiviteten av operativsystemet, som anropar specifika metoder för den aktiviteten. Följande diagram illustrerar dessa metoder i förhållande till aktivitetslivscykeln:
Som utvecklare kan du hantera tillståndsändringar genom att åsidosätta dessa metoder i en aktivitet. Det är dock viktigt att notera att alla livscykelmetoder anropas i användargränssnittstråden och blockerar operativsystemet från att utföra nästa del av användargränssnittet, till exempel dölja den aktuella aktiviteten, visa en ny aktivitet osv. Därför bör koden i dessa metoder vara så kort som möjligt för att ett program ska kännas bra. Alla långvariga uppgifter ska köras i en bakgrundstråd.
Nu ska vi undersöka var och en av dessa livscykelmetoder och deras användning:
OnCreate
OnCreate är den första metoden som anropas när en aktivitet skapas.
OnCreate åsidosätts alltid för att utföra startinitieringar som kan krävas av en aktivitet, till exempel:
- Skapa vyer
- Initiera variabler
- Binda statiska data till listor
OnCreate tar en Bundle-parameter , som är en ordlista för att lagra och skicka tillståndsinformation och objekt mellan aktiviteter Om paketet inte är null, anger detta att aktiviteten startas om och att den ska återställa sitt tillstånd från föregående instans. Följande kod visar hur du hämtar värden från paketet:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
string intentString;
bool intentBool;
if (bundle != null)
{
intentString = bundle.GetString("myString");
intentBool = bundle.GetBoolean("myBool");
}
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
}
När OnCreate är klart, kommer Android att anropa OnStart.
OnStart
OnStart anropas alltid av systemet efter att OnCreate är färdigt. Aktiviteter kan åsidosätta den här metoden om de behöver utföra några specifika uppgifter direkt innan en aktivitet blir synlig, till exempel uppdatera aktuella värden för vyer i aktiviteten. Android anropar OnResume omedelbart efter den här metoden.
OnResume
Systemet anropar OnResume när aktiviteten är redo att börja interagera med användaren. Aktiviteter bör åsidosätta den här metoden för att utföra uppgifter som:
- Öka bildfrekvenserna (en vanlig uppgift inom spelutveckling)
- Starta animeringar
- Lyssna efter GPS-uppdateringar
- Visa relevanta aviseringar eller dialogrutor
- Koppla in externa händelsehanterare
Följande kodfragment visar till exempel hur du initierar kameran:
protected override void OnResume()
{
base.OnResume(); // Always call the superclass first.
if (_camera==null)
{
// Do camera initializations here
}
}
OnResume är viktigt eftersom alla åtgärder som utförs i OnPause ska tas bort i OnResume, eftersom det är den enda livscykelmetoden som garanterat körs efter OnPause när aktiviteten återställs till liv.
OnPause
OnPause anropas när systemet är på väg att placera aktiviteten i bakgrunden eller när aktiviteten blir delvis dold. Aktiviteter bör åsidosätta den här metoden om de behöver:
Spara osparade ändringar till persistenta data
Förstöra eller rensa andra objekt som förbrukar resurser
Rampa ned bildfrekvenser och pausa animeringar
Avregistrera externa händelsehanterare eller meddelandehanterare (dvs. de som är knutna till en tjänst). Detta måste göras för att förhindra aktivitetsminnesläckor.
På samma sätt måste de rensas med
.Dismiss()metoden om aktiviteten har visat några dialogrutor eller aviseringar.
Till exempel släpper följande kodfragment kameran eftersom aktiviteten inte kan använda den när den är pausad:
protected override void OnPause()
{
base.OnPause(); // Always call the superclass first
// Release the camera as other activities might need it
if (_camera != null)
{
_camera.Release();
_camera = null;
}
}
Det finns två möjliga livscykelmetoder som anropas efter OnPause:
-
OnResumeanropas om aktiviteten ska returneras till förgrunden. -
OnStopkommer att anropas om Aktiviteten placeras i bakgrunden.
OnStop
OnStop anropas när aktiviteten inte längre är synlig för användaren. Detta inträffar när något av följande inträffar:
- En ny aktivitet startas och döljer den här aktiviteten.
- En befintlig aktivitet förs till förgrunden.
- Aktiviteten förstörs.
OnStop kanske inte alltid anropas i situationer med lite minne, till exempel när Android har brist på resurser och inte kan utföra bakgrundsprocessen korrekt. Därför är det bäst att inte förlita sig på OnStop att bli kallad när du förbereder en aktivitet för förstörelse. Nästa livscykelmetoder som kan anropas efter den här är OnDestroy om aktiviteten försvinner eller OnRestart om aktiviteten kommer tillbaka för att interagera med användaren.
OnDestroy
OnDestroy är den sista metoden som anropas på en aktivitetsinstans innan den förstörs och tas bort helt från minnet. I extrema situationer kan Android avsluta programprocessen som är värd för aktiviteten, vilket resulterar i OnDestroy att den inte anropas. De flesta aktiviteter implementerar inte den här metoden eftersom de flesta rensningar och avstängningar har gjorts i OnPause metoderna och OnStop . Metoden OnDestroy åsidosätts vanligtvis för att rensa tidskrävande uppgifter som kan läcka resurser. Ett exempel på detta kan vara bakgrundstrådar som startades i OnCreate.
Det kommer inte att finnas några livscykelmetoder som anropas efter att aktiviteten har förstörts.
OnRestart
OnRestart anropas efter att aktiviteten har stoppats innan den startas igen. Ett bra exempel på detta är när användaren trycker på hemknappen under en aktivitet i programmet. När detta händer OnPause och sedan OnStop anropas metoder, och aktiviteten flyttas till bakgrunden men förstörs inte. Om användaren sedan skulle återställa programmet med hjälp av aktivitetshanteraren eller ett liknande program anropar OnRestart Android aktivitetens metod.
Det finns inga allmänna riktlinjer för vilken typ av logik som ska implementeras i OnRestart. Detta beror på att OnStart alltid anropas oavsett om aktiviteten skapas eller startas om, så alla resurser som krävs av aktiviteten bör initieras i OnStart snarare än OnRestart.
Nästa livscykelmetod som anropas efter OnRestart blir OnStart.
Tillbaka vs. Hem
Många Android-enheter har två distinkta knappar: en "Bakåt"-knapp och en "Start"-knapp. Ett exempel på detta visas i följande skärmbild av Android 4.0.3:
Det finns en subtil skillnad mellan de två knapparna, även om de verkar ha samma effekt av att sätta ett program i bakgrunden. När en användare klickar på bakåtknappen berättar de för Android att de är klara med aktiviteten. Android förstör aktiviteten. När användaren däremot klickar på hemknappen placeras aktiviteten bara i bakgrunden – Android dödar inte aktiviteten.
Hantera tillstånd under hela livscykeln
När en aktivitet stoppas eller förstörs ger systemet en möjlighet att spara tillståndet för aktiviteten för senare uttorkning. Det här sparade tillståndet kallas instanstillstånd. Android innehåller tre alternativ för att lagra instanstillstånd under aktivitetslivscykeln:
Lagra primitiva värden i ett
Dictionaryså kallat paket som Android använder för att spara tillstånd.Skapa en anpassad klass som innehåller komplexa värden, till exempel bitmappar. Android använder den här anpassade klassen för att spara tillstånd.
Genom att kringgå konfigurationsändringarnas livscykel och anta det fullständiga ansvaret för att upprätthålla tillståndet i aktiviteten.
Den här guiden beskriver de två första alternativen.
Paketstatus
Det primära alternativet för att spara instanstillståndet är att använda ett nyckel/värde-ordboksobjekt som kallas Bundle.
Kom ihåg att när en aktivitet skapas så skickas OnCreate metoden ett paket som en parameter, och paketet kan användas för att återställa instanstillståndet. Vi rekommenderar inte att du använder ett paket för mer komplexa data som inte snabbt eller enkelt serialiseras till nyckel/värde-par (till exempel bitmappar). I stället bör den användas för enkla värden som strängar.
En aktivitet innehåller metoder för att spara och hämta instanstillståndet i paketet:
OnSaveInstanceState – Detta anropas av Android när aktiviteten förstörs. Aktiviteter kan implementera den här metoden om de behöver spara några nyckel/värde-tillståndsobjekt.
OnRestoreInstanceState – Detta anropas när
OnCreatemetoden är klar och ger en annan möjlighet för en aktivitet att återställa sitt tillstånd när initieringen är klar.
Följande diagram illustrerar hur dessa metoder används:
OnSaveInstanceState
OnSaveInstanceState anropas när aktiviteten stoppas. Den får en paketparameter som aktiviteten kan lagra sitt tillstånd i. När en enhet upplever en konfigurationsändring kan en aktivitet använda objektet Bundle som skickas in för att bevara aktivitetstillståndet genom att OnSaveInstanceStateåsidosätta . Tänk till exempel på följande kod:
int c;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
this.SetContentView (Resource.Layout.SimpleStateView);
var output = this.FindViewById<TextView> (Resource.Id.outputText);
if (bundle != null) {
c = bundle.GetInt ("counter", -1);
} else {
c = -1;
}
output.Text = c.ToString ();
var incrementCounter = this.FindViewById<Button> (Resource.Id.incrementCounter);
incrementCounter.Click += (s,e) => {
output.Text = (++c).ToString();
};
}
Koden ovan ökar ett heltal med namnet c när en knapp med namnet incrementCounter klickas, vilket visar resultatet i ett TextView med namnet output. När en konfigurationsändring sker , till exempel när enheten roteras, skulle ovanstående kod förlora värdet c för eftersom bundle skulle vara null, som visas i bilden nedan:
Om du vill bevara värdet c i det här exemplet kan aktiviteten åsidosätta OnSaveInstanceState, och spara värdet i paketet enligt nedan:
protected override void OnSaveInstanceState (Bundle outState)
{
outState.PutInt ("counter", c);
base.OnSaveInstanceState (outState);
}
Nu när enheten roteras till en ny orientering sparas heltalet i paketet och hämtas med raden:
c = bundle.GetInt ("counter", -1);
Anmärkning
Det är viktigt att alltid anropa basimplementeringen av OnSaveInstanceState så att tillståndet för vyhierarkin också kan sparas.
Visa tillståndsdata
Att åsidolägga OnSaveInstanceState är en användbar mekanism för att spara tillfälliga data i en Aktivitet över orienteringsändringar, såsom räknaren i exemplet ovan. Standardimplementeringen av OnSaveInstanceState tar dock hand om att spara tillfälliga data i användargränssnittet för varje vy, så länge varje vy har tilldelats ett ID. Anta till exempel att ett program har ett EditText element som definierats i XML enligt följande:
<EditText android:id="@+id/myText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
EditText Eftersom kontrollen har tilldelats id visas fortfarande data när användaren anger vissa data och roterar enheten, enligt nedan:
OnRestoreInstanceState
OnRestoreInstanceState anropas efter OnStart. Det ger en aktivitet möjlighet att återställa alla tillstånd som tidigare sparades i ett paket under föregående OnSaveInstanceState. Det här är dock samma paket som tillhandahålls till OnCreate.
Följande kod visar hur tillstånd kan återställas i OnRestoreInstanceState:
protected override void OnRestoreInstanceState(Bundle savedState)
{
base.OnRestoreInstanceState(savedState);
var myString = savedState.GetString("myString");
var myBool = savedState.GetBoolean("myBool");
}
Den här metoden finns för att ge viss flexibilitet kring när tillståndet ska återställas. Ibland är det lämpligare att vänta tills alla initieringar är klara innan instanstillståndet återställs. Dessutom kanske en underklass av en befintlig aktivitet bara vill återställa vissa värden från instanstillståndet. I många fall är det inte nödvändigt att åsidosätta OnRestoreInstanceState, eftersom de flesta aktiviteter kan återställa tillståndet med hjälp av paketet som tillhandahålls till OnCreate.
Ett exempel på hur du sparar tillstånd med hjälp av en Bundle, finns i Genomgång – Spara aktivitetstillståndet.
Paketbegränsningar
Även om OnSaveInstanceState det är enkelt att spara tillfälliga data har det vissa begränsningar:
Det anropas inte i alla fall. Om du till exempel trycker på Start eller Tillbaka för att avsluta en aktivitet kommer det inte att leda till
OnSaveInstanceStateatt anropas.Paketet som skickas till
OnSaveInstanceStateär inte utformat för stora objekt, till exempel bilder. När det gäller stora objekt är det bättre att spara objektet från OnRetainNonConfigurationInstance , enligt beskrivningen nedan.Data som sparas med hjälp av paketet serialiseras, vilket kan leda till fördröjningar.
Pakettillstånd är användbart för enkla data som inte använder mycket minne, medan icke-konfigurationsinstansdata är användbara för mer komplexa data eller data som är dyra att hämta, till exempel från ett webbtjänstanrop eller en komplicerad databasfråga. Instansdata som inte är konfigurationsdata sparas i ett objekt efter behov. Nästa avsnitt introducerar OnRetainNonConfigurationInstance som ett sätt att bevara mer komplexa datatyper genom konfigurationsändringar.
Bevara komplexa data
Förutom att spara data i paketet har Android även stöd för att spara data genom att åsidosätta OnRetainNonConfigurationInstance och returnera en instans av en Java.Lang.Object som innehåller de data som ska sparas. Det finns två huvudsakliga fördelar med att använda OnRetainNonConfigurationInstance för att spara tillstånd:
Objektet som returneras från
OnRetainNonConfigurationInstancefungerar bra med större, mer komplexa datatyper eftersom minnet behåller det här objektet.Metoden
OnRetainNonConfigurationInstanceanropas på begäran och endast när det behövs. Det här är mer ekonomiskt än att använda en manuell cache.
Användning OnRetainNonConfigurationInstance är lämplig för scenarier där det är dyrt att hämta data flera gånger, till exempel i webbtjänstanrop. Tänk till exempel på följande kod som söker på Twitter:
public class NonConfigInstanceActivity : ListActivity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SearchTwitter ("xamarin");
}
public void SearchTwitter (string text)
{
string searchUrl = String.Format("http://search.twitter.com/search.json?" + "q={0}&rpp=10&include_entities=false&" + "result_type=mixed", text);
var httpReq = (HttpWebRequest)HttpWebRequest.Create (new Uri (searchUrl));
httpReq.BeginGetResponse (new AsyncCallback (ResponseCallback), httpReq);
}
void ResponseCallback (IAsyncResult ar)
{
var httpReq = (HttpWebRequest)ar.AsyncState;
using (var httpRes = (HttpWebResponse)httpReq.EndGetResponse (ar)) {
ParseResults (httpRes);
}
}
void ParseResults (HttpWebResponse httpRes)
{
var s = httpRes.GetResponseStream ();
var j = (JsonObject)JsonObject.Load (s);
var results = (from result in (JsonArray)j ["results"] let jResult = result as JsonObject select jResult ["text"].ToString ()).ToArray ();
RunOnUiThread (() => {
PopulateTweetList (results);
});
}
void PopulateTweetList (string[] results)
{
ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.ItemView, results);
}
}
Den här koden hämtar resultat från den webbformaterade som JSON, parsar dem och visar sedan resultatet i en lista, enligt följande skärmbild:
När en konfigurationsändring sker – till exempel när en enhet roteras – upprepar koden processen. För att återanvända de ursprungligen hämtade resultaten och inte orsaka onödiga, redundanta nätverksanrop kan vi använda OnRetainNonconfigurationInstance för att spara resultaten, enligt nedan:
public class NonConfigInstanceActivity : ListActivity
{
TweetListWrapper _savedInstance;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
var tweetsWrapper = LastNonConfigurationInstance as TweetListWrapper;
if (tweetsWrapper != null) {
PopulateTweetList (tweetsWrapper.Tweets);
} else {
SearchTwitter ("xamarin");
}
public override Java.Lang.Object OnRetainNonConfigurationInstance ()
{
base.OnRetainNonConfigurationInstance ();
return _savedInstance;
}
...
void PopulateTweetList (string[] results)
{
ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.ItemView, results);
_savedInstance = new TweetListWrapper{Tweets=results};
}
}
Nu när enheten roteras hämtas de ursprungliga resultaten från LastNonConfiguartionInstance egenskapen. I det här exemplet består resultatet av ett string[] innehållande tweets. Eftersom OnRetainNonConfigurationInstance kräver att en Java.Lang.Object returneras, omsluts string[] i en klass som är en underklass till Java.Lang.Object, enligt nedan:
class TweetListWrapper : Java.Lang.Object
{
public string[] Tweets { get; set; }
}
Om du till exempel försöker använda ett TextView som det objekt som returneras från OnRetainNonConfigurationInstance kommer aktiviteten att läcka, vilket illustreras av koden nedan:
TextView _textView;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
var tv = LastNonConfigurationInstance as TextViewWrapper;
if(tv != null) {
_textView = tv;
var parent = _textView.Parent as FrameLayout;
parent.RemoveView(_textView);
} else {
_textView = new TextView (this);
_textView.Text = "This will leak.";
}
SetContentView (_textView);
}
public override Java.Lang.Object OnRetainNonConfigurationInstance ()
{
base.OnRetainNonConfigurationInstance ();
return _textView;
}
I det här avsnittet har vi lärt oss hur du bevarar enkla tillståndsdata med Bundle, och bevarar mer komplexa datatyper med OnRetainNonConfigurationInstance.
Sammanfattning
Android-aktivitetslivscykeln ger ett kraftfullt ramverk för tillståndshantering av aktiviteter i ett program, men det kan vara svårt att förstå och implementera. I det här kapitlet introducerades de olika tillstånd som en aktivitet kan gå igenom under dess livslängd, samt de livscykelmetoder som är associerade med dessa tillstånd. Därefter gavs vägledning om vilken typ av logik som ska utföras i var och en av dessa metoder.