All posts by tommy

OUYA, Android-konsoli

Kesälomalta paluu sorvin ääreen voi olla raskasta tai hauskaa… Kesälomalla hankittujen lelujen kanssa siitä onneksi saa hauskaa.

OUYA on Kickstarter-rahoituksen avulla luotu Android 4.1 -konsoli, jossa pyörivät kaikki normaalit Android-applikaatiot ilman suurempaa vaivaa. Konsoliin saa helposti ladattua SDK:n, jonka avulla ohjainta voi käyttää applikaatioissaan, mutta hiirellä ja näppäimistölläkin voi konsolia suoraan käyttää.

OUYA ei ole kovin tehokas konsoli eikä pelitarjonta ole vielä kovin laaja, tätä artikkelia kirjoitettaessa vain 384 peliä, mutta 99$ hintansa puolesta se on hyvä hankinta arcade pelien pelaamiseen olohuoneen sohvalla kavereiden kanssa.

Yrityksille OUYA tarjoaa halvan ja helpon ratkaisun tarjota interaktiivista sisältöä asiakasnäyttöihin. Valmiin applikaation kääntäminen ja asentaminen OUYA:lle on hyvin helppoa. Kehittäminen OUYA-konsolille ei ole yhtään sen vaikeampaa kuin mille tahansa muulle Android-laitteelle. Toki pieniä muutoksia joudutaan tekemään käyttöliittymään, jotta normaalisti kosketusnäytölle tarkoitettua applikaatiota voi käyttää peliohjaimella tai hiirellä. Kosketusnäytön korvaaminen hiirellä tai muulla ohjaimella johtaa helposti hyvin heikkoon käyttökokemukseen.

 

Asentaaksesi minkä tahansa applikaation OUYA:lle pitää sinun voida ladata applikaation .apk-tiedosto Internetistä. Mene OUYA:n kotinäkymän MAKE-valikkoon ja sisäänrakennetulla Internet-selaimella lataa APK-tiedosto. Tämän jälkeen palaa päävalikkoon, mene MANAGE-valikkoon, valitse SYSTEM ja siellä valitse ADVANCED. Tämä on jokaiselle Android-käyttäjälle tuttu asetukset-valikko Androidissa. Mene listassa alaspäin ja valitse Storage, jonka sisällä valitse Download. Täältä löydät juuri lataamasi APK-paketin ja asentaminen onnistuu ohjaimen O-napilla. Tämän jälkeen applikaatio löytyy samasta MAKE-valikosta kuin OUYA:n Internet-selain.

Samaan MAKE-valikkoon tulevat myös applikaatiot, jotka itse kehität ja asennat tietokoneen ADB:n avulla microUSB:n kautta.

Kehittäminen OUYA:lla

Kaikki OUYA-konsolit toimivat applikaatioiden kehittämiseen ilman maksuja tai muutoksia konsoliin. Jotta saat OUYA:n toimimaan normaalissa Android-kehitysympäristössäsi Macilla sinun täytyy vain lisätä kolmannen osapuolen USB Hex ID (0x2836) adb_usb.ini -tiedostoon .android-hakemistossa ja käynnistää ADB uudelleen. Tarkemmat ohjeet myös Windows-koneille on katsottavissa videona OUYA developers -sivustolta. Tämän jälkeen OUYA-konsolisi näkyy Eclipsessä ja ADB:ssä samalla tavalla kuin mikä tahansa muu Android-laite.

Seuraavaksi lataa OUYA Development kit (ODK) osoitteesta https://devs.ouya.tv/developers/odk, kopioi libs\-hakemistosta ouya-sdk.jar ja lisää se omaan Android-projektiisi. Tämän jälkeen ohjaimen komennot saa kiinni oman Activity -luokkasi onKeyDown() ja onGenericMotionEvent() -metodeista.

Tein tätä artikkelia varten yksinkertaisen applikaation, joka käyttää ODK:ta siirtelemään ImageView:tä FrameLayoutin sisällä.

Ota ohjain käyttöön Activity-luokan onCreate()-metodissa:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	OuyaController.init(this);
}

Tunnista ohjaimen komennot:

@Override
public boolean onKeyDown(final int keyCode, KeyEvent event) {
	switch (keyCode) {
		case OuyaController.BUTTON_O:
			Log.d(TAG, "OUYA button O");
			break;
		case OuyaController.BUTTON_U:
			Log.d(TAG, "OUYA button U");
			break;
		case OuyaController.BUTTON_Y:
			Log.d(TAG, "OUYA button Y");
			break;
		case OuyaController.BUTTON_A:
			Log.d(TAG, "OUYA button A");
			break;
	}
	return true;
}
@Override
public boolean onGenericMotionEvent(final MotionEvent event) {
// Joystick
	if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
		float LS_X = event.getAxisValue(OuyaController.AXIS_LS_X);
		float LS_Y = event.getAxisValue(OuyaController.AXIS_LS_Y);
	}
	return true;
}

Tämän jälkeen voit tehdä ohjaimen antamilla komennoilla mitä itse haluat.
Koko koodin ja Eclipse projektin saat ladattua Githubista:
https://github.com/Androidkehitys/OUYA

Onnea pelikehitykseen! Palataan leikkimään telkkujen kanssa, kun Cromecast saapuu postissa.

Android-listat ja -adapterit

Näyttäessämme tietoa laitteen näytöllä joudumme usein turvautumaan listoihin. Emme usein tiedä kuinka monta riviä listassa on ja ne voivat ohjelman ajoaikana muuttua. Lähes jokaisessa ohjelmassa joutuu käyttämään jotain listaa, gridiä, galleriaa tai jotain muuta Androidin AdapterViewn toteuttavaa luokkaa.

Kaikki luokat jotka jatkavat AdapterViewtä on tarkoitettu näyttämään tietoa selkeissä riveissä (list), sarakkeissa (gallery) tai ruudussa (grid). AdapterView tarvitsee toimiakseen Adapterin, jolle on annettu tarvittavat objektit. Adapterin ei kuitenkaan tule itse hallinnoida objekteja vaan säilyttää vain viittaus niihin. Objektien säilyttäminen on tämän artikkelin asian ulkopuolella. Tässä artikkelissa käydään läpi kuinka Adapterit toimivat ja mikä on paras käytäntö niiden tekemisessä. Kiinnitämme myös huomiota listan ja sen myötä myös adapterin muistin kulutukseen, sillä esimerkiksi kuvien näyttäminen listassa aiheuttaa herkästi muistin ylittämisen ja ohjelman kaatumisen.

