Naar content
Trending apps
  • Google Meet

  • Google Duo: videogesprekken van hoge kwaliteit

  • Maps: Navigatie en OV

  • WhatsApp Messenger

  • Messenger

Trending games
  • Fortnite

  • Minecraft Earth

  • Dr. Mario World

  • Harry Potter: Wizards Unite

  • Breaking Bad: Criminal Elements

Trending smartphones
  • OPPO Find X2 Pro

  • Nokia 8.3

  • Samsung Galaxy A51

  • Poco F2 Pro

  • Xiaomi Mi Note 10 Pro

Nieuwste tablets
  • Samsung Galaxy Tab S6

  • Samsung Galaxy Tab A 10.5

  • Samsung Galaxy Tab S4

  • Samsung Galaxy Tab S3 9.7

  • Asus Zenpad 3S 10

DevTutorial 8 - OrmLite for Android: SQLite the easy way

· 19 oktober 2011

Vandaag leer je over het database-gebruik in Android. Eigenlijk is dit een vrij geavanceerd begrip in Android programmeren. Maar door een (hopelijk) duidelijke uitleg én de hulp van extra Android classes die horen bij het OrmLite project kom je een heel eind.

Wat gaan we doen

Deze DevTutorial draait om de de interne Database van Android: SQLite. Op veel tutorials die je op het internet tegenkomt wordt je geleerd om op een best omslachtige manier gegevens in de database te stoppen en er weer uit te halen. De bedoeling is dat je via deze DevTutorial leert hoe dat veel sneller kan. Hierbij kom je de volgende onderwerpen tegen:

  • Hoe kan je andere classes gebruiken in Android die door andere mensen zijn geschreven.
  • Wat is een database en hoe kan je die het beste begrijpen.
  • Hoe pas je een class aan zodat je hem in een tabel van je database kan stoppen.
  • Wat is de OrmLiteSqliteOpenHelper class en hoe verbindt hij je SQLite-database met de objecten die je op wilt slaan.
  • Welke methods heeft de Dao<T, ID> interface en hoe kan je die gebruiken om relatief vrij eenvoudig met je database te werken.
De extra oefening gaat over het combineren van de AutoCompleteTextView met je database: Langzamerhand maak je ingewikkelde mooie dingen. :) Alle oefeningen van deze DevTutorial gaan door op de Twitter-app uit de vorige twee DevTutorials.
Succes ermee!

Java package downloaden en toevoegen: OrmLite

Voordat je straks verder kan met de DevTutorial heb je een extra verzameling van Java-packages nodig. Zoals je weet zie je hier alle packages die standaard in Android zitten, hierin zitten de Activity class, de View class, de LayoutInflator class en ga zo nog maar een tijdje door. Soms is het zo dat je bepaalde classes nodig hebt waarop je bepaalde methods wil kunnen gebruiken en dat deze classes niet standaard aanwezig zijn in Android. De eerste oplossing hiervoor is het zelf maken van nieuwe classes: dat heb je bijvoorbeeld in de twee vorige DevTutorials gedaan toen je zelf twee eigen classes vanaf het begin opbouwde. De tweede oplossing is zoeken op het internet of iemand anders al classes heeft geschreven die een groot deel van de methods bevatten die je nodig hebt. In deze DevTutorial willen we de lokale database van Android gebruiken. Er zijn al wel classes in Android die je hiervoor kan gebruiken, maar hiervoor moet je best veel programmeerwerk doen. De standaard Androidclasses hiervoor gebruiken is helemaal niet verstandig als je bedenkt dat er classes bestaan die je veel programmeerwerk uit handen nemen. Deze classes zijn samen gebundeld in de OrmLite package, daar kan je hier meer over lezen. Om de OrmLite package te kunnen gebruiken moet je hem downloaden en in Eclipse toevoegen aan je Androidproject. Hoe je deze package kan gebruiken lees je straks, eerst gaan we hem toevoegen:

  • Ga naar naar de downloadpagina van OrmLite en download de .jar bestanden van versie 4.27 voor core (de kolom die rechts naast de Date kolom staat) en android (de meest rechtse kolom). Na het downloaden heb je de bestanden ormlite-core-4.27.jar en ormlite-android.4.27.jar.
  • Voordat je deze kan gebruiken moet je eerst een map aanmaken in je AndroidProject: klik in het Package Explorer window met de rechtermuisknop op je Androidproject MyTwitter en klik op New --> Folder: .
  • Vul in het volgende scherm als map-naam lib in: .
  • Kopieer nu de bestanden ormlite-core-4.27.jar en ormlite-android-4.27.jar naar de map die je net hebt aangemaakt zodat je Pacakage Explorer Window er als volgt uit ziet: .
  • Klik weer met de rechtermuisknop op je project MyTwitter in je Package Explorer Window en klik op Properties: . En ga in het daaropvolgende scherm naar Java Build Path en klik op Libraries: .
  • Klik in het scherm waar je nu bent op de knop Add JARS... en zoek naar de bestanden die je net hebt toegevoegd . Voeg ze één voor één beide toe door op OK te klikken, zodat je scherm er als volgt uit ziet: . Je kan nu het scherm Properties for MyTwitter sluiten door rechtsonderin op OK te klikken.

