Mobilními aplikacemi v tuto chvíli míníme aplikace pro mobilní telefony. V dnešní době jde až na drobné výjimky o chytré telefony osazené Androidem nebo iOSem. My se zaměříme na ty první. Ty druhé se programují podobně, jen je třeba sehnat si jiné vývojové studio a absolvovat jinou byrokratickou proceduru.

Java

TBD (zatím použijte slidy). Java je jazyk velmi podobný C#, který již znáte.

Android Studio

Prostředkem, ve kterém společnost Google očekává, že bude vývoj probíhat, je Android Studio. Jde o IDE jménem Idea, do kterého je zabudované dedikované prostředí. Někdy je potřeba před zprovozněním tohoto studia zprovoznit Java SDK (ale jindy zase ne). Toto studio obsahuje (kromě IDE) především specifické knihovny pro Android. Instalace není úplně snadná, jelikož při obvyklém instalačním kroku člověk instaluje vlastně jen IDE. Idea

Jelikož Android se vyvíjí a existuje v mnoha verzích, po spuštění studia je zapotřebí doinstalovat API pro konkrétní verzi (či verze pro které míníme vývoj provádět). K údržbě těchto součástí studia najdeme v menu Tools položku SDK Manager.

Další věcí, kterou budeme potřebovat a kterou musíme instalovat zvlášť, je emulátor mobilního telefonu. O toto se stará AVD Manager, který je hned vedlejší položkou v menu Tools.

V AVD Manageru je třeba nastavit, na jakém z dostupných emulátorů telefonů chcete kód ladit a v jaké verzi Androidu. Při výběru (po kliknutí na Create Virtual Device) myslete na to, že celé studio je výpočetně velmi náročné a je tedy třeba konfiguraci přizpůsobit schopnostem hardwaru, na kterém budeme pracovat. Android běží na procesorech intelské architektury (x86), nebo na procesorech ARM. Pro oboje jsou k dispozici emulatory. Pro našince je přirozenější na x86 spustit emulator x86. Tyto procesory totiž mají již jistou dobu podporu virtualizace, takže se emulace dá touto virtualizací výrazně urychlit (kód běží přímo na našem hardwaru jen poněkud omezeně). Ovšem ke zprovoznění emulatoru x86 na x86 může být potřeba ve Windows v nastavení Turn Windows features on and off zakliknout Virtual Machine Platform, nebo naopak vykliknout Hyper-V (které ovšem našinec na Windows Home Edition nemá, mají jej jen zbohatlíci na Windows Professional edition). Bez tohoto nastavení nemusí jít zprovoznit HAXM (virtualizátor, který výrazně urychluje emulaci). Bez HAXM nelze spustit emulator x86 a je nutno spoléhat na emulator ARMu, který sice HAXM nepotřebuje, nemůže však využít virtualizaci. Napoprvé startuje kolem půl hodiny a při dalších spuštěních to není o mnoho lepší. Hracka pro deti Vidíme, že jde o emulator telefonu s Androidem se vším všudy (ale bez SIM-karty).

