Databasvyer med Room för Android

Room är ett abstraktionslager över SQLite som Google har paketerat som ett AndroidX-bibliotek och även rekommenderar. Sedan version 2.1 erbjuder Room möjligheten att lägga till databasvyer, även kallade lagrade frågor.

Några av de goda skälen till att använda databasvyer är:

  • De gör det enkelt för dig att skriva komplexa frågor och använda dem i DAO-förfrågningar (Data Access Object).
  • Du kan bara fråga efter de fält du behöver, istället för att behöva gå igenom alla fält i en tabell.

I den här handledningen kommer du att bygga en app för kundundersökningar som låter en restaurangs kunder lämna feedback och sedan sparar den feedbacken i Room managed database. Under processen lär du dig följande:

  • Vad är en databasvy?
  • Hur man skapar databasvyer?
  • Hur man använder dem för att förenkla skrivandet av SELECT-förfrågningar
Obs: Den här handledningen förutsätter att du har erfarenhet av att utveckla för Android i Kotlin och att du har jobbat med Room tidigare. Om du inte är bekant med Room kan du ta en titt på vår handledning Data Persistence with Room.

Denna handledning använder även Coroutines med Room. Om du vill veta mer kan du läsa vår handledning Coroutines With Room Persistence Library.

Kom igång

Ladda ner startprojektet genom att klicka på knappen Ladda ner material högst upp eller längst ner i den här handledningen.

Extrahera ZIP-filen och öppna startprojektet i Android Studio 4.0 eller senare genom att välja Öppna ett befintligt Android Studio-projekt på välkomstskärmen.

När Gradle-synkroniseringen är klar utforskar du projektstrukturen. Projektet följer MVVM-arkitekturen, så liknande funktioner finns under ett paket. Bekanta dig med de paket som finns – du kommer att använda dem i den här handledningen.

Bygg och kör. Du får se en enkel skärm med ett välkomstmeddelande, en bild och en knapp START SURVEY.

Skärm för startundersökning i appen Kundundersökningar

Tryck på knappen STARTA UNDERSÖKNING. I den här handledningen kan du bortse från att du inte har ätit någon måltid på restaurangen :]

Nästa skärm är enkätskärmen. Den har ett inmatningsfält för e-post, radioknappar för att välja den måltid som du betygsätter och tre frågor. Varje fråga har knapparna Bra, Medel och Dåligt under sig, så att användaren kan betygsätta sin tillfredsställelse.

Customer Surveys-appens enkätskärm med frågor och möjliga betyg

Du ser också knappen SUBMIT SURVEY (skicka enkät). Tryck på den och du ser en toast som säger att det inte är dags att göra undersökningen ännu. Oroa dig inte, du kommer att fixa det under den här handledningen.

Du har nu välkomnats till The View Restaurant, där du får se fantastiska naturutsikter, smaka på deras utsökta rätter och betygsätta din tillfredsställelse. Under tiden får du också lära dig om Room Database Views.

Användning av databasvyer

Konsultera en tabell som har extra funktionalitet av färdigpaketerade SELECT-frågor för bekvämlighetens skull. Room version 2.1 och högre kallar dessa för en databasvy och tillhandahåller en anteckning med samma namn, dvs. @DatabaseView.

Med hjälp av denna annotation kan du markera en klass så att den beter sig som en databasvy. På så sätt kan du bifoga en fråga till klassen, som nedan:

@DatabaseView("SELECT user.id, user.name " + "AS departmentName FROM user " + "WHERE user.departmentId = department.id")class User { var id: Long = 0 var name: String? = null}

Du kan sedan använda den här klassen i din DAO för att fråga efter data på samma sätt som du skulle göra med en klass som är markerad som en entitet, t.ex. en tabell i en databas.

En DAO hjälper dig att få tillgång till data från din applikations databas. Den innehåller vanligtvis CUD-metoderna (Create, Update och Delete) och kan även innehålla andra metoder som kan vara nödvändiga för läs- och skrivåtkomst till databasen.

Relationen mellan databasvyer och databasen liknar relationen mellan entiteter och databasen. Du kommer att ta en djupare titt på dessa relationer nästa gång.

Varför en databasvy och en entitet

Klasser som är annoterade med @DatabaseView liknar Entity-klasser. Så här gör du:

  • Båda kan använda SELECT FROM i DAO-frågor.
  • Database Views och Entitys kan båda använda @ColumnInfo, vilket gör det möjligt att anpassa den kolumninformation som är kopplad till ett fält.
  • Båda kan använda @Embedded, vilket gör det möjligt för ett fält att ha nästlade fält som frågor kan referera direkt.