Nu heb je de packages aan je Androidproject toegevoegd die horen bij OrmLite Core (de kern van OrmLite) en Orm Lite Android (de packages speciaal voor Android). Hierdoor kan je nu ook al deze classes en al deze classes gebruiken in je app. Een belangrijke class in deze packages is de OrmLiteSqliteOpenHelper class en een belangrijke interface is de Dao<T, ID> interface. Nadat zometeen wordt uitgelegd wat een database is, worden deze class en interface uitgelegd.

De database van Android: SQLite

Android heeft een eigen database-systeem waarin je gemakkelijk gegevens kan opslaan zodat je gegevens bewaard blijven als je je app afsluit. De technologie die Android hiervoor gebruikt heet SQLite. De homepage van SQLite ziet er wel een beetje simpel uit, maar dit is geen reden om deze technologie te onderschatten (de iPhone en iPad gebruiken SQLite bijvoorbeeld ook). Om te begrijpen wat een database is, kan je eerst het beste denken aan meerdere kaartenbakken die vroeger gebruikt werden in bedrijven om verschillende soorten gegevens bij te houden: Als je vroeger een bedrijf had dat bijvoorbeeld boeken verkocht, dan hadden ze een kaartenbak voor boeken, een kaartenbak voor klanten en een kaartenbak voor bestellingen van klanten. In de kaartenbak voor boeken zaten dan allemaal verschillende kaarten en op elke kaart stond de beschrijving van een boek, inclusief de titel, jaar van uitgave, prijs en meer van dat soort gegevens. Op dezelfde manier had je de kaartenbak voor klanten met allemaal kaarten waar op elke kaart de gegevens van één klant stonden (naam, woonplaats, rekeningnummer, etcetera). En op dezelfde manier hadden alle bestellingen die klanten gedaan hebben één kaart in de kaartenbak van bestellingen. Het idee van deze kaartenbakken was dat je verschillende plekken had (kaartenbakken dus) waar je verschillende gegevens in bewaarde. Door de informatie op deze manier op te delen wist je waar je moest zijn als je wat voor informatie dan ook nodig had, in plaats van dat alle kaarten allemaal in één kaartenbak werden gestopt. Als je dit verhaal wil snappen in database-taal, dan zijn alle kaartenbakken samen de database: dus de verzameling van álle gegevens (data). Maar omdat het belangrijk is verschillende soorten gegevens gescheiden te houden heb je in de database verschillende tabellen (dat zijn de kaartenbakken). Een tabel is dus eigenlijk een afgescheiden plek in de database waarin je dezelfde soort gegeven kan opslaan. Voordat je een tabel kan gebruiken moet je aangeven welke soort gegevens je erin wilt opslaan, of in kaartenbak-taal: welke informatie wil je op elke kaart kunnen invullen zodat je dit later kan teruglezen. Nu even terug naar onze Twitter-app: als we een kaartenbak (tabel dus) van Twitter-gebruikers willen hebben, dan willen we de gebruikersnaam van de gebruiker kunnen opslaan en ook de bio, de website en het aantal favoriete Tweets. En verder: omdat de kaartenbak altijd blijft bestaan (de tabel verdwijnt niet als we de Androidapp afsluiten) willen we weten of de gegevens in de kaartenbak niet te oud zijn: we willen op elke kaart kunnen bijhouden wanneer we hem voor het laatst hadden gecontroleerd op actualiteit. Hierdoor kunnen we straks programmeren: als de gegevens ouder dan één dag zijn, download dan nieuwere gegevens. Als we straks een kaart willen opvragen uit één specifieke kaartenbak, dan moeten we dat kunnen doen via een code die voor elke kaart anders is in de kaartenbak. Op deze manier kan je vanuit andere plekken in je app een verbinding maken met één specifieke kaart. In het geval van Twitter-gebruikers kan je als code prima de gebruikersnaam gebruiken: er is namelijk altijd maar één Twitter-gebruiker gekoppeld aan één specifieke gebruikersnaam (er zijn niet twee mensen die met een ander e-mailadres en ander wachtwoord gekoppeld zijn aan hetzelfde Twitter-account). In database-taal noem je zo'n unieke code een id, dat is een afkorting van identificatie. Meestal gebruik je als id per kaart in een kaartenbak (in database-taal: per record per tabel) een getal (int of long), maar voor het opslaan van Twitter-gebruikers is de gebruikersnaam ook een goede keuze. Als je nou één kaartenbak gebruikt voor Twitter-gebruikers, dan is het logisch om een andere kaartenbak te gebruiken voor Tweets van die gebruikers. Op elke kaart in de kaartenbak (database-taal: in elk record in de tabel) wil je een Tweet kunnen opslaan: dus elke kaart heeft dan een stippellijntje waarop je de tekst van de Tweet kan invullen en een stippellijntje voor de datum van de Tweet. Maar alle Tweets van iedereen in één kaartenbak gooien heb je niet zoveel aan: je kan nu wel alle Tweets snel opzoeken, maar vaak wil je alleen de Tweets opzoeken die bij een bepaalde gebruiker horen (want je hebt immers van verschillende gebruikers een kaartje in de kaartenbak voor Twitter-gebruikers). Een oplossing hiervoor is om een touwtje te knopen tussen elke Tweet en de Twitter-gebruiker die bij die Tweet hoort (er zit inderdaad een gaatje bovenin elke kaart in de kaartenbak waar je de touwtjes aan vast kan knopen). Als je dan een kaart uit de Twitter-gebruikers kaartenbak optilt, dan zie je door middel van de touwtjes welke Tweets er bij die Twitter-gebruiker horen. Hoe je dit verhaal van de touwtjes knopen vertaalt naar Databases wordt je uitgelegd in de volgende DevTutorial.