Kuinka tehdä Adapteri

Adapteria tehdessä suosittelen käyttämään yliluokkana ArrayAdapter<näytettävä objekti> luokkaa, joka toteuttaa jo lähes kaikki tarvitsemasi metodit. Sinun tarvitsee vain käsitellä, miten jokainen adapterille antamasi objekti näytetään getView()-metodissa.

Toimivan adapterin tekemiseen sinun täytyy jatkaa ArrayAdapteria ja toteuttaa tälle adapterille construktori ja haluamasi lainen getView()-metodi. Tässä esimerkki yksinkertaisesta adapterista, joka näyttää objektin tekstiä jokaisella adapteria käyttävän AdapterView:n alkiossa.

public class TestAdapter extends ArrayAdapter<YourObject> {
public TestAdapter(Context context, int textViewResourceId, List<YourObject> objects) {
    super(context, textViewResourceId, objects);
} 

// Luo AdapterViewn ilmentymän alkio tallennetusta objektista
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // Adapteri uudelleen käyttää listassa olevia näkymiä uudelleen,
    // mutta listaa luotaessa nämä ovat null:ja ja tarvitsee alustaa.
    // Jos listan näkymä on jo asetettu ei sitä tarvitse enää uudelleen luoda
    // tehokkaassa adapterissa tämä luonti jätetään väliin, kun sitä ei tarvita.
    if (convertView == null) {
        LayoutInflater inflater = LayoutInflater.from(getContext());
        convertView = inflater.inflate(R.layout.your_adapter_view_layout, null);
    }
    // tämän jäälkeen tarvitsee vain ottaa oikeasta adapterin alkiosta tarvittavat tiedot
    //  ja asettaa ne näkymään oikeille paikoille.
    YourObject item = getItem(position);
    // Huomaa että tekstikentän etsiminen vie turhaan tehoja ja aiheuttaa tarpeettomia viittauksia
    TextView text = (TextView)
    convertView.findViewById(R.id.adapter_item_textview);
    text.setText(YourObject.getText()); 

    return convertView; }
}

 

ViewHolder pattern: Lisää adapterin suorituskykyä

ViewHolder-malli tehostaa Adapterien käyttöä poistamalla tarpeen etsiä tarvittavat näkymä widgetit uudelleen getView()-metodissa. Mallin idea on luoda uudelleen käytettävä, Adapterin sisäinen, ViewHolder luokka, joka asetetaan uudelleenkäytettävän convertViewn sisälle tagiin. ViewHolder pitää sisällään referenssit oman convertViewnsä käytettyihin widgetteihin. Tämä poistaa widgettien etsimisen findViewById()-metodilla.

Myös jos tekstikentille tarvitsee lisätä kuuntelijoita riittää että ne tehdään vain kerran ViewHolderia luotaessa. Adapterisi toteuttaa ViewHolder mallin esimerkiksi näin:

// Luo AdapterViewn ilmentymän alkio tallennetusta objektista
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        LayoutInflater inflater = LayoutInflater.from(getContext());
        convertView = inflater.inflate(R.layout.your_adapter_view_layout, parent, false);

        // Luo ja lisää ViewHolder omalle näkymälleen tagiksi
        convertView.setTag(new ViewHolder(convertView));
    }
    ViewHolder viewHolder = (ViewHolder) convertView.getTag();
    YourObject item = getItem(position);

    viewHolder.text.setText(item.getText());
    viewHolder.image.setImageDrawable(item.getImageDrawable());

    return convertView;
}

private static class ViewHolder {
    final TextView text;
    final ImageView image;

    public ViewHolder(View convertView) {
        text = (TextView) convertView.findViewById(R.id.adapter_item_textview);
        image = (ImageView) convertView.findViewById(R.id.adapter_item_imageview);
    }
}

Monimutkaisemmissa riveissä ViewHoldering käyttäminen helpottaa ja tehostaa listan käyttämistä, koska vaikka oikean näkymän etsiminen convertView:stä kestää vain muutamia millisekuntteja, jos sen tekee esimerkiksi kymmenen kertaa voi lista nykiä selvästi kovassa käytössä.

Vinkki:

Listat ovat hyviä näyttämään tietoa, mutta vältä tiedon muokkauksen mahdollistamista listojen tai muiden AdapterView:n sisällä, esimerkiksi EditText widgeteillä. Tallentaminen ja oikean tiedon näyttäminen, kun listan näkymiä uudelleenkäytetään tulee todella vaikeaksi.

Rahaa applikaatioilla

Hauskojen ja hyödyllisten applikaatioiden tuottaminen Androidille ei tarvitse olla pyyteetöntä työtä. Applikaatioilla voi myös pieni kehittäjä tehdä rahaa. Androidilla on kolme yleistä talousmallia, joilla voit tienata applikaatiollasi.

1) Maksulliset applikaatiot

Applikaatiolle voi asettaa hinnan, kun sen laittaa markettiin myyntiin. Ongelma on että suurin osa applikaatioista on saatavilla myös ilmaiseksi. Asiakaskuntaa voi kasvattaa tarjoamalla esimerkiksi rajoitetun version applikaatiosta ilmaiseksi.

2) Applikaation sisäiset ostokset

Harvalla yksittäisellä koodaajalla on mahdollisuutta perustaa omaa kauppaa, mistä applikaation käyttäjä voi ostaa applikaatioon uutta sisältöä. Mikrotransaktiot toimivat hyvin peleissä kuten League of Legends. Android market tukee applikaation sisäistä maksamista ja lisää tietoa löytyy Android developer -sivustolta. Google ottaa 30% maksusta.

3) Mainokset applikaatiossa

Google tarjoaa valmiita paketteja mainosten lisäämiseen applikaation AdMob yrityksen kautta. Tässä artikkelissa käsitellään, kuinka mainoksia saa lisättyä omaan applikaatioonsa. Tässä artikkelissa neuvotaan, kuinka voit lisätä mainoksia omaan Android-applikaatioosi.

Mitä tarvitset?

Valmis applikaatio marketissa. Tarvitset AdMob:lle applikaation nimen ja linkin apk-pakettiin, mieluiten muodossa market://details?id=<app.package.name>

AdMob käyttäjätunnus:

Aloita täältä: Google Mobile Ads

Tässä kohtaa ainoa hämmennystä aiheuttava kohta yksityiselle Android-kehittäjälle voi olla Business name -kohta. Kaikki nämä tiedot voi vaihtaa koska tahansa. Jos et osaa täyttää jotain kohtaa ja se on pakollinen voit laittaa siihen väliaikaisen tiedon, tämä on myös AdMobin oma suositus. Verotiedot voi jättää tässä vaiheessa tyhjäksi, mutta muista ilmoittaa verottajalle AdMoblta saamasi tulot.