Pokud se při běhu Studia ozývá soustředěné chrastění disku a nepřichází delší dobu žádná reakce ani na Alt+Ctrl+Del, došla paměť (RAM) a systém tráví většinu času swapováním paměti na disk. Zprovozňovat toto vývojové prostředí může mít smysl s alespoň 4 GB RAM, kde je ovšem již za pomezím praktické použitelnosti, na 8GB systému již prostředí je použitelné. Došla-li paměť a systém swapuje, je vhodné pozavírat zbytečně běžící procesy a případně prostředí trochu přenastavit. Je možné povypínat integraci s verzovacími systémy (File -> Settings -> Plugins a tam tabu Installed odklikat položky Git Integration, GitHub, Google Cloud Tools For Android Studio, Firebase Testing, Google Cloud Tools Core a Subversion. Dále je možné nastavit si úspornější telefon (třeba Nexus One s pamětí 0,5 GB namísto Pixelu 2 s 1 nebo 2 GB). Dále je možné zkusit ještě v této konfiguraci ušetřit (v AVD Manageru klikneme na ikonu tužtičky, tam si necháme zobrazit Advanced Settings, zrušíme SD-kartu, zmenšíme interní storage a především zmenšíme paměť RAM). Při tomto šetření je však třeba myslet na to, že se nám do emulovaného prostředí musí vejít to, co chceme ladit, a to včetně operačního systému. Android je operační systém stojící na Linuxovém kernelu a dobře víme, co tento kernel udělá s procesy, kterým dojde paměť!

Otravné je, že emulace běží i na rychlých strojích tak pomalu, že watchdog občas zpozoruje, že některý důležitý proces (jako třeba uživatelský interface) během 15 sekund nereaguje. Na to upozorní a ptá se, jestli má neodpovídající proces zabít. Někdy (v případě našich vlastních zaseknutých procesů) je to užitečné. Mordovat uživatelský interface však úplně chytré není.

Nahrajeme-li nějakou aplikaci do emulatoru, reaguje emulator jako obyčejný telefon, tedy aplikaci instaluje do persistentní paměti a aplikace tam pak zůstávají. Zde například vidíte na prvním obrázku aplikaci My Application 3 (nahranou z Android Studia), ke které na dalším obrázku přibyla ještě aplikace vytvořená ve Visual Studiu (jmenující se AndroidApp1): Pribyla My Application 3 A ted jeste AndroidApp1

Již od nepaměti je toto studio slušně děravé. Dříve nefungoval designer formulářů a jednotlivé formuláře bylo nutné psát ručně. Ostatně při výpočetním výkonu, který tento designer požaduje, bývá dosud lepší na něj nespoléhat a formuláře si připravovat ručně v XML. Poslední dobou Studio zase má dojem, že i nejnovější verze emulátoru je příliš zastaralá (toto hlášení je zase potřeba ignorovat). Od chvíle, kdy si necháte vygenerovat první projekt, do chvíle, kdy vám začne být fungování studia přiměřeně jasné, prosím, nic neupdatujte (i když vás k tomu Studio bude v jednom kuse vybízet). Po updatu se totiž obvykle změní struktura projektu, a dokud vám nebude fungování dostatečně jasné, starý projekt v nové verzi Studia nezprovozníte.

Samotný vývoj probíhá poměrně standardním způsobem známým například z Mircosoft Visual Studia .NET. Tedy naklikáme si nastavení projektu. Tímto procesem necháme vygenerovat množství XML souborů projekt popisujících a také obvykle alespoň jeden soubor zdrojového textu. Jako programovací jazyky lze použít Kotlin nebo Javu. Při výběru projektu se pokuste vybrat "Basic Activity" (alespoň do chvíle, kdy tato bude přejmenována). My se soustředíme na Javu (tedy při výběru nevybírejte projekt v Kotlinu), ačkoliv dostupné údaje naznačují, že společnost Google chce nadále výrazně více protlačovat Kotlin. Ten je jejich vlastním jazykem, kdežto o Javu se s nimi průběžně soudí společnost Oracle (která Javu získala anexí společnosti SUN Microsystems). Java má však pro nás výhodu, že se syntaxí velmi podobná jazyku C#, který již znáte a bylo tedy možno probrat ji v minulé kapitole velmi rychle, kdežto Kotlin by vyžadoval důkladnější výklad.

Naklikání projektu probíhá způsobem velmi podobným tomu, který znáte z Visual Studia. Předpokládáme tedy, že si s tím poradíte (při využití pokynů z minulého odstavce).

New project I New project II

Všeobecný vzhled zdrojového kódu pro aplikaci v Androidu

V sekci věnované aplikacím v Javě jste se dozvěděli, že Java je povinně objektový jazyk, takže každá funkce musí být definována v nějaké třídě. Vstupním bodem programu je funkce main. Toto nyní není pravda. Aplikace pro Android je reprezentována třídou, která dědí od (alespoň dříve) abstraktní třídy Activity. S postupujícím vývojem vzniklo množství dalších tříd (dědících od Activity), například TabActivity, která má usnadnit implementaci programu zobrazujícího několik tabů.

Svět komunikuje s třídou Activity základním způsobem pomocí 6 metod (zvaných callbacky, jelikož tyto metody jsou vyvolávány příslušnými událostmi). Jde o funkce onCreate, onStart, onResume, onPause, onStop a onDestroy. Jednotlivé funkce se vyvolají při příchodu dotyčné události.

Nás zde bude především zajímat funkce onCreate, která se vyvolá po startu dotyčné aktivity. V té je třeba zajistit zobrazení vhodného formuláře a vůbec provést celkovou inicializaci programu, tedy například ponastavovat ovladače událostí týkajících se zobrazovaného formuláře (především pokud jsme k návrhu formuláře nepoužili klikací editor - ostatně v tom případě právě v této funkci provedeme osazení formuláře zobrazovaného při startu aplikace. Až definujete funkci onCreate a vytvoříte jednoduchý program komunikující s uživatelem, povšimněte si, že se nám budou porůznu ztrácet údaje při různých událostech (například při obyčejném otočení displaye). To je proto, že jsme nedefinovali funkce OnPause a onResume. Ačkoliv my se zde soustředíme jen na funkci onCreate (protože ostatní zde jmenované funkce pracují podobně), myslete na to, že i ostatní funkce jsou důležité (má-li se Vaše aplikace používat příjemně).

Funkce onCreate přijímá jako parametr uložený stav instance jako (datový) typ Bundle. Jako první krok v této funkci musíme zavolat metodu onCreate v naší rodičovské třídě (Activity). Ze C# pro tyto účely znáte klíčové slovo base. V Javě se však místo něj používá klíčové slovo super.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

Takto přibližně vypadá implicitně vygenerovaná funkce onCreate (necháme-li si vygenerovat prázdnou aplikaci). Nahoře vidíme anotaci, která říká, že overridujeme (virtuální) funkci v rodiči. Za hlavičkou funkce voláme rodičovskou funkci onCreate. Na posledním řádku nastavujeme, co se má zobrazit na displayi při startu.

Při experimentování s uvedenými příklady dávejte určitý pozor (alespoň do doby, kdy vám začne být fungování prostředí jasné). Konkrétně z vygenerovaných zdrojových textů neodstraňujte žádné funkce, i když v našem příkladu tyto funkce nejsou a to ani když ve zdrojovém kódu nevidíte žádné volání těchto funkcí. Nejspíše jsou nastavené jako ovladače nějaké události nastavené v XML popisu formuláře. Pokud byste takovou funkci smazali, bude se po ní překladač při nejbližší kompilaci shánět. A ještě přesněji, při své odladěnosti může Android Studio dokonce nějakou chvíli fungovat i bez dotyčné funkce (než zpozoruje, že se zdrojáky změnily a než je rekompiluje).

Povšimněte si, že k prvkům formuláře zde přistupujeme úplně jinak, nežli v Microsoft Visual Studiu. Tam existovaly proměnné s příslušným jménem. Společnost Google však jde na věc úplně jinak. Údaje o prvcích formulářů, které jsme naklikali v builderu, jsou dostupné v resourcech pod jménem R a tam konkrétně v prvku layout. Prvek activity_main odkazuje k souboru activity_main.xml, kde je XML-ský popis zobrazovaného formuláře. A hned si začneme všímat rozdílů oproti tomu, co jsme dosud znali.

Klikatko Idea

Formulář zobrazovaný Androidem je navržený jednoduše. Sestává z layoutu, na kterém jsou jednotlivé prvky (které již znáte). Tento layout, přesněji tyto layouty, si nyní trochu objasníme. Základním layoutem je lineární layout. Ten je význačný tím, že prvky na něm obsažené skládá jeden za druhý (v tom pořadí, v jakém je obsahuje). Lineární layout může být vertikální (pak skládáme prvky nad sebe) nebo horizontální (vedle sebe). Další layouty nám umožňují například zobrazovat taby či scrollovat. Dalším layoutem, u kterého se zastavíme, je scrollovací layout. Ten je zvláštní tím, že smí obsahovat pouze jeden prvek. Proto se do scrollovacího layoutu dává layout lineární (který umí obsáhnout více prvků, což je obvykle důvodem, proč potřebujeme s layoutem scrollovat). Pro začátek tedy vystačíte s lineárním layoutem. Další layouty především posilují možnosti rozmisťování prvků po formuláři. Nastudujete je, až budete potřebovat napsat reprezentativněji vyhlížející aplikaci.

Příklad:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">

    <TextView
        android:id="@+id/textovepole"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
Všimněte si, že používáme právě to XML, o kterém jste se nedávno učili. V příkladu máme constraintový layout, který je nyní obzvlášť v módě. Tam prvky rozmísťujeme s využitím omezení, tedy řekneme, které prvky k sobě mají přibližně přiléhat a celkový vzhled vypadá, jako by se dotyčná místa pospojovala pružinkami, které se pokoušejí mít správnou délku, a tedy za prvky na layoutu tahají, až jim přidělí místo v pozici, která odpovídá minimálnímu pnutí v systému.

Společnost Google má trochu jinou politiku správy zdrojových textů nežli Mircosoft. Namísto importu všeho užitečného hned na začátku je člověk průběžně dotazován při použití konkrétních tříd (EditText, Calendar, TextView). Zpravidla stačí stisknout Alt+Enter k naimportování správného balíčku.

Patrně jste si již vyzkoušeli, že i takto poměrně prázdný projekt lze zkompilovat a spustit. K tomu je třeba nastavit, na čem se má aplikace spustit (tedy musíte vybrat ten emulator, který jste si v AVD-manageru připravili (v horní části okna). Hned vedle tohoto nastavení se nachází zelená šipka známá z magnetofonů jako piktogram pro funkci hraj. Tím program spustíte (a případně předtím zkompilujete). Nedebugujeme však ikonou play, ale ikonou brouka (až nám program nebude fungovat). V prostředí si všimněte množství poschovávaných tabů reprezentovaných nápisy po okraji. Až budete ladit, debugger se vám rozhodně sám nezobrazí. Ale na dolní liště uvidíte nápis Debug a že už se debuguje, vás upozorní zelený puntík u tohot nápisu. Při ladění počítejte s tím, že do hlavy a do mobilu je dost špatně vidět, takže je vhodné používat debugovací výstupy a ladit inkrementálně, tedy omezit množství prováděného kódu na možné minimum (kdy aplikace přestane padat) a kód postupně přidávat (a tak zjistíte, kde padá).

Start je tu opravdu pomalý, protože kromě kompilace a spuštění aplikace je třeba spustit celý emulator androidu (a do něj aplikaci nahrát). Start tohoto nelidsky pomalého prostředí je implicitně podle možností urychlen. Implicitně zkusí emulator napříště nastartovat do stavu uloženého současném ukončení emulace. Tak celý Android nestartuje pokaždé znovu, zato pokaždé při vypínání stav ukládá (a při startu načítá). Toto nastavení sice není úplně pitomé, ale ve chvíli, kdy se emulace zřítí (tedy emulator vytuhne), je třeba aktivní zásah k obnovení funkčnosti. Tento zásah vypadá tak, že v AVD-Manageru klikneme na další akce a řekneme Wipe User Data. Tím zlikvidujeme všechna data, co si Android dosud spočítal a napříště začneme studeným startem (tedy tím, co sice trvá, ale častěji se povede). Taktéž v takovém případě můžeme v dalších akcích objednat napříště studený start.

Všeobecné vlastnosti jazyku Java již znáte (tak víte, jak se pracuje s proměnnými, definují a volají funkce, jaké jsou základní řídicí struktury,...). Zbývá tedy vstup a výstup. Tedy jak načíst data, vypsat data (což je obvykle obdobné) a především jak vyvolat metody, které mají příslušnou akci provést. Těmito metodami se budeme zabývat napřed, protože musíme začít drobným krokem stranou.

Událostmi řízené programování

Při tvorbě aplikací pro Android se používá událostmi řízené programování. To vypadá tak, že aplikace se z hlediska programátora jeví jako nečinná, dokud nepřijde nějaká událost, o kterou jsme si řekli, že chceme obsloužit. Že chceme dotyčnou událost obsloužit, jsme sdělili překladači tak, že jsme nějakou funkci nastavili jako ovladač dotyčné události, případně jsme o nějaké třídě řekli, že dotyčnou událost umí zpracovat. Klasickým přístupem v Javě je ten druhý (protože v Javě nejsou pointery). Povšimněte si, že událostmi řízené programování jsme již viděli v úvodu, protože metody onCreate, onStart,... onDestroy jsou přesně představitelé ovladačů událostí. Aby bylo možno obsloužit událost, musíme typicky pro prvek, kterého se událost týká, stanovit, která třída tuto událost umí obsloužit. Pro konkrétní události jsou stanovena jména metod, které se při této události vyvolají. Podobný přístup již znáte z Tvorby webu, kde stránka také nic nedělala do chvíle, kdy přišla událost, na kterou jsme pověsili Javascriptový ovladač. Způsob fungování je tedy stejný, jiné je jen technické provedení.

Chceme-li definovat ovladač nějaké události klasickým Javovým způsobem, musíme definovat třídu, která implementuje stanovený interface. Interfacy jsou náhražkou násobné dědičnosti (která v Javě neexistuje). Interface říká, jaké funkce musí třída implementovat, aby ji bylo možno použít k žádanému účelu. V Javě se interfacy implementují, tedy za název třídy (a případný údaj o předkovi) přidáme klíčové slovo implements a jméno interfacu, o kterém chceme slíbit, že jej třída implementuje. Tedy například: public class MainActivity extends AppCompatActivity implements View.OnClickListener. Následuje definice třídy (ve složených závorkách). Třída implementující OnClickListener dovede zpracovávat události týkající se kliknutí (například na tlačítko). Tato třída pak musí overridovat metodu onClick asi takto:

@Override
public void onClick(View v)
{...}

Metoda onClick je vyvolána, kdykoliv klikneme myší na prvek, pro který je jako tento ovladač příslušná třída definována. Abychom dotyčnou třídu (přesněji jednotlivé její instance) nastavili jako ovladač dotyčné události, nastavíme dotyčný listener. Tedy máme-li například v proměnné B odkaz na tlačítko (o kterém si v dalších odstavcích řekneme, jak jej získat) a jsme-li funkce ve třídě dotyčný listener implementující, jen tlačítku zavoláme metodu setOnClickListener asi takto:

B.setOnClickListener(this);

Kdybychom chtěli nastavit jinou třídu (nežli sebe), museli bychom si vytvořit instanci dotyčné třídy asi takto:

B.setOnClickListener(new ovladaci_trida());

Alternativou tomuto postupu je, pokud prvky klikáme do formuláře v XML, rovnou popsat, která funkce je ovladačem které události. Tento popis píšeme rovnou do XML-souboru, ve kterém je popis dotyčného formuláře:

<Button android:id="@+id/cudlik"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Jen si zaklikej!"
    android:onClick="ovladac_udalosti" />
Funkci ovladac_udalosti přijímající jeden argument typu View pak definujeme v hlavní třídě naší aktivity.

K těmto ovladačům chybí už jen drobnost: Co znamená argument typu View? Jde o údaj umožňující nám zjistit, kdo událost vyvolal. Třída View je abstraktní třída, která je společným rodičem všech nakreslitelných (a tedy vyjevitelných) prvků (textové políčko, tlačítko,...). Tato rodičovská třída vynucuje, aby šlo o prvky, které lze nakreslit. Různé prvky se tedy kreslí různě (stromeček se kreslí jinak nežli domeček, tlačítko se kreslí jinak nežli textové políčko), ale kliká se na ně stejně.

Vstup a výstup

V našem rychlokurzu zbývá už opravdu poslední (leč velmi důležitá) věc: Vstup a výstup. Jak již bylo naznačeno, k prvkům naklikaným na formuláři přistupujeme jinak nežli jsme byli zvyklí z Visual Studia. Zde se k prvkům dostaneme přes tzv. ID, které najdeme v R.id.... Toto ID jsme již definovali v příkladu tlačítka. Tam si povšimněte atributu android:id="@+id/cudlik". Tento atribut jsme takto divně nenastavovali z dlouhé chvíle, ale proto, abychom se k dotyčnému tlačítku dostali pomocí ID R.id.cudlik. Tedy platí, že chceme-li k prvku naklikanému na formulář přistoupit, musíme mu nastavit ID na @+id/ a za lomítko napsat ID, pod kterým budeme k prvku přistupovat.

Předposlední položkou, která nám chybí, je, jak s těmito ID pracovat. To je tentokrát nečekaně snadné. K nalezení dotyčných prvků slouží funkce findViewById, které dotyčné ID (tedy ve formátu R.id.samotne_id předáme). Výsledek volání následně zkonvertujeme na požadovaný typ (jakého předmětné políčko je). Přiřadíme-li výsledek této operace, dostáváme se do podobného stavu, v jakém jsme ve Visual Studiu hned na začátku. Tedy například:

Button button1=(Button)findViewById(R.id.cudlik);
nám umožní nadále odkazovat k dotyčnému tlačítku pod jménem button1.

Jelikož samotný vstup a výstup je zpravidla řešen týmž atributem, či intuitivně se jmenujícím párem funkcí, stačí už snad nyní říci, jak se některé tyto atributy či funkce jmenují (a už budeme schopni tvořit aplikace pro Android bez omezení).
PrvekDatový typHodnota
Textové políčkoEditTextgetText()/setText(String)*
Vypsaný text (label)TextViewgetText()/setText(String)*
Zaklikávátko (checkbox)CheckBoxisChecked()/setChecked(Boolean)
Výběr z možností (radiobutton)RadioGroupgetCheckedRadioButtonId()/setChecked()**
* - výsledku volání při getu je třeba zavolat metodu toString, abychom získali typ String.
** - metoda setChecked se nenastavuje RadioGroupě, ale příslušnému prvku (získanému například dle ID). Ten je typu RadioButton.

Závěrem je možno snad už jen podotknout, že na konkrétní specializované úkoly jsou knihovny, ve kterých jsou třídy i s metodami. Tyto metody budeme volat, abychom se dobrali konkrétních specializovaných úkolů. S pomocí těchto knihoven tedy můžeme komunikovat po síti (HttpURLConnection), pracuje se SQLite databází (SQLiteDatabase) a podobně.

Chceme-li udělat téměř cokoliv netriviálního, potřebujeme k tomu práva (jinak příslušná operace spadne s výjimkou). Práva potřebujeme i k úkonům na pohled banálním (otevření souboru na microSD-kartě, stažení dat z webu). Ono to ale dává smysl. Mobilní telefony bývají z podstaty věci připojené k Internetu a data v nich bývají zpravidla citlivá. Poslat si HTTP-request je také na pohled neškodné - dokud do URI jako parametry skriptu třeba nepošleme současné GPS-souřadnice. Že budeme tato práva používat, musíme deklarovat v Manifestu (soubor AndroidManifest.xml), kde je projekt popsán (co se z něj bude spouštět, jak se to bude jmenovat, jaká práva potřebujeme,...). Tam například přidáme:
<uses-permission android:name="android.permission.INTERNET"/>
chceme-li provést HTTP-request (syntax je tedy jasná, prvek se jmenuje uses-permission, atribut name ve jmenném prostoru Android říká, jaká práva chceme získat). Chceme-li HTTP konekci (ne HTTPS), musíme ještě do manifestu v tagu application nastavit atribut android:usesCleartextTraffic="true".

Ačkoliv o vývoji aplikací pro Android v Android Studiu by bylo možno napsat ještě plno dalších informací, tohle je v tuto chvíli všechno. Následují příklady a ukázky.

Příklad 1:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }
Takto vypadá implicitně vygenerovaná funkce onCreate v Basic Activity. Na začátku vidíme zavolání funkce onCreate v rodiči. Na dalším řádku nastavujeme, co se má zobrazit. Na třetím řádku uchopíme Toolbar. Nás ovšem bude zajímat především situace o 3 řádky dále. Povšimněme si, že tam napřed (jen o dva neprázdné řádky dále) uchopíme FloatingActionButton, což je stisknutelný prvek v pravém dolním rohu displaye. Na dalším řádku tomuto tlačítku nastavujeme ovladač události. Jak vidíte, vytvoříme třídu s overridovanou metodou onClick. Snackbar je ten prvek, který se na chvíli objeví po stisknutí dotyčného tlačítka.

Příklad 2: Tentokrát si nastavíme pro TextView, které máme na layoutu, ID textovepole a takto do toho TextView zapíšeme:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TextView t=(TextView)findViewById(R.id.textovepole);
    t.setText("Nazdar");
}
Povšimněte si, že tentokrát namísto řetězce Hello world vidíme český pozdrav. Zapsali jsme tedy úspěšně text do textového políčka.

Příklad 3: Tentokrát si uděláme příklad, který založí databázi, pokud tato neexistuje a založí v ní tabulku. Pokud již databáze a námi vytvořená tabulka existuje, zapíšeme do ní (a pro kontrolu jeden ze zapsaných údajů opět zobrazíme v TextView).

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView t=(TextView)findViewById(R.id.textovepole);
        SQLiteDatabase mydb=openOrCreateDatabase("Lol",MODE_PRIVATE,null);
        Cursor c=mydb.rawQuery("select DISTINCT tbl_name from sqlite_master where name='studenti' and type='table';",null);
        if(c.getCount()==0)
        {   mydb.execSQL("create table studenti(jmeno varchar(30),prijmeni varchar(60),ID int);");
            t.setText("Nazdar");}
        else
        {   String result="";
            mydb.execSQL("insert into studenti values('Jan','Novak',123);");
            c=mydb.rawQuery("select * from studenti;",null);
            c.moveToFirst();
            while(!c.isAfterLast())
            {   result+=c.getString(0)+"\n";
                c.moveToNext();
            }
            t.setText(result);
        }
    }