Samtidigt som det finns många likheter mellan de två finns det också skillnader mellan DatabaseViews och Entitys:

  • Du kan använda INSERT, UPDATE och DELETE med en Entity, men inte med en DatabaseView.
  • Du definierar alla vyer i dina appar med hjälp av views, men du definierar enheter med hjälp av entities.

Nu när du vet vad en DatabaseView är och hur den jämför och kontrasterar mot en Entity-klass är det dags att använda den och börja lämna in enkäten för The View Restaurant.

Sändning av enkäten

Ditt första steg är att lägga till logiken för att skicka in enkäten och spara den i Room-databasen efter att du tryckt på knappen SUBMIT SURVEY.

Navigera till customersurveys/CustomerSurveyFragment.kt, där du lägger till logiken för att samla in svaren och spara dem i Room. Gör detta genom att ersätta koden i submitSurvey() med denna:

// 1val email = editEmail.text.toString()// 2if (validateEmail(email)) { // 3 val meal = when (radioGroupMeals.checkedRadioButtonId) { R.id.radioBreakfast -> "Breakfast" R.id.radioLunch -> "Lunch" R.id.radioDinner -> "Dinner" else -> "No Meal" } // 4 val customerSurvey = SurveyListItem .CustomerSurvey(0, email, meal, questionOneAnswer, questionTwoAnswer, questionThreeAnswer) customerSurveyViewModel.insertCustomerSurvey(customerSurvey) // 5 findNavController() .navigate(R.id.action_surveyFragment_to_surveyCompletedFragment)}

Det här är vad du gör med den här koden:

  1. Du hämtar e-postmeddelandet från editEmail och tilldelar det till en variabel: email.
  2. Denna villkorskontroll kallar validateEmail(email), som kontrollerar om e-postmeddelandet är null eller inte. Den returnerar false om det är null. Den kontrollerar också om det angivna e-postmeddelandet är giltigt och returnerar false om det inte är det.
  3. Koden inuti if-anvisningen exekveras när validateEmail(email) returnerar true. meal innehåller den typ av måltid som användaren valt från radiogrupperna.
  4. När du har meals värde skapar du SurveyListItem.CustomerSurvey, som innehåller all information om undersökningen. Den hämtar värdena för questionOneAnswer, questionTwoAnswer och questionThreeAnswer från toggleButtonListeners(), som har lyssnare för detta ändamål.
  5. Här sparar du customerSurvey genom att anropa insertCustomerSurvey(customerSurvey) i CustomerSurveyViewModel, som hanterar logiken för att spara till Room.
  6. Du navigerar till SurveyCompletedFragment.

När du lagt till detta märker du att customerSurveyViewModel och findNavController() har röda understrykningar. För att åtgärda detta lägger du först till CustomerSurveyViewModel-initieringen högst upp i klassen, precis under questionThreeAnswer-initieringen.

private val customerSurveyViewModel: CustomerSurveyViewModel by viewModels()

Se till att lägga till respektive importutsagor när IDE uppmanar dig.

Bygg och kör. Starta undersökningen, ange den nödvändiga e-postinmatningen och välj dina svar på frågorna.

Undersökningsskärm med svaren

Skönt, du har slutfört undersökningen.

Visa alla undersökningar med hjälp av databasvyer Skärmdump

Tryck på knappen VIEW SURVEYS … oj, det gör ingenting ännu. Oroa dig inte, du kommer att fixa det snart.

I nästa avsnitt lär du dig hur du skapar din första DatabaseView.

Skapa en databasvy

För att skapa en vy lägger du till en @DatabaseView-annotation till en klass eller dataklass. Börja med att navigera till customersurveys/SurveyListItem.kt. Detta är en förseglad klass med ett par dataklasser som du ska använda i den här handledningen.

I botten av SurveyListItem, precis under QuestionOneSadView, lägger du till följande:

data class HappyBreakFastView( override val email: String) : SurveyListItem()

Den här dataklassen åsidosätter email-variabeln från SurveyListItem och ärver från klassen – vilket innebär att den är en undertyp till SurveyListItem.

När du har skapat den här dataklassen lägger du till @DatabaseView med en SELECT-fråga för att hämta alla e-post-ID:n från tabellen CustomerSurvey där måltid är inställd på ”Breakfast”, precis ovanför dataklassen HappyBreakFastView. Din annotation bör se ut så här:

@DatabaseView("SELECT CustomerSurvey.email FROM CustomerSurvey WHERE CustomerSurvey.meal = 'Breakfast'")

