Java 8 för Android Cleaner Code med Lambda Expressions

Lambda-uttryck kan hjälpa dig att ta bort boilerplate-kod från dina projekt och bearbeta enorma mängder data med lätthet. Se hur du använder den här djupgående koll på de funktioner i Java 8 som du kan börja använda i dina Android-projekt idag. 

Java 8 för Android

Java 8, som debuterade långt tillbaka i mars 2014, var ett stort steg framåt för programmeringsspråket och introducerade en lista över funktioner som lovade att göra kodning i Java enklare och mer koncis än någonsin tidigare.

Olyckligtvis skulle Android-utvecklare inte känna fördelarna med dessa funktioner för ett tag, eftersom Google experimenterade med att föra Java 8 till Android-plattformen via Jack (Java Android Compiler Kit) innan den avlägsnade Jack till stöd för att stödja Java 8 inbyggt i Android Studio.

Nu, med utgåvan av Android Studio 3.0, har vi äntligen en version av Android toolchain som har inbyggt stöd för några av Java 8: s viktigaste funktioner.

I den här serien ska jag visa dig hur du tar bort en massa kedjekodskoder från dina projekt, bearbetar enorma mängder data med lätthet och till och med omfamnar en mer funktionell stil i din Java-programmering med Java 8. Vi kommer att vara Ta en djupgående titt på de Java 8-funktioner som du kan börja använda idag.

När du har avslutat den här serien är du redo att använda alla följande Java 8-funktioner i dina Android-projekt:

  • lambda uttryck
  • metodreferenser
  • standardmetoder
  • statiska gränssnittsmetoder
  • skriv anteckningar
  • upprepa anteckningar
  • funktionella gränssnitt
  • Stream-API: n

I det här första inlägget ska vi titta på den funktion som genererade störst när Java 8 släpptes för första gången, och det har potential att göra mest skillnad för Android-utvecklare: lambda-uttryck.

Förbereda din utvecklingsmiljö

Innan du kan börja använda några Java 8-funktioner måste du se till att din utvecklingsmiljö är konfigurerad för att stödja denna version av Java.

Om du inte redan har Java 8 installerat måste du hämta den senaste JDK8 och uppdatera Android Studios JDK-sökväg så att den pekar på JDK8-paketet:

  • Starta Android Studio.
  • Välj Arkiv> Projektstruktur ... från Android Studio-verktygsfältet.
  • Uppdatera JDK Plats fält så att det pekar på ditt nyladdade JDK8-paket.

Om du inte är säker på vilken version av Java du har installerat kan du kontrollera genom att öppna ett terminalfönster (om du är en Mac-användare) eller en Kommandotolk (om du är på Windows) och sedan köra följande kommando:

java -version

Om den returnerar bygg 1.8 eller högre, så är du bra att gå!

Du måste också ha Android Studio 3.0 Preview 1 eller senare installerat, men för att minska dina chanser att stöta på buggar och annat märkligt beteende rekommenderas att du installerar den senaste versionen av Android Studio 3.0, oavsett om det är en beta eller förhandsgranskning eller, helst en stabil version av Android Studio 3.0 (som ännu inte var tillgänglig vid skrivningstillfället). 

Därefter måste du göra några ändringar i ditt projekt build.gradle filer. Vanligtvis behöver du bara lägga till några rader kod som anger att detta projekt ska generera Java 8 bytecode. Om du tidigare har experimenterat med Java 8-funktioner med Jack-kompilatorn eller det populära Retrolambda-projektet måste du inaktivera dessa verktyg innan ditt projekt kan använda det nya och förbättrade Java 8-stödet från Android standardverktygskedja.

I följande avsnitt visar jag hur du aktiverar support för Java 8 och hur du inaktiverar Retrolambda och Jack om det behövs.

Lägga till Java 8-stöd till ett nytt projekt

Om du antar att du inte tidigare har aktiverat Jack eller lagt till Retrolambda som ett projektberoende, öppnar du det första steget på din projektnivå build.gradle fil och se till att du använder version 3.0.0-alpha1 (eller högre) i programmet Gradle for Android:

buildscript repositories google () jcenter () beroenden classpath 'com.android.tools.build:gradle:3.0.0-alpha6'

Öppna sedan varje modulnivå build.gradle fil där du vill använda Java 8-funktioner och ställa in källkodens språknivå och versionen av den genererade Java bytecode till JavaVersion.VERSION_1_8:

android compileSdkVersion 26 buildToolsVersion "26.0.1" defaultConfig applicationId "com.jessicathornsby.myapplication" minSdkVersion 26 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // Lägg till följande block // compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8

Om du migrerar från Jack

Jack-kompilatorn kan avlägsnas, men så länge det är aktiverat kommer ditt projekt att använda det Java 8-stöd som Jack tillhandahållit, i stället för det stöd som Android-verktyget innehåller.

Att använda ett avstängt verktyg är aldrig en bra idé, men det finns några ytterligare anledningar till varför du ska migrera från Jack-kompilatorn, om du inte redan har.

För det första kan Jack stödja en delmängd av Java 8-funktioner, men till skillnad från standardverktygskedjan stöder det inte bibliotek från tredje part som använder dessa funktioner, så med hjälp av Jack begränsar du omedelbart dina alternativ när det gäller bibliotek från tredje part.

För det andra tar Jack-kompilatorn Java-kod och omvandlar den direkt till dex utan att producera någon mellanliggande bytekod. Så länge Jack är aktiverat kan du inte använda något av de verktyg som är beroende av denna mellanproduktion, till exempel annoteringsprocessorer och bytecode analysatorer.

För att inaktivera Jack-kompilatorn, öppna din modulnivå build.gradle fil och ta bort jackOptions avsnitt, men se till att du lämnar compileOptions blockera intakt:

android compileSdkVersion 26 buildToolsVersion "26.0.1" defaultConfig applicationId "com.jessicathornsby.myapplication" minSdkVersion 26 targetSdkVersion 26 versionCode 1 versionName "1.0" // Ta bort hela jackOptions avsnittet // jackOptions enabled true testInstrumentationRunner "android.support. test.runner.AndroidJUnitRunner "// Ta inte bort compileOptions avsnittet // compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8

Om du migrerar från Retrolambda

Liksom Jack, stödjer Retrolambda inte tredje parts bibliotek som använder Java 8-språkfunktioner. Om ditt projekt är inställt för att använda Retrolambda-pluginet, bör du ta bort det här plugin så att ditt projekt kan återgå till standardverktygskedjan.

Öppna din projektnivå build.gradle fil och ta bort Retrolambda som ett projektberoende:

 beroenden classpath 'com.android.tools.build:gradle:3.0.0-beta2' // Ta bort följande rad // classpath 'me.tatarka: gradle-retrolambda: 3.7.0'

Ta sedan bort Retrolambda-pluginprogrammet från var och en av din modulnivå build.gradle filer:  

tillämpa plugin: 'com.android.application' // Ta bort följande rad // använd plugin: 'me.tatarka.retrolambda' android ... // Ta inte bort compileOptions block! // compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8

Testa din Java 8-support

Det enklaste sättet att verifiera att ditt projekt nu kan stödja Java 8 är att skriva ett snabbt lambda-uttryck och se om ditt projekt fortfarande sammanställer.

Lägg till en knapp i ditt användargränssnitt (eller använd en knapp som redan finns) och sedan implementera en onClickListener för den här knappen, med ett lambda-uttryck. Oroa dig inte om följande kod inte ger stor mening nu - det kommer i slutet av den här artikeln!

importera android.support.v7.app.AppCompatActivity; importera android.os.Bundle; importera android.widget.Button; importera android.view.View; importera android.widget.Toast; public class MainActivity utökar AppCompatActivity @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); // Implementera onClickListener med ett lambdauttryck // Knappknapp = (Knapp) findViewById (R.id.button); om (knapp! = null) button.setOnClickListener ((Visa vy) -> Toast.makeText (detta "Jag var skrivet i Java 8!", Toast.LENGTH_LONG) .show ()); 