Po prvním spuštění se objeví hlášení Nazdar, od druhého dál (kdy už je databáze založena) přibývá počet Janů. Cursor, ve kterém nám přijde výsledek funkce rawQuery, funguje podobně, jak jsme zvyklí z PHP, kde jsme dostávali jeden řádek po druhém. Tentokrát nás metoda moveToFirst nastaví na začátek odpovědi a my přistupujeme k prvnímu řádku (té odpovědi). Metoda getString(i) vrátí údaj z i-tého sloupce jako string (existují i podobné metody vracející údaje jako jiné datové typy). Metoda moveToNext nás posune na další řádek. Metoda isAfterLast oznamuje, zda jsme už došli na konec. U objektů třídy SQLiteDatabase je snad rozdíl mezi metodami rawQuery a execSQL je snad patrný. Ta první vrací objekt třídy Cursor (ze kterého získáme údaje), ta druhá nevrací nic.

Příklad 4: Tentokrát si předvedeme přečtení dat z webu. Příklad bude o něco delší, protože načítání údajů z webu musí probíhat asynchronně. Abychom se dostali k výsledkům, použijeme ovladač pro FloatingActionButton, který známe již z prvního příkladu. Údaje, které jsme přečetli, se nám tak (jako obvykle v TextView) objeví po stisknutí kulatého tlačítka v pravém dolním rohu:

public class MainActivity extends AppCompatActivity {
    String httpres;
    class updater extends AsyncTask {
        @Override
        protected String doInBackground(String... params) {
            int i;
            String vytvoreno = "", httpresr;
            try {
                byte[] rawres = new byte[1000000];
                URL u = new URL("http://jikos.cz/~perm/");
                HttpURLConnection urlConnection = (HttpURLConnection) u.openConnection();
                try {
                    InputStream in = new BufferedInputStream(urlConnection.getInputStream());
                    in.read(rawres);
                    httpres = new String(rawres);

                } finally {
                    urlConnection.disconnect();
                }
            } catch (Exception e) {
                httpres = "Bouchlo to! "+e.toString();
            }
            return "Nazdarek!";
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        httpres="Jeste nic...";
        new updater().execute("Nazdarek!");

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                TextView t=(TextView)findViewById(R.id.textovepole);
                t.setText(httpres);
            }
        });
    }
}
Tentokrát si všimněte, že vláknu běžícímu v pozadí (zbytečně) předáváme stringové parametry. My sice nic předat nepotřebujeme, ale takto bychom mohli poslat parametry celkem libovolného typu. Kdybychom chtěli, aby aplikace zareagovala ke konci běhu vlákna na pozadí, museli bychom se pustit do posílání zpráv (vlákno v pozadí by poslalo zprávu vláknu v popředí, které by tato zpráva probudila). Metoda onCreate vytvoří instanci třídy updater, která dědí od abstraktní generické třídy AsyncTask. Tímto manévrem vyvoláme pozaďové vlákno, ve kterém proběhne metoda doInBackground. K té nyní přesuneme svou pozornost, jelikož provede celou síťovou komunikaci. Tato komunikace se odehrává v try-blocích. Ve finalizátoru komunikaci ukončíme (ať výjimka přijde nebo ne).
Nezapomeňte v manifestu ohlásit potřebná práva (přístupu k Internetu) a také deklarovat, že budeme používat nešifrované HTTP-spojení (nebo to bouchne). Občas se stane, že údaje o změně v Manifestu nejsou do emulatoru nahrána. V tom případě je potřeba aplikaci z emulatoru odinstalovat a znovu celou instalovat.