Vissa saker att notera om frågan i annotationen:

  • Frågan fungerar som vilken annan fråga som helst som du har skrivit i Room.
  • Du måste begära alla fält som du har i din dataklass när du skriver frågan. I det här fallet behöver du bara e-postadressen. Du använder SELECT CustomerSurvey.email From... för att hämta e-postadressen från CustomerSurvey.
Obs: I den här handledningen behåller du vyerna och entiteten inom SurveyListItem för att undvika upprepningar och för att göra koden lättare att läsa. Du behöver inte alltid underklassa dina vyer i en förseglad klass; de kan också finnas i en egen separat fil eller klass.

Grattis, du har skapat din första vy! Därefter ska du se hur du kan använda vyn i dina DAO-förfrågningar.

Användning av rumsdatabasvyer i DAO-förfrågningar

Först ska du inkludera HappyBreakFastView i views i appens @Database.

Navigera till database/AppDatabase.kt och i views lägger du till SurveyListItem.HappyBreakFastView::class. Din uppdaterade @Database-annotation bör se ut som nedan:

@Database(entities = , version = 2, exportSchema = false, views = )

Bemärk att version = 2. Du måste uppdatera databasversionen varje gång du lägger till en view i AppDatabase – annars kommer din app att krascha. I det här fallet har du uppdaterat versionen till 2. Synkronisera gradle för att tillämpa alla dessa ändringar.

Nästan, navigera till customers/CustomerSurveysDao.kt och lägg till följande kod strax under getQuestionOneSadView():

@Query("SELECT * FROM HappyBreakFastView")fun getHappyBreakFastCustomers():LiveData<List<SurveyListItem.HappyBreakFastView>>

Denna metod hämtar alla kunder som var nöjda med någon aspekt av undersökningen från restaurangen. För att förklara den närmare:

  • Först använder du HappyBreakFastView som du skulle göra i en vanlig fråga.
  • Du anropar den här metoden i CustomerSurveyRepo för att få en lista över alla kunder som besvarade någon av frågorna med Bra. Observera att metodens returtyp är en lista LiveData av typen SurveyListItem.HappyBreakFastView, som är en observerbar variabelhållare.

Nu har du skapat en view och metoden för att fråga efter listan över kunder som svarat med ett positivt svar i CustomerSurveysDao. I nästa avsnitt lär du dig hur du anropar den här metoden från arkivklassen.

Hämtning av data med hjälp av en databasvy

Navigera till customersurveys/CustomerSurveyRepo.kt och lägg till följande metod precis under getQuestionOneSadView():

fun getHappyBreakFastCustomers() : LiveData<List<SurveyListItem.HappyBreakFastView>> { return customerSurveysDao.getHappyBreakFastCustomers()}

Den här metoden anropar getHappyBreakFastCustomers() från CustomerSurveysDao för att hämta data från Room. Dess returtyp är en LiveData, vilket gör att den som anropar den här metoden kan observera eventuella förändringar i data.

Nästan lägger du till ett anrop till getHappyBreakFastCustomers() i CustomerSurveyViewModel. Den ansvarar för att visa data för vyn – som i det här fallet inte är DatabaseView utan AllSurveysFragment.

Navigera till customersurveys/CustomerSurveyViewModel.kt och lägg till följande kod:

val happyBreakfastCustomers : LiveData<List<SurveyListItem.HappyBreakFastView>> by lazy { customerSurveyRepo.getHappyBreakFastCustomers()}

Denna variabel får sitt värde genom att anropa getHappyBreakFastCustomers() från CustomerSurveyRepo. Det finns en by lazy{} så att du inte laddar data omedelbart, utan snarare när variabeln nås för första gången.

Nästan uppdaterar du användargränssnittet så att det kan visa data.

Avisering av data till användargränssnittet

Navigera till allsurveys/AllSurveysFragment.kt och lägg till följande kod längst ner i klassen:

private fun getHappyBreakfastCustomers() { customerSurveyViewModel.happyBreakfastCustomers.observe(viewLifecycleOwner, Observer { customerSurveyList -> if (customerSurveyList.isEmpty()) { layoutEmptyView.visibility = View.VISIBLE rvReviews.visibility = View.GONE } else { layoutEmptyView.visibility = View.GONE rvReviews.visibility = View.VISIBLE initView(customerSurveyList) } })}private fun initView(customerSurveySurveyList: List<SurveyListItem.HappyBreakFastView>) { val customerSurveysAdapter = CustomerSurveysAdapter(customerSurveySurveyList) rvReviews.adapter = customerSurveysAdapter}