Tämän jälkeen voit lisätä applikaation “Sites & Apps” valikosta. Applikaation tietoja täyttäessäsi voit lisätä keksityt tiedot aluksi, jos haluat vain ladata ja testata SDK:n lisäämistä applikaatioosi. Voit myöhemmin päivittää nämä tiedot, kun olet lisännyt applikaatiosi markettiin.

Tuottojen maksu:

Mainoksista saamiesi tuottojen maksuun suositellaan PayPalia Yhdysvaltojen ulkopuolella. Tulot on mahdollista saada myös suoraan tilillesi, mutta pankkisi saattaa periä ylimääräisiä kuluja kansainvälisistä tilisiirroista. Huomaa myös että tulot maksetaan dollareina ja pankkisi voi periä ylimääräisiä kuluja euroiksi muuttamisesta.

AdMob maksaa jokaisesta kerrasta, kun applikaatiosi mainoksia painetaan. Maksu vaihtelee monesta asiasta riippuen, mutta on joitakin senttejä per painallus.

AdMob SDK applikaatioosi

Tarvittavan SDK:n löydät täältä: http://code.google.com/mobile/ads/docs/
Samasta linkistä löytää tarvittavat ohjeet mainosten lisäämiseen. Käymme tässä läpi pääkohdat.

1) Lisää SDK applikaatioosi

2) Lisää Manifestiin

2.1) Mainos aktiviteetti:

<activity android:name="com.google.ads.AdActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>

2.2)

Lisää permissionit:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

3) Lisää AdView layoutiisi

<com.google.ads.AdView android:id="@+id/adView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
ads:adUnitId="YOUR_ADMOB_AD_UNIT_ID"
ads:adSize="BANNER"
ads:testDevices="TEST_EMULATOR,e78b974e6ab65648b52951e69edcb571"
ads:loadAdOnCreate="true"/>

TestDevices-atributtiin lisätään laitteen device ID ja tämän avulla saat testimainoksia laitteellesi. Poista tämä markettiin menevästä versiosta.

Tämä mainos ladataan heti, kun aktiviteettisi luodaan.
Voit itse ladata uuden sisällön mainosnäkymään:

AdView adView = (AdView) findViewById(R.id.adView);
adView.loadAd(new AdRequest());

4) ???

(Olet valmis, markkinoi applikaatiotasi sosiaalisessa mediassa)

5) Profit

Testaus Androidilla

Testaus yleisesti

Testaus, tuo ohjelmistokehityksen välttämätön paha. Testausta ei koskaan voi suorittaa täysin kattavasti, eikä ole ohjelmistoa, jossa ei olisi yhtään virhettä, paitsi ehkä Nasalla. Testauksella kuitenkin kasvatetaan merkittävästi ohjelmiston laatua ja viimeistään työelämässä tämä osa-alue on hyvä hallita.

Testaus, niin kuin kaikki muukin välttämätön epämukavuus on hyvä suorittaa kehityksen ohella, kuin sen jälkeen. On paljon helpompaa testata se mitä on juuri tehnyt, kuin kaikki mitä on tehty kehitysvaiheen aikana. Testaus kehityksen yhdeydessä  tekee koodista myös helpommin testattavaa. Jälkikäteen testattaessa voi hyvinkin olla mahdotonta tehdä testejä ilman koodin muuttamista.

Tässä artikkelissa käymme läpi Androidin testivälineitä ja -käytäntöjä.

Testit on hyvä suunnitella yksinkertaisiksi, toistettaviksi ja mahdollisimman vähän aikaa vieviksi. Hyvä ratkaisu tähän on testikoodi, jotka voi sitten vain ajaa tarvittaessa. Jokaista loppukoodin luokkaa kohden on hyvä tehdä oma testiluokkansa, joka testaa luokan julkiset metodit “riittävällä” testijoukolla. Hyvä nyrkkisääntö on, ettei testimetodeita ole koskaan liikaa, eikä yhden metodin kannata kaikkea tehdä.

Yksikkötestaus JUnitilla

Eclipseen tulee Android SDK:n mukana valmis wizard Android JUnit testiprojektien luomiseen. Yksityiskohtainen ohje JUnit testien tekemiseen ja ajamiseen löytyy developer.android.com sivustolta: Hello, Testing. Käymme muutaman yksityiskohdan läpi, oman testikoodin ja testisalkun avulla. Testisalkku on kokoelma testiluokkia, jotka suoritetaan peräkkäin. Jokaisen testiluokan voi suorittaa myös erikseen.

Android developers sivuston esimerkissä on huonosti mainittu, että testi projektiin pitää muistaa lisätä linkki testattavaan projektiin. Voit lisätä tämän projektin asetuksista (properties), “Java build path” valikosta, “projects”-välilehdestä. Tai testiprojektia luotaessa heti toisessa valikossa, joten muista painaa “next” äläkä “finnish”. Developer sivuston esimerkki on myös hiukan vanhemmasta Android SDK:sta, mutta sisältö on sama, vain luonti wizard on yksinkertaistunut.

- Testisalkku:

package fi.androidkehitys.esimerkkiapp.test; import junit.framework.Test; import junit.framework.TestSuite; public class AllTests { /** * Testisalkku, missä kaikki testiluokat lisätään. * @return Test */ public static Test suite() { TestSuite suite = new TestSuite(AllTests.class.getName()); suite.addTestSuite(EsimerkkiTesti.class); return suite; } }

Testisalkun voi ajaa suoraan “run as” ja “Android JUnit test”.

- Testiluokka:

package fi.androidkehitys.esimerkkiapp.test;

import fi.androidkehitys.applikaatio.MyActivity;
import java.util.List;
import android.test.ActivityInstrumentationTestCase2;

public class RSSTest extends ActivityInstrumentationTestCase2<MyActivity> {