Cvičení: Modernizujte kafomat na mobilní aplikaci. :-)

Microsoft Visual Studio

Vývoj aplikací pro Android umí i Microsoft Visual Studio, ovšem až v posledních verzích. Chceme-li vyvíjet aplikace v tomto studiu, při instalaci (ve Visual Studio Installeru) je třeba zakliknout Mobile Development with .NET (buďto při nové instalaci, nebo v sekci Installed vybrat Modify, v tabu Workloads vybrat Mobile Development with .NET a nechat instalovat).

I Visual Studio má AVD Managera. Ten se pokusí založit si vlastní hromádku emulatorů. Nechceme-li emulatory udržovat dvakrát, můžeme si otevřít ten AVD Manager, kde již emulatory nastavené máme (v mém případě v Android Studiu). Odtud emulator pustíme a Visual Studio je schopno komunikovat s již takto běžícícm emulatorem (samo si je ale patrně schopno spustit jen své emulatory). V současné konfiguraci jsem zpozoroval, že zatímco Android Studio zabírá něco málo přes 1 GB RAM, Visual Studio je přibližně poloviční. Pokud tedy máte potíže s velikostí paměti, může být Visual Studio jistou výhodou.
Novy projekt

Nový projekt (pro Android) vytvoříme výběrem Android App (Xamarin). Následně se objeví prostředí velmi podobné Android Studiu. Kód se ovšem tentokrát píše v C#. Jelikož Visual Studio a jazyk C# už znáte, opět stačí popsat formátu vstupu a výstupu. Třídy, objekty, metody a atributy k tomu používané však téměř splývají s tím, co jsme používali v Javě v Android Studiu, takže téměř není co vykládat. Chceme-li se dostat k prvku formuláře, tentokrát nejde o třídu R, ale o třídu Resource. Její podtřída, kde máme hledat ID, se ale již jmenuje (správně) Id. Funkce, která podle ID hledá, se jmenuje findViewById, ovšem je generická a při zavolání ji parametrizujeme typem dotyčného prvku. Její výsledek tak narozdíl od Android Studia nemusíme přetypovávat. Obsah textového okénka se nastaví metodou setText. Ke čtení ovšem nemáme metodu getText, nýbrž wrapper Text, který můžeme jak číst tak nastavovat (a můžeme jej tedy použít i k nastavení textu), což jsou typické rozdíly mezi Javou a C#. Tedy k textovému políčku se dostaneme takto: TextView t = FindViewById<TextView>(Resource.Id.textovepole); a jeho obsah nastavíme t.Text="Nazdarek!";
Novy projekt
I Visual Studio má klikátko pro návrh formulářů:
Klikatko na navrh formulare