För att förklara vad koden gör:

  • Först anropar den happyBreakfastCustomers och observerar dess värde.
  • Inuti lambdaen för att observera finns en kontroll för att se om customerSurveyList är null eller inte. Om listan är null ställer du in TextViews meddelande No surveys found! till visible och döljer RecyclerView. Om den inte är null ställer du in TextViews synlighet till GONE och visar RecyclerView. Du anropar också initView(customerSurveyList) med customerSurveyList-värdet från CustomerSurveyViewModel.
  • initView(customerSurveySurveyList: List) initialiserar CustomerSurveysAdapter med customerSurveyList och ställer in adaptern för RecyclerView till CustomerSurveysAdapter, som nu visar listan över undersökningar på användargränssnittet.

IDE uppmanar dig att lägga till importen SurveyListItem. Om det inte gör det, lägg till den här importen:

import com.raywenderlich.android.customersurveys.customersurveys.SurveyListItem

När du nu har visat data på användargränssnittet har du bara några få steg kvar innan allt fungerar perfekt.

Nästan ska du lägga till koden som hanterar hämtning av data från Room beroende på det alternativ som valts i rullgardinsmenyn i användargränssnittet, dvs. widgeten Spinner.

Hämtning av data för de olika vyerna

Lägg till följande kodstycke precis under onCreate i AllSurveysFragment.kt::

private fun spinnerListener() { filterSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { when (position) { 0 -> resetState() 1 -> getAllCustomerSurveys() 2 -> getHappyBreakfastCustomers() 3 -> getSadDinnerCustomers() 4 -> getAverageLunchCustomers() 5 -> getQuestionOneSadCustomers() } } }}

Kodstycket ovan ställer in onItemSelectedListener till filterSpinner och åsidosätter två metoder: onNothingSelected och onItemSelected. Du vill inte göra något när ingenting är valt, så onNothingSelected lämnas tomt. Du vill dock reagera på när ett objekt är valt, så du måste implementera onItemSelected.

onItemSelected har ett when-uttryck som anropar olika metoder beroende på vilket alternativ som valts i filterSpinner. Dessa metoder liknar getHappyBreakfastCustomers(), men hämtar data med hjälp av ett annat DatabaseView.

Se till att du lägger till importerna när IDE uppmanar dig.

Slutligt lägger du till ett anrop till spinnerListener() inne i onViewCreated, precis efter setupSpinner(), enligt nedan:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupSpinner() // Added spinnerListener()}

Nu när du har allting klart för att hämta enkäterna är ditt nästa steg att lägga till koden för att navigera till AllSurveysFragment.

Navigera till alla undersökningar

Detta är det sista steget du behöver för att se vyerna i praktiken.

Navigera till completedsurvey/SurveyCompletedFragment.kt och avkommentera koden inuti btnViewSurveys. Slutresultatet kommer att se ut så här:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) btnViewSurveys.setOnClickListener { findNavController() .navigate(R.id.action_surveyCompletedFragment_to_allSurveysFragment) }}

Här ställer du helt enkelt in klicklyssnaren för knappen VIEW SURVEYS och navigerar till AllSurveysFragment.

När du har avkommenterat koden kommer IDE att uppmana dig att importera findNavController(). Importera helt enkelt det som krävs.

Bygg, kör och starta en enkät, svara sedan på frågorna och skicka in dem. Visa slutligen Alla undersökningar, där du kan hämta alla data beroende på vilket alternativ du valt på spinnaren.

Spinnaralternativ i appen
Genomsnittligt nöjda kunder listas med hjälp av en databasvyn
Sorgsamma middagskunder hittas inte med hjälp av en databasvyn

Gratulerar! Du har avslutat din upplevelse på The View Restaurant. Förhoppningsvis har du ätit en god måltid, sett fantastiska vyer och haft en chans att lära dig vad DatabaseViews är.

Vart ska vi ta vägen härifrån?

Ladda ner slutprojektet med hjälp av knappen Ladda ner material högst upp eller längst ner i handledningen.

För mer information om Roms funktioner kan du läsa den officiella dokumentationen från Android.

Vi hoppas att du gillade den här handledningen om Room Database Views. Om du har några frågor, kommentarer eller häftiga ändringar till den här projekt-appen, vänligen delta i forumdiskussionen nedan.

raywenderlich.com Weekly

Raywenderlich.com-nyhetsbrevet är det enklaste sättet att hålla sig uppdaterad om allt du behöver veta som mobilutvecklare.

Få en veckovis sammanställning av våra handledningar och kurser, och få en gratis fördjupad e-postkurs som bonus!

Genomsnittligt betyg

4.8/5

Lägg till ett betyg för detta innehåll

Logga in för att lägga till ett betyg

10 betyg

Leave a Reply