De Twitter class klaar maken voor zijn eigen tabel

Voordat je begint met de Twitter class klaarmaken voor het opslaan in zijn eigen tabel, wil je nadenken welke gegeven je wilt opslaan. Dit zijn sowieso de gegevens die je al in je TwitterUser class had:

  • mUserName
  • mWebsite
  • mDescription
  • mFavouritesCount

Nu komt het leuke van OrmLite: je kan aangeven dat je deze gegevens wilt opslaan door middel van annotaties. Een annotatie (annotation) is een soort notitie bij je code die gebruikt wordt om extra eigenschappen toe te schrijven aan (onder andere) je variabelen. Een annotatie gebruik je door '@' te schrijven boven het definiëren van de variabele, en daarachter de naam van de annotatie te schrijven. De notatie die je moet gebruiken om aan te geven dat je een variabele wil opslaan in een tabel is de DatabaseField annotatie die in de package com.j256.ormlite.field.DatabaseField zit.

  • Voeg de annotaties aan de variabelen van je TwitterUser class toe zodat het begin van je class er als volgt uit ziet (en vergeet de import niet te doen):

Zoals net beschreven wil je ook een id, een unieke code, hebben voor elk TwitterUser object. Je kan hiervoor mUserName gebruiken, maar soms zijn bepaalde letters van gebruikersnaam in hoofdletters en soms juist niet. Als je dan wil zoeken naar een bepaalde Twitter-gebruiker weet je niet welke letters uit de naam met hoofdletters zijn. Voor het opslaan van de id kan je als oplossing een extra String onthouden die altijd de gebruikersnaam in kleine letters opslaat. Als er dan via onze Twitter-app gezocht wordt op een gebruikersnaam, veranderen we de zoekopdracht eerst naar kleine letters en zoeken we daarna verder. Hierdoor zoek je op alle gebruikersnamen en maakt het niet uit of de gebruiker hoofdletters en/of kleiner letters gebruikt. Om dit te doen willen we de gebruikersnaam in kleine letters op kunnen slaan. Dit moet in een String object gebeuren, deze String geven we de naam mId. Om aan OrmLite te laten weten dat je deze variabele als id wil gebruiken moet je een parameter meegeven in de annotatie. Het speciale aan annotaties is dat je parameters altijd een naam geeft. In dit geval heeft de parameter de naam id en is het een boolean die je de waarde true of false kan geven. Voeg deze code toe aan het begin van je class block van je TwitterUser class:Je hoeft voor deze variabele geen get-method te maken. Maar je moet er wel voor zorgen dat deze variabele gevuld wordt in je constructor. Hiervoor moet je de volgende regel code toevoegen in je constructor:Omdat we de TwitterUsers opslaan en bij het volgende keer opstarten van de app weer op kunnen vragen, moeten we bijhouden hoe oud de gegevens zijn die bij het TwitterUser object horen. Hiervoor gebruiken we een long, dat is een getal dat erg grote afgeronde getallen kan opslaan. Voeg deze code toe bovenin je class block:Voor het opslaan van dit getal gebruiken we de statische methode elapsedRealTime van de SystemClock class, dat getal geeft aan hoeveel milliseconden het geleden was dat dit specifieke Androidapparaat voor het eerst was opgestart. Wanneer we op een bepaald moment dit getal opvragen en we krijgen het getal 8888810000 terug en een tijdje daarna vragen we weer dit getal op en we krijgen 8888840000 dan weten we dat er 30 seconden zaten tussen het opvragen van deze twee getallen. Door het eerste getal op te slaan in de database (via het TwitterUser object) bij het aanmaken van een TwitterUser object en op elk willekeurig moment een nieuw getal op te vragen kunnen we uitrekenen hoe oud de gegevens zijn die bij een bepaald TwitterUser object horen. Voeg deze code toe in de constructor van je TwitterUser class:En maak ook ergens onderin je class block een get-method aan voor deze variabele:Het laatste wat je moet doen voordat je class 'OrmLite-Ready' is, is ervoor zorgen dat je class een constructor heeft zónder parameters. Dit heeft OrmLite nodig als het automatisch de objecten gaat laden vanaf je SQLite database.

  • Je hebt nog niet zo een constructor, dus voeg deze code toe in je class block (en maak hem bij voorkeur niet-public omdat je hem zelf niet nodig hebt in je code):