Jelikož toto prostředí znáte výrazně lépe, přidáme jen jeden souhrnný příklad demonstrující stažení souboru z webu a práci s databází najednou (jako v posledních dvou příkladech minulé sekce dohromady).

Příklad:

    public class MainActivity : AppCompatActivity
    {   class updater : AsyncTask
        {   protected override string RunInBackground(params string[] p)
            {   byte[] rawres = new byte[10000];//Zde opet delame tridu pracujici na pozadi
                char[] cres = new char[10000];//Pole charu lze zkonvertovat do stringu, pole bytu ne
                URL u = new URL("http://jikos.cz/~perm/");//temer stejne jako v Jave
                HttpURLConnection urlConnection = (HttpURLConnection)u.OpenConnection();
                urlConnection.Connect();//Navaz spojeni
                System.IO.Stream j= urlConnection.InputStream;
                j.Read((byte[])rawres);//Precti odpoved jako stream
                for (int i = 0; i < rawres.Length; i++)
                    cres[i] = (char)rawres[i];//zkonvertuj do stringu
                async_res+= new string(cres);//a pridej k vysledne odpovedi
                MainActivity.uz = true;
                return "nazdarek";
            }
        }
        public static string async_res = "";//sem vlakno z pozadi zapise vysledek
        public static bool uz = false;//nase synchronizacni primitivum

        protected override void OnCreate(Bundle savedInstanceState)
        {   base.OnCreate(savedInstanceState); //v C# rikame base, ne super
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            SetContentView(Resource.Layout.activity_main);//Opet nastavime, co se ma zobrazit
            TextView t = FindViewById(Resource.Id.textovepole);
            new updater().Execute("nazdarek");
            SQLiteDatabase mydb = OpenOrCreateDatabase("pokus",  Android.Content.FileCreationMode.Private, null);
            ICursor res = mydb.RawQuery("select DISTINCT tbl_name from sqlite_master where name='studenti' and type='table';", null);
            res.MoveToFirst();
            if (res.IsAfterLast)
            {   mydb.ExecSQL("create table studenti (jmeno varchar(30),prijmeni varchar(50),narozeni date);");
                async_res += "\nZakladam databazi\n";
            }else{
                mydb.ExecSQL("insert into studenti values('Jan','Novak','2000/03/22');");
                res = mydb.RawQuery("select * from studenti;", null);
                res.MoveToFirst();
                async_res += res.GetString(0) + "\n";
            }
            FloatingActionButton fab = FindViewById(Resource.Id.fab);
            fab.Click += FabOnClick;
        }

        private void FabOnClick(object sender, EventArgs eventArgs)
        {
            TextView t = FindViewById(Resource.Id.textovepole);
            t.Text = async_res;
        }
    }