    public RSSTest() {
        super("fi.androidkehitys.applikaatio", MyActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
    }

    /**
    * Testi metodi.
    *
    */
    public void testPotenssiMetodiOne() {
        assertTrue(“Metodi parametrillä 1 palautti väärän arvon”, potenssiMetodi(1) == 1);
    }
    public void testPotenssiMetodiTwo() {
        assertTrue(“Metodi parametrillä 2 palautti väärän arvon”, potenssiMetodi(2) == 4);
    }

    @Override
    protected void tearDown() throws Exception {
        getActivity().finish();
        super.tearDown();
    }
}

Testiluokan kaikki julkiset metodit ovat testi tapauksia ja suoritetaan testauksessa. Tässä testiluokassa ei tarvitsisi käyttää aktiviteettiä vaan tehdä yksinkertainen TestCase-luokan ilmentymä, mutta usein Android-metodit voivat tarvita kontekstin tai pääsyn resursseihin, joten yleisin yleisimmissä testitapauksissa tarvitset aktiviteetin parametrien käyttöön.

Aktiviteettien testaus on hiukan ongelmallisempaa yksikkötesteissä, koska view-komponenttejä ei saa käsitellä UI-säikeen ulkopuolella. Ratkaisu tähän on seuraavassa kappaleessa esiteltävä black box testaus ja Robotium framework.

Black box testaus Robotiumilla

Black box testauksella tarkoitetaan testausta, missä ei tarvitse pääsyä koodiin, vaan painellaan laatikon nappuloita. Robotiumille riittää päästä käsiksi applikaation .apk pakettiin. Black box testauksella pystyy tekemään monimutkaisiakin käyttötapaustestejä, mutta yksinkertaisuus on yleensä parempi tavoite sillä monimutkaiset testit alkavat kaatua pienilläkin muutoksilla.

Android tarjoaa rasitustestaukseen oman Monkey Runner:in, joka simuloi satunnaisia, mutta toistettavia painalluksia applikaation näyttöön. Tällä on kuitenkin mahdotonta testata jotain tiettyä tarkasti määriteltyä ominaisuutta applikaatiossa.

Robotium on voimakas työkalu Andoridin black box testaukseen. Sen käyttö ei vaadi kattavaa tietoa applikaation koodista ja käyttö on hyvin suoraviivaista. Robotiumilla voi ActivityInstrumentationTestCase2-luokkaan luoda Solo-olion, joka käyttää ja testaa käyttöliittymäelementtejä.

@Override
protected void setUp() throws Exception {
    super.setUp();
    solo = new Solo(getInstrumentation(), getActivity());
}

public void testLogin() {
    solo.clickOnButton("Kirjaudu sisään");
    assertTrue(solo.waitForText("Kirjautuminen epäonnistui", 1, 120000));
}

@Override
protected void tearDown() throws Exception {
    try {
        solo.finalize();
    } catch (Throwable e) {
        e.printStackTrace();
    }
    getActivity().finish();
    super.tearDown();
}

Kun Solo:lle on annettu Instrumentaatio ja aloitus-aktiviteetti voi navigoinnin applikaatiossa hoitaa kokonaan Solo:lla. Voit myös mennä toisiin aktiviteetteihin ilman ongelmia, jos testitapaus tätä vaatii.

Lisää JavaDoc Robotiumiin

Robotiumin käyttö helpottuu huomattavasti, kun lisäät JavaDocin Robotiumin lähdekoodiin. Sekä lähdekoodin, että JavaDocin voit ladata täältä (uusimmat tiedostot listan alapäässä). Eclipsessä testiprojektin asetuksissa (properties), Java Build Path, Libraries ja lisättyäsi Robotium Solo .jar tiedosto avaa se vasemmassa laidassa olevasta nuolesta, valitse Javadoc location ja paina edit. Tämän jälkeen valitse ulkoinen tiedosto ja etsi Robotium javadoc .jar ja lisää se archive pathiin. Nyt näet Robotiumin JavaDocit Eclipsessä.

Widgetit Androidissa

Widgetit näyttävät applikaation kaikkein oleellisimman tiedon nopeasti ja yksinkertaisesti työpöydällä. Widgettejä voi myös käyttää käynnistämään applikaatioita tai taustaprosesseja, kuten mediasoittimen.

Tässä artikkelissa toteutamme mahdollisimman yksinkertaisen widgetin, jotta saamme idean mitä kaikkea sen toimintaan saamiseen tarvitaan. Toteutamme widgetin, jossa on tekstikenttä ja nappula, nappulaa painamalla kasvatamme tekstikentässä olevaa numeroa yhdellä. Erityisesti kiinnitämme huomiota widgetin tilan päivittämiseen sekä manuaalisesti, että automaattisesti.

Esimerkki koodi: https://github.com/Androidkehitys/Androidkehitys-Widget

Widget layout

Widgetin layout toteutetaan samalla tavalla, kuin minkä tahansa aktiviteetin layout. On kuitenkin otettava huomioon, että widgetti luodaan RemoteViews-luokan ilmentymänä ja layoutissa voi näin ollen käyttää vain seuraavia layout-elementtejä:

Layoutit

Widgetit

AppWidgetProviderInfo Metadata

Tämä XML-tiedosto määrittää widgetille tarpeelliset tiedot, kuten korkeuden ja leveyden, päivitysvälin, esikatselun, layoutin, asetukset-aktiviteetin sekä widgetin koon muuttamisen vaaka ja/tai pystysuunnassa (Android 3.1).

Androidin kotinäkymä koostuu ruudukosta, jossa jokainen ruutu on kooltaan 74dp * 74dp. Widgetin tulee jättää 2dp:tä tilaa leveydessä ja korkeudessa, jotta vältetään mahdolliset virheet ajonaikaisessa koon määrityksessä kun dp:t muutetaan pikseleiksi. Käytä kaavaa (solujen määrä * 74) – 2 widgetin koon määrittelemiseen.

Päivitysväli määrittää ajan, jonka välein Android päivittää widgetin tiedot. Tämä päivitys herättää laitteen lepotilasta ja suorittaa onUpdate()-metodin AppWidgetProvider-luokassa. Ei myöskään ole takuita tapahtuuko päivitys ajallaan. Suositeltavampaa on toteuttaa widgetti niin että se päivittää vain silloin, kun sen on pakko, eikä useammin.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
  android:minWidth="146dp"
  android:minHeight="72dp"
  android:updatePeriodMillis="86400000"
  android:initialLayout="@layout/widget_layout"
  >
  </appwidget-provider>

Widget Manifestissä ja painallukseen reagointi

Manifestissä widget määritellään <receiver> elementin sisälle. Tässä elementissä määrittelet oman toteutuksesi AppWidgetProvider luokan ilmentymästä ja määrittelet ne Intent lähetykset (broadcast), jotka AppWidgetProvider hyväksyy. Nämä lähetykset suoritetaan onReceive() -metodissa.

<receiver android:name=".ExampleWidgetProvider">
  <intent-filter>
    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  </intent-filter>
  <meta-data android:name="android.appwidget.provider"
   android:resource="@xml/appwidget_provider" />
</receiver>

AppWidgetProvideria voi myös kutsua PendingIntent:llä, kuten tässä esimerkissä napin painalluksiin reagointi on toteutettu:

Intent intent = new Intent(context, ExampleWidgetProvider.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
views.setOnClickPendingIntent(R.id.start_button, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);

AppWidgetProvider

Kokonaisuudessaan omaan toteutukseesi AppWidgetProvideristä tarvitset vain onUpdate()- ja onReceive()-metodit. Voit myös tehdä tämän esimerkin mukaisesti paketin sisällä näkyvän staattisen metodin, joka päivittää widgetin grafiikat esimerkiksi konfiguraatioaktiviteetistä.

Liikkuva 2D-grafiikka Androidilla

Ensimmäinen asia, mitä lähes jokainen aloitteleva ohjelmoija haluaa tehdä on oma peli. Tämän toteuttamiseen tarvitaan lähes välttämättä liikkuvien grafiikkojen piirtämistä. Tässä artikkelissa esitetään Androidin 2D-grafiikkojen piirtäminen kankaalle. Käymme myös läpi hiukan yksinkertaista säikeiden käyttöä ja säieturvallisuutta.

Androidille 2D-grafiikkojen piirtämiseen tarvitaan näkymä, jolle voi piirtää. Toteutamme tässä esimerkissä oman versiomme SurfaceView:stä, jolle piirtäminen tapahtuu käytännössä antamalla SurfaceView:lle kankaan onDraw()-metodissa.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    starList = new ArrayList<StarFallActivity.Star>();

    setContentView(new StarSurface(this));
}

class StarSurface extends SurfaceView implements SurfaceHolder.Callback {
    private GameThread gameThread;
    private GraphicThread graphicThread;

public StarSurface(Context context) {
    super(context);
    getHolder().addCallback(this);
    gameThread = new GameThread(this);
    graphicThread = new GraphicThread(getHolder(), this);
}

@Override
public void onDraw(Canvas canvas) {
    canvas.drawColor(Color.BLACK);
    synchronized (starList) {
        for(Star star : starList) {
            canvas.drawBitmap(star.getImage(), star.getX(), star.getY(), null);
        }
    }
}
...

Pidämme kirjaa rajoittamattomasta määrästä putoavia objekteja ja piirrämme ne kankaalle niiden sisällään pitämän sijainnin muukaan onDraw()-metodissa. Resursseissa olevan kuvan piirtäminen valmiista kuvaresurssista ei vaadi, kuin resurssin bittikartaksi muuttamisen ja X- ja Y-koordinaatit, mihin kuva pintanäkymässä tulee.

BitmapFactory.decodeResource(getResources(), R.drawable.icon);

Säikeet ja säieturvallisuus

Pidämme objektien liikuttamisen ja ruudunpäivityksen erillisissä säikeissä ja laitamme säikeet odottamaan, kun niitä ei tarvita. Säikeiden käytössä täytyy myös ottaa huomioon samanaikaisten operaatioiden turvallisuus. Koska pidämme liikuvat objektit yhdessä listassa täytyy lista asettaa synkronoiduksi, niin että siihen voi kohdistua vain yksi operaatio kerrallaan. Tämä täytyy tehdä jokaisessa paikassa, missä listaa käsitellään. Esimerkiksi pelin osia liikuttelevassa säikeessä:

synchronized (starList) {
    for (Iterator<Star> iterator = starList.iterator(); iterator.hasNext();) {
        Star star = iterator.next();
        star.move();
        if(star.getY() >= maxHeight) {
            iterator.remove();
        }
    }
}

Koska poistamme listasta putoavan tähden, jos se menee ruudun ulkopuolelle joudumme käyttämään iteraattoria, koska listasta poistaminen sen läpikäynnin aikana ei suoraan ole mahdollista.

Säikeissä tarvitsee pääasiassa vain metodit säikeen käynnissä olon asettamiseen ja run()-metodin oma toteutus, missä säikeen suoritusaikainen toiminta tapahtuu. Sijettä ei tule tappaa stop()-metodilla, sillä se voi jättää ohjelmistosi outoon tilaan. Tämän sijasta tulisi käyttää run()-metodissa while lausetta joka toistuu niin kauan, kuin säikeen sisäinen while ehto muuttuu epätodeksi ja run()-metodi pääsee suorittamaan loppuun. Säikeet joiden run()-metodi on päässyt suoriutumaan kokonaan tapetaan automaattisesti järjestelmän toimesta.

Jos et halua säikeesi vievän kaikkea prosessointitehoa aseta säie jokaisen while luupin lopuksi odottamaan jonkin aikaa. Piirtopintaa ei esimerkiksi kannata päivittää useampaa, kuin 60 kertaa sekunnissa ja tämäkin on yleensä liikaa. Säikeen voi myös pakottaa käynnistymään kutsumalla sille notify()-metodia.

Lataa esimerkki tästä.
Voit nyt tehdä esimerkiksi oman versiosi kitarasankarista ja pudottaa ruudun yläpäästä kuvia ja tarkastella osuuko käyttäjä niihin ennen, kuin ne putoavat ulos ruudulta.

Android Manifest

Android-manifest voi aluksi näyttää vaikealta ymmärtää, mutta ilman sitä ei koodia voi suorittaa. Tässä artikkelissa opetetaan Android-manifestin perus käyttö ja käydään läpi vaativammat käyttömahdollisuudet.

Manifestin rakenne Androidin dokumentaatiosta:

<?xml version="1.0" encoding="utf-8"?>

<manifest>
<uses-permission />
 <permission />
 <permission-tree />
 <permission-group />
 <instrumentation />
 <uses-sdk />
 <uses-configuration />
 <uses-feature />
 <supports-screens />
 <compatible-screens />
 <supports-gl-texture />
 <application>
 <activity>
 <intent-filter>
 <action />
 <category />
 <data />
 </intent-filter>
 <meta-data />
 </activity>
 <activity-alias>
 <intent-filter> . . . </intent-filter>
<meta-data />
 </activity-alias>
 <service>
 <intent-filter> . . . </intent-filter>
 <meta-data/>
 </service>
 <receiver>
 <intent-filter> . . . </intent-filter>
 <meta-data />
 </receiver>
 <provider>
 <grant-uri-permission />
 <meta-data />
 </provider>
 <uses-library />
 </application>

</manifest>

Nämä on osattava

Käytetään esimerkkinä SQL-tietokanta artikkelissa annettua koodia ja sen AndroidManifest.xml:ää.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="androidkehitys.fi.database"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="7" />

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name="DatabaseActivity" android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity android:name="EnterProductActivity"></activity>

</application>
</manifest>

Manifestissä ei elementtien sisään tule muuta, kuin toisia elementtejä. Arvon kirjottaminen elementin sisään ei toimi ja aiheuttaa virheen. Kaikki arvot mitä manifestissä annetaan tulee antaa elementtien parametreissä.

<manifest>-elementin attribuuteiksi tulee applikaation oletuspaketti, versionumeroja nimi. Elementin sisälle asetetaan minimi versio Android käyttöjärjestelmästä. Esimerkiksi tässä tapauksessa käytetty API versio 7 on “Android 2.1 – update 1”.

Manifest-elementin sisälle tulee myös <application>-elementti, jossa määritellään applikaation ikoni ja nimi.

Applikaatio elementtien sisällä tehdään jokaiselle aktiviteetille, jonka käyttäjä voi käynnistää, <activity>-elementti. Mikäli aktiviteetti on samassa paketissa, kuin manifest-elementissä määritelty oletuspaketti, riittää asettaa aktiviteetin nimeksi kyseisen luokan nimi. Mikäli aktiviteetti on jossain toisessa paketissa tulee sinun määrittää koko paketin polku.

Aktiviteetti-elementin sisälle voi määritellä <intent-filter>-elementin, joka määrää erityistilanteita, missä aktiviteetti käynnistetään. Ensimmäinen aktiviteetti, mikä applikaatiossasi käynnistetään täytyy määrittää oikeilla intent-filtereillä:

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

Aktiviteetit, jotka eivät saa intenttejä applikaation ulkopuolelta ei tarvitse muuta, kuin nimetä manifestissä. Mikäli aktiviteettiä ei julisteta manifestissa sen käynnistäminen kaataa applikaation ja aiheuttaa ActivityNotFoundExceptionin;

Vaativampaa käyttöä varten hyvä tietää

Tässä käydään nopeasti läpi manifestin vaikeammin käytettäviä ja ymmärrettäviä ominaisuuksia. Näitä ei välttämättä tarvitse tietää yksinkertaisia applikaatioita tehtäessä, mutta ennemmin tai myöhemmin ne tulevat vastaan androidkehityksessä.

Tietyt osat Android-järjestelmästä vaativat käyttäjän lupaa ja ne täytyy määritellä manifestissä. Esimerkkinä tällaisista luvista on esimerkiksi internet-yhteyden käyttö:

<uses-permission android:name="android.permission.INTERNET" />

Intenttien vastaanottamisen määrittely manifestissä taas mahdollistaa reagoinnin tapahtumiin, vaikka applikaatio ei olisikaan käynnissä. Tällaisia tapauksia voi esimerkiksi olla NFC-sirujen lukeminen tai push-notificaatioiden vastaanottaminen.

<service>-elementti määrittelee applikaation tarjoaman palvelun muille laitteen applikaatioille. Näin määritellyt palvelut pysyvät päällä, vaikka käyttäjä vaihtaisi toiseen applikaatioon. Tällaisten palveluiden tulisi suorittaa yksi tehtävä ja sitten sulkea itsensä, sillä muuten näitä palveluita ei tapeta koskaan.

Jos haluat applikaatiosi reagoivan järjestelmän tai muiden applikaatioiden lähettämiin intentteihin, kuten push-notifikaatioihin on sinun määriteltävä <receiver>-elementti ja sen sisältö manifestissä applikaatio-elementin sisällä.

<provider>-elementin sisällä määritellään applikaation tarjoama sisältö. ContentProvider tarjoaa pääsyn applikaation hallinoimaan tietoon, myös muista applikaatioista.

Manifestin vaativampaan käyttöön palataan, kun sitä tarvitaan käytännön esimerkeissä.

Nine-patch Androidissa

Graafinen käyttöliittymä vaatii paljon kuvaresursseja, ja mikäli on tarkoitus tukea erilaisia Android-laitteita, joudutaan tekemään samoista kuvista eriresoluutioiset versiot. Työ vähenee huomattavasti, jos kuvat venyvät sopivaan kokoon.

Nine-patch tarjoaa mahdollisuuden venyttää kuvia järkevästi, eli se venyttää vain määriteltyjä osia kuvasta. Muu osa kuvasta pysyy sellaisena, kuin se on kuvassa, eikä veny. Venyviä alueita voi kuvassa olla useita ja niiden kokojen suhteet pysyvät aina samoina.

Nine-patch-kuvaan on myös mahdollista määritellä alue, johon sisältö tulee. Tämä on erityisen käytännöllistä tehtäessä grafiikoita säiliöihin, kuten nappeihin. Kun napin kuvasta tekee pienen ja asettaa sen kasvamaan sisällön mukaan ja kun sisältö on määritelty napin keskelle, grafiikka tulee automaattisesti oikean kokoiseksi.

Kuinka tehdä 9-patch kuvia

Nine-patch kuvat ovat normaaleja png-kuvia, joissa on yhden kuvapisteen paksuinen reuna joka on joko täysin läpinäkyvä tai valkoinen. Tälle reunalle piirretään yhden kuvapisteen levyistä mustaa viivaa ja kahden eri sivuilla olevan viivan määrittämä leikkaava alue rajaa venyvän ja sisällön täyttävän alueen. Android laitteella nämä kuvat on talletettava muotoon “.9.png”, jotta Android tietää kuvien olevan nine-patchejä.

Vasemmalla ja ylhäällä olevat mustat viivat rajaavat venyvän alueen. Oikealla ja alhaalla olevat viivat taas rajaavat kuvan sisällön alueen.

Kuvat voi tehdä millä tahansa kuvankäsittelyohjelmalla, mutta helpoin tapa on käyttää Android SDK:n mukana tulevaa työkalua “draw9patch”. Ohjelma löytyy “tools” kansiosta.

Ohjelma lisää kuvaan yhden kuvapisteen kokoiset reunat ja antaa muokata kuvaa vain näiden reunojen alueelta. Ohjelma myös näyttää oikeassa laidassa miltä kuva näyttää eri tavoilla venytettynä.

Avattuasi ohjelman voit raahata haluamasi kuvan ohjelmaan. Tämän jälkeen näet kuvan venytyksen tuloksen oikealla ja kuvan muokattavana vasemmalla. Kuvassa kannattaa valita tasaisia – usein vain yhden pikselin mittaisia – alueita venyväksi, koska tällöin kuvan väritys ei muutu. On myös tapauksia, missä on järkevää venyttää väritystä niin että isommilla ko’oilla väritys muuttuu tasaisesti.

Voit asettaa kuvassa editorissa venyvän alueen näkyväksi “show patches” valinnalla. Venyvät alueet ovat violetit alueet, jossa vihreät viivat kohtaavat. Samalla tavalla saat sisällön alueen näkyviin “show content” valinnalla.

Huomaa miten kuvassa olevat yksityiskohdat pysyvät tarkkoina, vaikka muu kuva venyy täyttämään koko näytön.

Viime viikon animaatioartikkelissa on käytetty tässä esimerkkinä olevaa nine-patch kuvaa. Nine-patch kuvien käyttö onnistuu applikaatiossa täysin samalla tavalla kuin normaalien kuvien. Android itse hoitaa kuvan koon ja sisällön asettelun.

Esimerkkikuva ilman nine-patchiä.

Esimerkkikuva nine-patchin jälkeen.

Animaatiot Androidissa

Helppo tapa tehdä applikaatiostasi hienompi on lisätä animaatioita. Käymme tässä läpi yksinkertaisia animaatioita ja niiden käyttämistä applikaatiossasi. Tässä esimerkissä keskitymme elementtien sijainnin muuttamiseen animaatioilla.

Animaatioita voi tehdä kahdella tavalla. Joko suoraan koodissa, tai määritellä erikseen xml:ssä. Jos animaatiossa ei tarvitse laskea suoritusaikana mitään tulisi ne tehdä xml:ssä, että koodi on selkeämpää.

Voit ladata esimerkin lähdekoodin täältä.

Animaatio koodissa

Animaation luominen koodissa on hyvin suoraviivaista. Uusi animaatio olio luodaan halutunlaisesta animaatiotyypistä. Tässä tapauksessa:

TranslateAnimation (int fromXType, float fromXValue,
                    int toXType, float toXValue,
                    int fromYType, float fromYValue,
                    int toYType, float toYValue)

Type attribuutit kertovat onko animoitava matka absoluuttinen vai suhteellinen. Suhteellinen matka voi riippua joko itse animoitavasta näkymästä, tai sen vanhemmasta:

Animation.ABSOLUTE
Animation.RELATIVE_TO_SELF
Animation.RELATIVE_TO_PARENT

Koodissa olen käyttänyt absoluuttista siirtymätyyppiä, sillä haluan animoida valikon liikkumisen vain piiloon jäävältä osiolta, jonka olen asettanut 100dp:ksi. Huomaa että koodissa täytyy ottaa huomioon dp:t, eli näytön tiheydestä riippumattomat pikselit. Näytön tiheyden saat:

float scale = getResources().getDisplayMetrics().density;

Koska absoluuttisia siirtoja ei voi ilmoittaa dp:nä on annetut dp:t kerrottava näytön tiheydellä, jotta saat dp:n arvon pikseleinä.

Animaatiolle tulee asettaa kesto, minkä aikana animaatio suoriutuu loppuun. Kesto annetaan millisekuntteina.

animation.setDuration(500);

Animaation loputtua liikutettu näkymä ei suoraan jää sille asetettuun paikkaan, vaan sen paikka täytyy asettaa koodissa ja vasta, kun animaatio on päättynyt. Muuten kuva hyppää asettamaasi paikkaan suoraan.

Huomaa:

Huomaa, että animaatio lähtee aina animoidun näkymän sen hetkisestä asemasta. Aseman pystyy muuttamaan myös automaattisesti:

animation.setFillEnabled(true);
animation.setFillAfter(true);

Tätä tapaa voi käyttää sekä koodissa, että xml:ssä, mutta jos kuvasi reagoi painalluksiin sen oikea paikka on edelleen siellä, missä se on xml layoutissa asetettu. Eli kuvan tai napin paikka on oikeasti eri, kuin missä se näyttäisi olevan. Tästä syystä tätä tapaa ei voi käyttää tässä esimerkissä. Jos esimerkiksi valikossa olisi nappula se kyllä tulisi näkyviin, mutta sen painalluksen tunnistava osuus olisi edelleen näytön ulkopuolella, eikä nappulaa näin voisi painaa.

Lopuksi animaatio käynnistetään sille näkymälle, mitä halutaan liikuttaa:

slider.startAnimation(animation);

Animaatio XML:ssä

Animaatioiden määrittäminen XML:ssä on melko suoraviivaista. Tarvittavat ohjeet ja listat mahdollisista elementeistä löytyvät Googlen Android developer-sivustolta.

Animaatiot tallennetaan applikaatiosi resurssi kansioon res/anim/animaation_nimi.xml. Animaatio voi sisältää useita animaatioita, kuten liikkumista ja koon muutosta. Nämä animaatiot tulee asettaa <set>-elementin sisälle. Jos animaatiosi koostuu vain yhdestä osasta ei <set>-elementin käyttäminen ole pakollista. Yllä olevasta linkistä löytyy mahdolliset animaatiotyypit ja niiden parametrit.

Tähän esimerkkiin tein yksinkertaisen siirtymisanimaation, missä kuva tulee näytön vasemmasta laidasta sisään. Animaatiolle on asetettu kesto, lähtöpaikka ja lopullinen paikka. Tässä tapauksessa kuva on koko näytön kokoinen ja aloittaa -100%:sta ja siirtyy normaaliin paikkaansa. X-akselin negatiiviset arvot ovat ruudun vasemmalla puolella ja positiiviset oikealla puolella.

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
   android:interpolator="@android:anim/decelerate_interpolator"
   android:duration="500"
   android:fromXDelta="-100%"
   android:toXDelta="0"
   android:fillEnabled="true"
   android:fillAfter="true"
   />

Animaatio ladataan koodissa:

Animation slideFromLeft = AnimationUtils.loadAnimation(this, R.anim.slide_in_from_left);

Muista asettaa animoitu näkymä näkyväksi, ennen kuin aloitat animaation, jos näkymän näkyvyys on poissa. Tai jos viet näkymää pois ruudulta aseta näkymä pois animaation jälkeen. Muuten animaatio aloitetaan samalla tavalla, kuin koodiesimerkissä.

Vinkki

Mukava tapa sulkea valikoita, jotka eivät peitä koko näyttöä on tehdä tyhjän osan peittävä näkymä, jota painamalla valikko menee kiinni. Tässä esimerkissä teemme slidecontainer:ssä FrameLayoutin nimeltä hider, joka tekee saman asian, kuin välilehden painaminen. Kun valikko on ylhäällä piilotamme hiderin, niin ettei sitä pysty painamaan. Tämä lisää valikon käytettävyyttä.

Valikot olisi myös hyvä toteuttaa omina aktiviteetteinään, mutta tässä esimerkissä näin ei voi tehdä, sillä valikon alareuna on osittain näkyvissä koko ajan. Aktiviteetti voi olla vain osittain toisen päällä, mutta alempaa aktiviteettiä ei voi käyttää.

Android SQL-tietokannan käyttäminen

Jatkona viimeviikon tiedon tallentamiselle toteuttakaamme SQLite-tietokanta Androidissa.

Yksinkertaisuuden vuoksi luomme vain yhden taulun sisältävän tietokannan. Tietokantaan talletetaan tuotteita, joilla on nimi, määrä sekä kuvaus. Tämän artikkelin liitteenä on esimerkki ohjelma, jossa tietokantaa voi kokeilla.

Product
product_name
product_count
product_description


Lataa esimerkin lähdekoodi.

Jatkamme SQLiteOpenHelper-luokkaa omalla toteutuksellamme ja luomme taulut onCreate-metodissa. Taulujen sarakkeiden nimet voi kätevästi tallettaa finaaleina muuttujina erillisiin sisäluokkiin, jotta ne on määritelty vain yhdessä paikassa ja selkeästi jäsenneltyinä.

Taulujen luonti tapahtuu samoilla SQL-komennoilla, kuin normaalin tietokannan luonnissa. Komentojen kirjoittaminen koodissa vain on hiukan eri näköistä, jos sen tekee StringBuilderilla. Create table-lauseen voi antaa myös suoraan stringinä.

private static final class ProductTable {
    public static final String PRODUCT_NAME = "product_name";
    public static final String PRODUCT_COUNT = "product_count";
    public static final String PRODUCT_DESCRIPTION = "product_description";
}
@Override
public void onCreate(SQLiteDatabase db) {
    StringBuilder sql = new StringBuilder();

    sql.append("CREATE TABLE ").append(PRODUCT_TABLE_NAME).append(" (");

    sql.append(ProductTable.PRODUCT_NAME).append(" TEXT,");
    sql.append(ProductTable.PRODUCT_COUNT).append(" INTEGER,");
    sql.append(ProductTable.PRODUCT_DESCRIPTION).append(" TEXT,");
    sql.append("PRIMARY KEY(").append(ProductTable.PRODUCT_NAME).append(")");
    sql.append(")");

    db.execSQL(sql.toString());
}

Tiedon tallentaminen tietokantaan

Tieto tallennetaan tietokantaan SQLiteDatabase-luokan instanssilla, joka saadaan SQLiteOpenHelper:ltä perityllä getWritableDatabase()-metodilla. Näin saatuun tietokantaolioon voi suorittaa kyselyitä tai lisäyksiä.

Lisättävät arvot on asetettava ContentValues-luokan ilmentymällä, joka pitää sisällään avain-arvo pareja, jossa avaimet ovat tietokannan sarakkeen nimiä ja arvo tietokantariville tulevan sarakkeen arvo. Jokaiselle tietokantariville täytyy luoda oma ContentValues-joukkonsa.

Hyvän ohjelmointitavan mukaista on, että tietokantametodit palauttavat boolean arvon riippuen onnistuiko metodi vai ei. Tietokantakyselyt luonnollisesti palauttavat kyselyn tuloksen.

boolean addProduct(Product product) {
    boolean result = true;
    SQLiteDatabase db = this.getWritableDatabase();

    ContentValues values;
    values = new ContentValues();
    values.put(ProductTable.PRODUCT_NAME, product.getName());
    values.put(ProductTable.PRODUCT_DESCRIPTION, product.getDescription());
    values.put(ProductTable.PRODUCT_COUNT, product.getQuantity());

    long id = db.insert(PRODUCT_TABLE_NAME, null, values);

    if(id == -1) {
        Log.e(TAG, "Could not add product");
        result = false;
    }
    return result;
}

Tiedon hakeminen tietokannasta

Tietokannan lukemiseen riittää vain lukemiseen tarkoitettu SQLiteDatabase-olio, jonka saa getReadableDatabase()-metodilla. Kyselyn voi toteuttaa yhteen tauluun query()-metodilla, joka tarvitsee useanlaisia parametrejä. Tietokantaan voi myös suorittaa kyselyitä rawQuery()-metodilla, mille voi argumenttinä antaa sql-kyselyn suoraan stringinä.

Metodeista saadaan paluuarvona Cursor-luokan ilmentymä. Kursori antaa pääsyn haettuun tietokantatauluun. Tässä esimerkissä käymme kursorin jokaisen rivin läpi ja otamme sarakkeiden tiedot getString()- ja getInt()-metodeilla, missä parametreinä on sarakkeen numero alkaen nollasta. Sarakkeet ovat samassa järjestyksessä, kuin kyselyssä annetussa argumentissä.

Kursori tulisi aina muistaa sulkea sen käyttämisen jälkeen.

List<Product> getProducts() {

    SQLiteDatabase db = this.getReadableDatabase();

    String[] colums = new String[] { ProductTable.PRODUCT_NAME, ProductTable.PRODUCT_COUNT, ProductTable.PRODUCT_DESCRIPTION };

    Cursor cursor = db.query(PRODUCT_TABLE_NAME, colums, null, null, ProductTable.PRODUCT_NAME, null, null);

    cursor.moveToFirst();

    List<Product> products = new ArrayList<Product>();
    while (cursor.isAfterLast() == false) {
        String name = cursor.getString(0);

        Log.v(TAG, "Found product: "+name);
        Product product = new Product(name);

        product.setDescription(cursor.getString(2));
        product.setQuantity(cursor.getInt(1));

        products.add(product);

        cursor.moveToNext();
    }
    cursor.close();
    return products;
}

Tiedon poistaminen

Tiedon poistaminen tietokannasta tapahtuu hyvin samalla tavalla, kuin lisääminen. Metodin onnistumisen seuraaminen onnistuu helpoiten, kun se palauttaa boolean arvon. SQLiteDatabase tarjoaa metodin delete(taulun_nimi, where_lause, where_parametrit), joka palauttaa poistettujen rivien lukumäärän.

boolean deleteProduct(Product product) {
    SQLiteDatabase db = this.getWritableDatabase();
    String[] params = new String[] { product.getName() };

    int numOfRows = db.delete(PRODUCT_TABLE_NAME, ProductTable.PRODUCT_NAME + " = ?", params);

    if (numOfRows >= 1) {
        return true;
    }
    return false;
}