Nu hebben we via de annotaties genoeg informatie toegevoegd aan je class zodat OrmLite hem kan verwerken tot een tabel in je database. Voor de verwerking hiervan moet je de OrmLiteSqliteOpenHelper class subclassen, zie de volgende paragraaf.

OrmLiteSqliteOpenHelper: de verbinding tussen je classes en je database inclusief de tabellen

De OrmLiteSqliteOpenHelper class vormt de verbinding tussen de SQLite database op je Androidtelefoon en de classes die je gebruikt in je app. Maak eerst een nieuwe class aan met als superclass de OrmLiteSqliteOpenHelper class:

  • Als je niet meer weet hoe dit moet, kijk naar DevTutorial 7 bij de paragraaf "BaseAdapter subclassen in TweetsListAdapter".
  • Klik in het Package Explorer Window met de rechtermuisknop op je package me.moop.mytwitter en maak via New –> Class een nieuwe class aan.
  • Geef de class DatabaseHelper als naam en vul bij Superclass de class com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper in. Zorg ervoor dat het vinkje bij “Inherited abstract methods” aan staat en klik op Finish.
  • Voeg bovenin je class deze code toe (dus twee statisch variabelen en één constructor):

De constructor die je net hebt toegevoegd wordt aangeroepen als deze class wordt aangemaakt, dat is dus wanneer er vanuit de app behoefte is om gegevens weg te schrijven naar de SQLite-database of juist uit te lezen. Bij het aanmaken van een nieuw Object van het type DatabaseHelper wordt de constructor van de superclass aangeroepen, dat gebeurt met de regel code die begint met super. Daar worden als parameters onder andere de databasenaam en het versienummer van de database meegegeven. Deze twee parameters kan je zelf veranderen door de statische variabelen aan te passen die boven je constructor staan. Je geeft een naam van de database mee zodat je controle hebt over in welk bestand de SQLite database wordt opgeslagen. Het SQLite-bestand komt in een afgesloten map op je Androidapparaat die alleen toegankelijk is voor je jouw Androidapp. Deze map wordt automatisch verwijderd als je de app verwijdert (maar hij blijft staan bij een update van de app). Het meegeven van een versienummer wat je doet via de statische variabele DATABASE_VERSION is belangrijker: hiermee geef je aan of je de annotaties van je TwitterUser class (of een andere class die je gebruikt in OrmLite) hebt aangepast. Zodra je een annotatie aanpast (weghalen, toevoegen, parameters veranderen) of een class toevoegt of verwijdert van OrmLite, moet je het getal DATABASE_VERSION ophogen naar het daaropvolgende getal. Dit heeft ermee te maken dat je niet zomaar je annotaties of classes die voor je OrmLite gebruikt mag aanpassen: als je dat aanpast dan moeten namelijk ook de tabellen worden aangepast. Bij SQLite betekent dat in het eenvoudigste geval dat je database opnieuw moet worden opgebouwd, zodat hij de nieuwe annotaties en classes kan opslaan. Doe je dit niet dan krijg je bijvoorbeeld deze fout:De fout hierboven kreeg ik toen ik vergeten was het getal op te hogen en mLastUpdate had toegevoegd (ik was toen bezig met de code van deze DevTutorial). Dit is alles wat je moet onthouden over de constructor van deze class. De eerste method die automatisch voor je is gegenereerd is de onCreate method. Deze method wordt aangeroepen als er een nieuwe database wordt aangemaakt. Dit aanmaken gebeurt als er nog geen SQLite database was. In deze method moet je de tabellen aanmaken die je nodig hebt voor je database. In dit geval moet je de tabel aanmaken voor je TwitterUser class. Het aanmaken van een tabel doe je met deze code:De enige regel code in het try block zorgt voor het aanmaken van de tabel voor de TwitterUser class. Wat hier gebeurt is dat er een method wordt aangeroepen die voor jou automatisch een tabel aanmaakt. Het is nu niet belangrijk waar de method vandaan komt of wat connectionSource doet. Je moet wel begrijpen dat als tweede parameter van deze method-aanroep het Class type van de TwitterUser class wordt meegeven. Door de naam van de class te schrijven en daarachter .class te gebruiken maak je een verwijzing naar de class zelf, zonder er een object of iets anders van te maken. Hierdoor krijg je een object van het type Class<T> (in dit geval van het type Class<TwitterUser>) en dit object bevat informatie over de class zelf: onder andere welke variabelen er zijn en welke annotaties daarbij zijn geschreven. Dit wordt dus gebruikt door OrmLite om automatisch een tabel voor je TwitterUser class aan te maken. Dit alles is omringd door een try catch block waar in het catch block een nieuwe Exception wordt aangemaakt. Een Exception van het type RuntimeException hoef je niet op te vangen met een catch block, dit zorgt ervoor dat je hiermee je app kan laten crashen. Dit doe je meestal alleen bij ernstige fouten: bijvoorbeeld als het aanmaken van je database niet goed gaat (een vrij essentieel iets).

  • Voeg bovenstaande code voor het aanmaken van een nieuwe tabel toe in je method onCreate.