Pozor, sice jsme na to už snad upozornili, ale vlákno běžící v pozadí nesmí zasahovat do zobrazování. Proto nemůže přímo vypsat výsledek svého snažení (a my toto omezení workaroundujeme tím, že se ovladač nějakého tlačítka vždycky podívá, jestli už výpočet doběhl, a pokud ano, výsledek zobrazí). Že program dělá, co má, v tomto případě poznáme podle toho, že pokaždé vypíše obsah staženého souboru a poprvé k tomu přidá poznámku, že zakládá databázi, kdežto od druhého spuštění dále přidá obsah prvního políčka, tedy jméno "Jan". Statický boolský atribut uz slouží jako zámek. Až má vlákno běžící v pozadí všechno spočítané, nastaví ji na true. Nicméně při implementaci funkce FabOnClick, která výsledky zobrazí, se ukázalo, že přečtením částečných výsledků žádná škoda nevznikne, proto tato funkce ani nekontroluje stav zámku. Kdybychom program neměli jen pro vlastní potřebu, bylo by potřeba zamykání dodělat.

Cvičení: Přepište kafomat do C# ve Visual Studiu (a všimněte si, že kód vlastně zůstává téměř stejný).

Aplikace pro iPhone

Vyvíjejí se podobně, a to buďto opět x využitím Xamarinu ve Visual Studiu, nebo je třeba si obstarat další vývojové studio. Vývoj probíhá buďto v Objective C nebo Swift (verze 2) a jde o další prostředí na způsob toho, co jsme už viděli. Jelikož se společnost Apple průběžně pokouší svým uživatelům vývoj vlastních aplikací na vlastním Macu pro vlastní iPhone znepříjemňovat (až znemožňovat), alespoň letos bude vývoj aplikací pro iPhone pouze dobrovolný a podléhá samostudiu.