Kontrollera att ditt projekt fortfarande kompilerar, antingen genom att välja Synkronisera från bannern som visas eller genom att välja Verktyg> Android> Synkronisera projekt med gradfiler från Android Studio-verktygsfältet.

Om Android Studio inte kastar några fel, är du redo att börja använda alla funktioner som vi listade i början av den här artikeln, inklusive lambda-uttryck!

Varför är Lambda-uttryck så viktiga?

Lambda uttryck var enkelt Java 8: s största nya funktion, och kan ha en enorm inverkan på mängden av boilerplate-kod du behöver skriva när du skapar nästan alla Android-appar.

I huvudsak representerar ett lambda-uttryck en funktion som inte hör till någon klass, och att du kan passera med lätthet och sedan utföra efterfrågan.

Den här funktionen avlägsnar en långvarig frustration som många Android-utvecklare har upplevt med Java: som ett objektorienterat språk har passerar runt block av kod alltid kändes svårare än det borde vara. Om du till exempel vill skapa en ny tråd och sedan skicka någon kod till den tråden, måste du vanligtvis skapa en tråd med en anonym implementering av Runnable-gränssnittet. Det är mycket arbete bara för att klara någon kod! Genom att tillhandahålla ett enkelt sätt att skicka en funktion till en metod har lambda-uttryck potential att förenkla några av de vanligaste uppgifterna som du ska utföra som en Android-utvecklare.

Lambda-uttryck kommer också att vara ett välkommet tillägg för alla Java-utvecklare som vill ta ett mer funktionellt sätt att programmera. Före Java 8 behöver kodning i en funktionell stil oundvikligen kräva att du skriver mycket pannkodskod, men nu när du kan skicka funktioner runt med lambda-uttryck behöver du inte skriva in Java-koden på ett mindre objektorienterat sätt. skriver ett ton av anonyma klasser.

Hur skapar jag en Lambda-uttryck?

Du skapar ett lambda-uttryck med följande syntax:

(argument) -> expression body

Piloperatören är ganska självförklarande, men reglerna för hur du ska strukturera lambdas argument och uttryck kan variera beroende på vad du försöker uppnå, så låt oss utforska dessa två detaljer mer ingående.

Argumentet

Argumentet är en eller flera parametrar, som är nästan alltid omsluten inom parentes. Även om ditt lambda-uttryck inte har några parametrar behöver du fortfarande leverera tomma parentes, till exempel:

() -> System.out.println ("Detta lambda uttryck har inga parametrar");

Undantaget från denna regel är när din metod har en enda parameter med dess typ avledd, i vilket fall du kan utelämna parenteserna:

textView.setOnLongClickListener (händelse -> System.out.println ("Long Click"));

Du kan använda flera parametrar i ditt argument, genom att separera varje parameter med ett kommatecken:

(parameter1, parameter2) -> System.out.println ("Parametrar:" + parameter1 + "," + parameter2);

Typinferens är möjlig i lambdas, så du kan allmänt släppa datatypen från ditt argument. Om kompilatorn inte kan avleda datatypen måste du dock lägga till typen framför din parameter (er):

 Knappknapp = (Knapp) findViewById (R.id.button); om (knapp! = null) button.setOnClickListener ((Visa vy) -> Log.d ("debug", "Button clicked")); 

Expressionsorganet

Expressionsorganet är koden som du vill utföra, vilket kan vara ett enda uttryck eller flera streckkod. Om du vill utföra flera rader måste du skapa ett uttalandeblock genom att omsluta det här avsnittet av din kod med lockade hängslen:

Knappknapp = (Knapp) findViewById (R.id.button); button.setOnClickListener (view -> Log.d ("debug", "Button clicked"); Toast.makeText (detta "Jag var skrivet i Java 8!", Toast.LENGTH_LONG) .show ();

Om ditt uttryck returnerar ett värde måste det returneras med ett returmeddelande, till exempel:

(parameter1) -> System.out.println ("Parameter:" + parameter1); returnera "returvärde"; 

Använda Lambda-uttryck i dina Android Apps

Nu har vi en översikt över de olika sätten att strukturera ett lambda-uttryck, låt oss ta en titt på några av de vanligaste scenarierna där du kan använda lambda-uttryck i ditt Android utvecklingsarbete.

Lambdas för händelsehantering

Din typiska Android-app måste kunna svara på ett brett utbud av användarinmatningshändelser, och lambda-uttryck kan göra denna händelsehantering mycket enklare.

I följande kod använder vi en anonym klass för att skapa en instans av onClickListener med en överdriven onClick metod. Chansen är att du har skrivit den här typen av kod otaliga gånger.

Knappknapp = (Knapp) findViewById (R.id.button); button.setOnClickListener (new View.OnClickListener () @Override public void onClick (View view) doSomething (););

Genom att skriva om ovanstående kod med ett lambda-uttryck kan vi ta bort alla följande:

  • klassen instansiering: ny View.OnClickListener ()
  • åtkomstmodifieraren, metodnamnet och typen: offentligt ogiltigt onClick (View view)
  • och parametertyperna, så du behöver inte skriva Visa vy

Det innebär att vi kan implementera exakt samma funktionalitet, med en enda rad:

button.setOnClickListener (visa -> doSomething ());

Lambdas för Multithreading

Multithreading är ett annat vanligt scenario där lambda-uttryck kan hjälpa dig skriva renare kod. Som standard har Android en enda användargränssnitt (användargränssnitt) som ansvarar för hanteringen av all användarinteraktion, skickar händelser till lämpliga användargränssnitt och ändrar användargränssnittet. Så snart du blockerar den här användargränssnittet med några långvariga eller intensiva operationer, kommer din ansökan att bli oförsvarlig och kan till och med utlösa Android: s ANR-dialog (Ansökan inte svara). Så att skapa ytterligare trådar och tilldela kod för att köra på dessa trådar är ofta en viktig del av Android-utvecklingen.

Före Java 8 måste du ange en anonym klass som implementerar Runnable gränssnitt:

Runnable r = new Runnable () @Override public void run () System.out.println ("My runnable"); ; Gänga tråd = Ny tråd (r); thread.start ();

Alternativt kan du skapa en ny tråd med ett anonymt genomförande av Runnable gränssnitt:

Tråd = Ny tråd (Ny Runnable () @Override public void run () System.out.println ("My runnable");); thread.start ();

Att ersätta den här anonyma klassen med ett lambda-uttryck kan göra denna ofta utförda uppgift mycket mer koncis:

Runnable r = () -> System.out.println ("My runnable"); ; // Starta den nya tråden // Ny tråd (r) .start ();

Slutligen, om du använder biblioteket RxJava eller RxAndroid, kan du använda lambda-uttryck för att hjälpa dig att skapa observabla.

Här skapar vi en enkel Märkbar som avger hej världssträngentill allt dess observatörer:

Observable.just ("Hej världen!") .Subscribe (new Action1() @Override public void call (String s) Log.d (TAG, s); );

Med ett lambda-uttryck kan du ersätta allt detta Åtgärd 1 kod med en enda rad:

Observable.just ("Hej världen!") .Subscribe (s -> Log.d (TAG, s));

Använda Lambda-uttryck i din Verkliga livet Koda

Efter att ha läst all teori bakom en ny funktion, blir nästa utmaning vanligtvis i vanan använder sig av den här nya funktionen. Detta kan vara särskilt tufft med något som lambdas, som är utformat för att användas istället för bekant boilerplate-kod, eftersom det alltid är frestelsen att helt enkelt falla tillbaka på vad du vet.

Android Studio har några funktioner som kan hjälpa dig att den sista pushen för att ersätta bekant-men-clunky-kod med glänsande nya lambda-uttryck.

Den första funktionen är Android Studios meningsåtgärdsmeny, som automatiskt kan konvertera alla kompatibla anonyma klasser till motsvarande lambda-uttryck. Det här är perfekt om du någonsin är osäker på hur du skriver ett visst kodstycke i ett lambda-format: skriv det som vanligt och använd sedan handlingsmenyns automatiskt konvertera funktion.

För att omvandla en anonym klass till ett lambda-uttryck:

  • Håll markören över den anonyma klassen och Android Studio ska visa ett verktygstips som informerar dig om att det kan konvertera detta avsnitt av kod till ett lambda-uttryck.
  • Tryck på din Mac Alt / Option-Enter tangenterna, eller använd Alt-Enter genväg om du är en Windows- eller Linux-användare.
  • Välj Byt ut med lambda alternativet från snabbmenyn.


Alternativt kan du använda Android Studio inspektionsverktyg för att flagga varje anonym klass som du potentiellt kan ersätta med ett lambda-uttryck, över hela ditt projekt. Du kan sedan antingen skriva om varje anonym klass manuellt eller låta Android Studios automatiskt konvertera funktion visa dig hur det är gjort.

För att markera alla anonyma klasser som Android Studio kan ersättas med ett lambda-uttryck:

  • Välj Analysera> Kör inspektion med namn från Android Studio-verktygsfältet.
  • I popupen som visas börjar du skriva Anonym typ kan ersättas med lambda, och välj sedan det här alternativet när det visas i rullgardinsmenyn.


  • I det efterföljande fönstret väljer du Hela projektet att flagga varje anonym klass över ditt projekt. Alternativt kan du ange enskilda moduler eller filer där Android Studio ska utföra denna inspektion.
  • Klick ok.
  • Välj Analysera> Kontrollera kod från Android Studio-verktygsfältet.

Rutan för inspektionsresultat ska nu visas och visa en lista över alla anonyma klasser som du kan ersätta med ett lambda-uttryck. Att titta närmare på en anonym klass, helt enkelt dubbelklicka den klassen i Inspektionsresultat fönster och Android Studio öppnar filen och tar dig till den exakta raden som innehåller den här anonyma klassen.

För att ersätta den nu valda anonyma klassen med ett lambda-uttryck, ge Byt ut med lambda tryck ett klick.


Om Android Studio inte öppnar fönstret Inspection Results automatiskt, kan du starta det manuellt genom att välja Visa> Verktyg Windows> Inspektionsresultat från Android Studio-verktygsfältet. Om inspektionsresultat inte visas i Verktyg Windows undermenyn, kan du behöva välja Analysera> Kontrollera kod ... från Android Studio-verktygsfältet först.

Testa Lambda-uttryck

Trots de många fördelar som lambda-uttryck har att erbjuda, finns det en stor nackdel som du bör vara medveten om innan du lägger till dem i din kod. Eftersom lambdas inte har ett namn kan du inte ringa dem direkt från din testkod, så att lägga ett stort antal lambdas till ditt projekt kan göra det svårare att testa.

Helst bör ditt lambda uttryck vara för enkelt att bryta, så att du inte kan testa dem ska inte vara för stor för ett problem. Men om du behöver testa en lambda, kan du alltid behandla den som en privat metod och test enheten resultat, snarare än lambda själv. Alternativt kan du refactor lambda uttrycket i sin egen metod, så du kan referera det direkt och därför testa det som normalt.  

Slutsats

I det här första inlägget på Java 8-språkfunktioner såg vi på hur du ställer in dina Android-projekt för att stödja Java 8 och hur man skär ner på boilerplate-kod genom att ersätta anonyma klasser med lambda-uttryck. 

I nästa inlägg visar jag dig hur du trimmer ännu mer kod från dina Android-projekt genom att kombinera lambda-uttryck med metodreferenser och hur du kan förbättra dina gränssnitt med standard- och statiska gränssnittsmetoder.

Under tiden, kolla in några av våra andra inlägg om Android app utveckling!