Ongeveer hetzelfde geldt voor de method onUpgrade. Deze method wordt aangeroepen als je het getal DATABASE_VERSION hebt opgehoogd. Voor nu kiezen we ervoor om in deze method alle tabellen te verwijderen (met de method dropTable) en daarna de method onCreate aan te roepen zodat alle tabellen opnieuw worden aangemaakt. Als je in de toekomst je gegevens wil blijven behouden tijdens het veranderen van DATABASE_VERSION moet je in deze method je code schrijven die de tabellen aanpast in plaats van ze verwijdert.

  • Voeg deze code toe in je method onUpgrade:

Je weet bijna het broodnodige over OrmLite. Het laatste is de Dao<T, ID> interface.

De Dao<T, ID> interface

De Dao<T, ID> interface draait om het opvragen en bewerken van objecten van classes die je opslaat in de SQLite-database. Zoals je van de vorige DevTutorial hebt geleerd, is een interface een beschrijving van welke methods er in een class aanwezig moeten zijn. Zodra je weet dat een object waar je mee werkt een interface implementeert, weet je welke methods je er sowieso op kan aanroepen, zonder dat je weet welk object het nou precies is. Zoals je ziet kan je ook bij de Dao<T, ID> interface aangeven dat je hem met een type van een bepaalde class wilt gebruiken, maar in dit geval gaat het om het aangeven van twee types: T en ID. Het type T betekent het type van het object dat je wil opvragen/bewerken met de Dao<T, ID> interface. Dus zodra je een object hebt die ook de interface Dao<TwitterUser, ID> implementeert, dan heb je onder andere de method create tot je beschikking: Maar omdat je voor type T als type TwitterUser gebruikt, verandert deze method in de volgende method: Op dezelfde manier heeft de Dao<T, ID> interface ook methods voor het verwijderen (delete) en aanpassen (update) van een object uit de database. Een andere belangrijke method van de Dao<T, ID> interface is de method queryForId: Deze method gebruik je door het id van het object wat je wil opvragen mee te geven. Het resultaat van deze method is het object dat je aan de hand van zijn id hebt opgevraagd. Het id is, zoals je daarstraks las, de unieke code voor een object waar je mee werkt in een tabel. Voor het TwitterUser object hebben we ingesteld dat de id een String is waar we de Twitter-gebruikersnaam in kleine letters in opslaan. Als we een een Dao<T, ID> gebruiken als Dao<TwitterUser, String> dan krijg je voor queryForId de volgende method: Wanneer je dus een Dao<T, ID> interface gebruikt, dan geef je als extra types mee het type T van de class waarvoor je objecten van de SQLite database wil opvragen en het type ID dat beschrijft van welk type de id (identificatie) variabele is die bij de class hoort. Als je een Dao<T, ID> wilt gebruiken met de TwitterUser class, dan moet je werken met de Dao<TwitterUser, String> variant van de Dao<T, ID> interface. De Dao's kan je aanmaken in je DatabaseHelper class.

  • Je moet hiervoor eerst in het class block van de DatabaseHelper class een variabele toevoegen van het type Dao<TwitterUser, String> met de naam mTwitterUsersDao:

Daarna moet je een method aanmaken die deze Dao teruggeeft en aanmaakt als dat nodig is. Het aanmaken gebeurt via de method getDao van OrmLiteSqliteOpenHelper class waar je wederom een beschrijving van de TwitterUser class meegeeft via het Class<T> object.

  • Voeg deze method toe aan je DatabaseHelper class:

Voeg ook deze code toe in je class block die ervoor zorgt dat je mTwitterUsersDao object op null wordt gezet als de DatabaseHelper gesloten wordt, dit zorgt voor een betere benutting van het geheugen op je Androidapparaat:Nu is de class DatabaseHelper klaar voor gebruik.

Dao<TwitterUser, String> gebruiken in MainActivity

Om de DatabaseHelper te gebruiken in je Activity kan je het beste hiervoor een variabele aanmaken in je class block van je MainActivity class.

  • Voeg de volgende code toe in MainActivity:

Als je een object van de DatabaseHelper class wil hebben, moet je dat niet doen via de constructor van die class, maar via de statische getHelper method van de OpenHelperManager class. Die method zorgt ervoor dat er niet onnodig meerdere DatabaseHelper objecten worden aangemaakt. Dit zou namelijk teveel geheugen kosten. Voor het aanmaken van een DatabaseHelper object heeft de getHelper method een Context object nodig en een Class<T> object die een beschrijving is van de DatabaseHelper class. Als Context object kan je het woordje this gebruiken dat verwijst naar de huidige class (de Activity class is een indirecte subclass van de Context class).

  • Voeg de volgende method toe aan je MainActivity class zodat je een method hebt die je een DatabaseHelper object kan geven:

Om goed om te gaan met het geheugen van het Androidapparaat kan je verder nog het beste het object mDatabaseHelper weer leeg maken als je hem niet meer nodig hebt, anders blijft hij onnodig lang rondzweven. Hiervoor kan je de method onDestroy() gebruiken van de Activity class. Deze method wordt aangeroepen zodra je Activity wordt afgesloten. Als je deze method overschrijft ('override') dan wordt je method uitgevoerd als je Activity wordt afgesloten. Je moet niet vergeten de method onDestroy van de superclass nog aan te roepen in je method. Anders krijg je namelijk een foutmelding omdat in de method onDestroy van de superclass nog essentiële code moet worden uitgevoerd om de Activity netjes af te sluiten.

  • Voeg deze method toe aan je class:

Nu moet je een aparte method in je Activity aanmaken die het gedownloade TwitterUser object kan opslaan in je SQLite database.

  • Maak een method saveToDbAndShow die als parameter een String ontvangt. Als eerste regel in de method moet je zorgen dat mTwitterUser gevuld wordt door de constructor van het TwitterUserObject aan te roepen:

  • Nu moet je eerst de DataBaseHelper opvragen via de method getDatabaseHelper. En tegelijk daar achteraan kan je ook de method getTwitterUsersDao aanroepen zodat je een Dao<TwitterUser, String> object ontvangt. Deze kan je opslaan in de variabele twitterUsersDao:

  • Roep op het twitterUsersDao object van het type Dao<TwitterUser, String> de method createOrUpdate aan die een nieuw TwitterUser opslaat in de database of de TwitterUser met dezelfde id (Twitter-gebruikersnaam in dit geval) aanpast:

  • Je krijgt een suggestie voor een try catch block. Voeg deze toe en roep ook  de method updateView aan zodat het scherm wordt ververst met de laatste gegevens.
  • Je method saveToDbAndShow moet er nu als volgt uit zien:

  • Zorg er ook voor dat in de method onPostExecute van de inner class DownloadUserInfoTask de juiste method wordt aangeroepen. Een method-aanroep naar updateView is niet meer nodig omdat dat nu in de method saveToDbAndShow gebeurt:

Je TwitterUser object wordt vanaf nu netjes opgeslagen in je SQLite database. Het laatste wat je moet doen is voordat het downloaden wordt gestart, kijken of je nog een TwitterUser in je SQLite database hebt. Als je die hebt en hij is niet ouder dan tien minuten (1000 milliseconden * 60 seconden * 10 minuten  = 600000 milliseconden), dan hoeft er niet gedownload te worden en wordt de gevraagde Twitter-gebruiker meteen getoond vanuit je SQLite database.

  • Maak de method downloadOrShowFromDb die een String met de naam username als parameter accepteert. In de eerste regel van de method moet je een TwitterUser object definiëren met de naam twitterUser die je meteen waarde null meegeeft:

  • Vraag net zoals in de method saveToDbAndShow een Dao<TwitterUser, String> object op via de method getTwitterUsersDao. Geef de variabele de naam twitterUsersDao:

  • Gebruik de method queryForId op het Dao<TwitterUser, String> object met de naam twitterUsersDao en stop het resultaat ervan in twitterUser. En geef als parameter de string username mee waarvan je met de method toLowerCase nog snel een kleine letter-variant opvraagt:

Als er al een TwitterUser aanwezig was in de SQLite database, dan is het object twitterUser van het type TwitterUser gevuld met een TwitterUser. Bestond er nog geen TwitterUser met de gevraagde username, dan heeft twitterUser de waarde null. In een if block kan je vragen of twitterUser null is of niet. Is dat niet het geval, dan kan je vragen of twitterUser ouder is dan 10 minuten. Dat kan je doen met de volgende regel:

Doordat eerst gevraagd wordt of twitterUser niet de waarde null heeft, en daarna pas de leeftijd van de twitterUser wordt opgevraagd, kan altijd de leeftijd worden opgevraagd. Als twitterUser de waarde null heeft, dan wordt namelijk niet geprobeerd de tweede vraag te beantwoorden. Als je de vragen tussen de haakjes '(' en ')' zou omkeren, dan wordt soms ook gevraagd aan twitterUser wat zijn leeftijd is terwijl hij dan de waarde null heeft. In dat geval crashet je app. Vandaar dat deze vragen in deze specifieke volgorde staan.

Als twitterUser inderdaad gevuld is en hij is niet ouder dan tien minuten, dan mag twitterUser toegewezen worden aan mTwitterUser en mag updateView worden aangeroepen. Is dit niet het geval dan wordt het 'normale' downloadproces opgestart inclusief het tonen van de ProgressDialog.

  • Probeer te kijken of je deze method zelf af kan maken.
  • En pas de method downloadUserInfo aan zodat hij de method downloadOrShowFromDb aanroept.

Uiteindelijk kan je op dit uitkomen voor de method downloadOrShowFromDb:En dit voor de method downloadUserInfo:

Extra oefening

We gaan de AutoCompleteTextView gebruiken zodat er suggesties worden weergegeven als de gebruiker een Twitter-gebruikersnaam invoert. De lijst van suggesties bouwen we op basis van de Twitter-gebruikers die eerder ingevoerd waren: .

  • Het eerste dat je moet doen is naar je layout-bestand nicelayout.xml gaan en een AutoCompleteTextView toevoegen op de plek die nu nog de TextView etxtUsername is. Je moet alle properties over nemen van de oude EditText, en je moet de id property en de Completion threshold property deze waarden geven: . Door de Completion threshold property aan te passen zorg je ervoor, dat als er één letter is ingetypt, de suggesties al worden getoond.
  • In MainActivity moet je de variabele mEtxtUsername verwijderen en in plaats daarvan een variabele van het type AutoCompleteTextView gebruiken met de naam mActxtUsername (je moet op 3 plekken in je MainActivity class mEtxtUsername vervangen door mActxtUsername).
  • Voeg de volgende code toe in je method updateViewin je MainActivity class, nog voor het if block. Met deze code vraag je alle TwitterUser objecten op die in de database zitten via de Dao<TwitterUser, String>. Daarmee maak je vervolgens een simpele variant van de Adapter class als adapter voor de AutoCompleteTextView:
  • Voeg deze method toe aan je TwitterUser class. Als je een simpele adapter gebruikt, dan wordt deze method gebruikt om de tekst per TwitterUser object op te vragen:
Nu weet je ook hoe je een AutoCompleteTextView kan gebruiken.

Sourcecode en Forum

Als je vragen hebt over deze DevTutorial dan kan je ze stellen aan mij en andere programmeurs in deze forum thread. Als je wil weten hoe de code van deze app er uiteindelijk uit moet zien, kan je hier de sourcecode bekijken op github.

Spelfouten, taalfouten of inhoudelijke fouten ontdekt? Stuur dan een mailtje naar de auteur van dit artikel!

Reacties (10)
Bezig met laden van reacties...