Jak se kompilují a spouští zdrojové kódy – C++ -> Java -> Android
V tomto článku si probereme základní principy toho, jak fungují kompilátory a běhové prostředí. Objasníme několik důležitých pojmů, jako například co je to strojový jazyk a podíváme se na rozdíly, podle toho jak jsou spouštěny kódy pro C++, Javu, nebo Android.
C++
Dejme tomu, že máme nějaký jednoduchý zdrojový kód v jazyce c++, např hello.cpp, který akorát vypíše „hello word“. Nyní chceme tento kód spustit. Klasicky ho tedy zkompilujeme za pomocí nástroje g++ dostupného na našem systému. Při této kompilaci se děje několik dílčích procesů, které lze spouštět i samostatně, v první řadě dochází k takzvanému předzpracování (preprocessing).
Preprocessing: g++ -E
Předzpracování zpracovává všechny direktivy, tedy #include, #define apod.. Tato fáze do našeho jednoduchého kódu o pár řádkách přidá řadu funkcí z includovaných zdrojových souborů, čímž můžeme dostat i soubor o několika tísíce řádkách a mimo jiné například i nahradí všechny direktivy #define za požadovaný text. Tento soubor pak putuje do další fáze – kompilace.
Compilation: g++ -S
Kompilace převádí kód do tzv Assembleru, (česky: jazyku symbolických adres). Tento kód je ještě pro člověka čitelný, avšak typicky se nám v něm nebude pracovat tak dobře jako v C++. Kdo někdy programoval v assembleru, ví že zde máme již jen jednodušší sadu instrukcí (například aritmetické operace, práce s pamětí a registry: MOV, ADD, …), která se navíc vztahuje ke konkrétnímu procesoru. To znamená, že tento kód nám již nebude fungovat na různých procesorech, ale pouze na tom, vzhledem ke kterému byl vytvořen. Assembler se tedy nachází mezi jazykem čitelným pro člověka (C++) a jazykem čitelným pro procesor (strojový jazyk), jehož binární kód získáme v další fázi.
Assemble and Linking: g++ -c
V této fázi převádíme kód assembleru do binární kódu (strojového jazyka), který je pro běžného člověka nečitelný – (kdyby jsme se do něj podívali, bude se jednat o nečitelnou sadu jedniček a nul). Daný procesor tento kód však dokáže efektivně a rychle zpracovat, tedy rozpoznat v něm požadované instrukce a ty vykonat. Binární soubory, které jsme získaly v této fázi jsou typu .object, avšak ještě nejsou spustitelné. Konečný spustitelný soubor vytvoří až linker zkombinováním jednoho nebo více souborů typu .object. Linker se také stará o odstranění duplicit vzniklých v souborech při kompilaci a podobně.
Shrnutí:
C++ se často označuje za nativní jazyk, jelikož kompiluje kód do strojového jazyka konkrétního procesoru. Nepotřebujeme zde tedy žádné běhové prostředí, což nám může poskytnout vyšší rychlost zpracování těchto instrukcí, avšak musíme si sami řešit například alokace a dealokace paměti. Výsledný spustitelný soubor po zkompilování pak spustíme pouze na systému ke kterému byl určen.
Java
Hlavní výhodou Javy je to, že je multiplatformní. Dosáhne se toho za použití virtuálního stroje – Java Virtual Machine. Místo toho, aby jsme kód kompilovali do strojového jazyka konkrétního systému, zkompilujeme ho do jazyka pro JVM (.class soubory). Takový kód se nazívá java bytecode a můžeme spustit na kterékoliv platformě na které běží tento virtuální stroj, který ho pak interpretuje do jazyka fyzického stroje. Konkrétní implementace JVM jsou tedy různé, avšak zkompilovaný java kód pak spustíme na kterékoliv z nich.
Compilation: javac
O kompilaci se nám tedy postará kompiler Javac, který náš soubor (např hello.java) zkompiluje do byte kódu typu .class, čitelného na jakékoliv implementaci JVM.
Execution: java – JVM
Virtuální stroj, pak obsahuje class loadery, které dokážou .class soubory načíst do virtuální paměti (JVM memory) a spustit za použití komponentů obsažených v JVM execution engine, kam patří například i garbage collectory
JVM kromě spuštění a interpretace těchto tříd tedy řeší i spoustu dalších věcí, včetně práce s pamětí. V této paměti jsou často zmiňovány oblasti Heap (kde se uchovávají večkeré vytvořené objekty), nebo Stack (který si zapamatovává aktuální spouštění metod pro každé vlákno a vidíme ho, pokud nám Java vyhodí nějakou výjímku).
Shrnutí:
Java se označuje jako multiplatformní managed jazyk, jelikož neřešíme práce s pamětí jako např v C++. Pro spuštění java aplikace na našem systému musíme mít však nainstalované běhové prostředí (Java Runtime Enviroment, které pak obsahuje virtuální stroj JVM).
Android
Systém Android lze rozdělit do několika vrstev. Na nejnižší vrstvě máme Linuxové jádro, které obsahuje veškeré nejdůležitější ovladače zařízení, řízení procesů, přístupu k síti a podobně.
Na vyšší vrstvě se nachází nativní knihovny (tedy napsané např v C/C++). Jedná se například o knihovnu sqlite, která zajišťuje práci s lokální databází. K těmto knihovnám se typicky přistupuje až z vyšších vrstev přes různé Java rozhraní.
Abychom zde mohli instalovat a spouštět klasické androidí .apk aplikace se základem napsaným v Javě, potřebujeme běhové prostředí. Dříve se používal Dalvik Virtual Machine, ale v dnešní době už se používá spíše jen Android RunTime. Tyto běhové prostředí (hlavně DVM) fungují podobně jako JVM, avšak jsou optimalizovány pro mobilní zařízení (tedy například nižší spotřebu baterie). V případě androidu jsou již známe .class soubory překompilovány do efektivnějších .dex souborů (dalvik executable), které již umí toto prostředí načíst a interpretovat.
Rozdíl mezi DVM a ART je hlavně v tom, že DVM funguje spíše jako interpret (Just In Time kompilace), který byte code za běhu překládá do strojového jazyka procesoru. Zatím co ART se zaměřuje na předkompilaci (Ahead Of Time), tedy byte code je zkompilován do strojového jazyka už při instalaci aplikace. Důsledkem je, že ART má pomalejší instalaci aplikací, ale aplikace pracují rychleji (jelikož je není třeba za běhu interpretovat).
Aby vývoj android aplikací byl pohodlnější, máme zde k dispozici vrstvu s aplikačním frameworkem od google, který například zpracovává aktivity a navigaci mezi nimi. (ale i spoustu dalších věcí)
V nejvyšší vrstvě pak máme veškeré systémové aplikace, včetně námi implementovaných aplikací.
Compilation: gradlew, gradle
V případě android aplikací typicky ke kompilaci a vytvoření samotného .apk používáme nějaký pokročilejší nástroj (tedy například gradle, nebo jeho nadstavbu gradle wrapper). Ten se kromě samotného sestavení .apk stará i o stáhnutí všech dodatečných knihoven. Do výsledného .apk souboru jsou zabaleny zmiňované .dex soubory, včetně dalších ne-java souborů jako jsou layouty, nebo různé další přílohy, které pak fungují skrze android aplikační framework.
Shrnutí:
Programování aplikaci android lze tedy spíše považovat hlavně jako práci s aplikačním frameworkem, který je k dispozici na všech android zařízeních. Tento framework ovládáme za pomocí dostupného Java Android SDK (ale jsou i jiné možnosti). Framework pak dosahuje jeho plné funkčnosti za použití nižších vrstev systému android.