Taula de continguts

Android

Android és un sistema operatiu per a dispositius mòbils impulsat per Google. Està basat en Linux, però conté també mòduls privatius.

android.jpeg

Referències:

, , , , , , ,

Altres articles d'Android a aquesta web

2018/08/24 11:31 ENRIC MIEZA SANCHEZ
2023/01/20 17:59 ENRIC MIEZA SANCHEZ
2021/10/15 15:02 ENRIC MIEZA SANCHEZ
2023/09/25 14:33 ENRIC MIEZA SANCHEZ
2023/12/18 16:01 ENRIC MIEZA SANCHEZ
2022/09/16 14:41 ENRIC MIEZA SANCHEZ
2019/01/01 16:36 ENRIC MIEZA SANCHEZ
2022/12/16 19:14 ENRIC MIEZA SANCHEZ
2022/08/18 09:11 ENRIC MIEZA SANCHEZ
2023/07/04 16:08 EVA BARBEITO ANDRADE
2024/05/06 21:35 ENRIC MIEZA SANCHEZ
2021/02/25 18:20 ENRIC MIEZA SANCHEZ
2022/07/15 22:16 Joan Iglesias
2022/10/10 10:38 ENRIC MIEZA SANCHEZ
2022/04/19 18:06 ENRIC MIEZA SANCHEZ
2024/01/26 15:15 ENRIC MIEZA SANCHEZ
2022/10/10 10:56 ENRIC MIEZA SANCHEZ
2024/01/22 13:16 ENRIC MIEZA SANCHEZ
2023/12/15 10:11 ENRIC MIEZA SANCHEZ
2021/11/28 13:25 ENRIC MIEZA SANCHEZ
2022/12/13 13:28 ENRIC MIEZA SANCHEZ


Desenvolupament Android

Hi ha diverses opcions per desenvolupar per a Android, fem un breu resum de les més habituals:

Eines de desenvolupament per a Android:

Android Studio i les àrees de treball:

Alguns aspectes inicials

La programació per a Android utilitza alguns recursos molt habituals com l'aniuament de classes i la sobreescriptura de mètodes en el moment de la instanciació.

Fes-li un cop d'ull a aquest codi d'exemple per clarificar certs aspectes de la programació Java aplicada a Android, abans d'aprofundir en la programació Android. Ens aclarirà l'aniuament de classes i com emprar adequadament els punters this.

Tornem a referenciar-nos als apunts de desenvolupament per a Android de la IOC.

En particular, para atenció als conceptes següents:

Cicle de vida d'una Android Activity

Cicle de vida d'una Android Activity. Font: apunts IOC.


Instal·lació de les eines de desenvolupament

Per poder gestionar l'entorn de compilació adequadament convé que augmentem el nombre de inodes que el sistema operatiu pot tenir oberts. Sobre Debian/Ubuntu/Centos/RHEL es fa així:

$ sudo sh -c "echo fs.inotify.max_user_watches=524288 >> /etc/sysctl.conf"
$ sudo sysctl -p

L'eina oficial de Google és Android Studio. En Ubuntu s'instal·la fàcilment mitjançant el gestor de paquets snap:

$ snap install android-studio --classic

La instal·lació implica la descàrrega de paquets força voluminosos:

  1. El software d'Android Studio
  2. Les llibreries d'Android necessàries (SDK): us farà acceptar una llicència abans de descarregar. L'eina SDK Manager us ajudarà a gestionar les versions i llicències.
  3. Les imatges binàries d'Android per a l'emulador del dispositiu mòbil. L'eina AVD Manager us ajudarà a gestionar-ho.


Primeres passes

  1. Mira els apartats de més amunt i l'article Java per disposar de la instal·lació del IDE i llibreries adequades.
  2. Es recomana utilitzar algun tipus de sistema de control de versions. El més popular és Git i la web www.github.com . Es pot fer les operacions més comunes al menú VCS de IntelliJ IDEA.
  3. Si ja has llegit els apunts per aclarir els conceptes previs, podem passar a l'acció. Crea una aplicació amb una empty views activity des del menú File→New→Project→Android
  4. Afegeix un botó (Button). Busca els arxius de recursos R.layout (carpeta res→layout i arxiu activity_main.xml) per afegir-ho gràficament (millor no complicar-nos la vida abans d'hora ;).
  5. Afegeix un OnButtonClickListener al mètode onCreate de la MainActivity.java. En principi, el botó no fa res quan el premem. Aquest listener ens permetrà captar l'event OnClick i fer alguna cosa. A la documentació del mateix Button trobaràs un exemple de com fer-ho.
  6. Afegeix un Toast. El primer que podem fer per comprovar que l'event OnClick s'està processant és construir un Toast, que son els missatges que apareixen en un globus temporalment sobre la app, a la part inferior de la pantalla habitualment. La manera més fàcil de crear-lo és amb Toast.makeText, i després fer-li un show a l'objecte perquè es visualitzi.


Una aplicació complerta

Requeriments joc "endevina el número"

  1. La app ens oferirà un casella d'entrada de text i un botó.
  2. La app crearà un número aleatori entre 1 i 100 que el jugador haurà d'endevinar.
  3. Cada cop que l'usuari fa una temptativa es compta un nou intent.
  4. Quan l'usuari endevina del número que la app ha «pensat», s'acaba la partida.
  5. La puntuació resultant és el nombre d'intents que ha fet l'usuari fins a endevinar el número. Quan més petita és, millor.
  6. La app demanarà el nom a l'usuari per posar-ho a la taula de rècords. L'usuari podrà posar el seu nom o desestimar-ho.
  7. La app torna a «pensar» un nou número aleatori i comença nova partida.

Aplicació principal (joc)

  1. Crea una nova aplicació amb una sola Activity (Empty Views Activity).
  2. Ves al layout i crea la casella d'entrada per al número. Es tracta d'un objecte TextEdit, però volem restringir-ho perquè només ens deixi entrar nombres i no lletres. Entre els objectes disponibles al IDE hi ha un de predefinit que ja ens ho facilita.
  3. Crea un botó i un listener perquè l'usuari validi l'entrada del número. Prova que funciona amb un Toast, per exemple.
  4. Investiga com crear un nombre aleatori en Java. Crea un al mètode onCreate de l'Activity i emmagatzema-ho en una propietat de l'objecte per tenir-ho disponible quan calgui.
  5. Anem al codi principal del joc, que estarà principalment al onClick del listener. Quan l'usuari entri un número li mostrem un Toast dient-li si el nombre que busca és major o menor.
  6. Afegeix un AlertDialog per avisar l'usuari de quan acaba la partida i felicitar-lo.
  7. Quan s'acaba la partida, es regenera el número aleatori i es torna a jugar.
  8. Posa un TextView per anar indicant a l'usuari l'historial dels intents i resultats que ha obtingut.
  9. Per aconseguir un scroll en la pantalla de l'historial podem posar un ScrollView i a dins seu posar el TextView.
  10. Implementa un comptador d'intents que es visualitzi en algun racó de la pantalla. Ens servirà per després fer el ranking.

Podem millorar la jugabilitat amb alguns detalls més.

  1. Per facilitar el joc a l'usuari, esborrem el número del EditText quan l'usuari fa un intent d'endevinar (si no, l'usuari haurà d'esborrar-ho manualment).
  2. Es pot millorar la jugabilitat si implementem que el joc detecti la tecla Enter del teclat de pantalla.
    • S'implementa millor amb un OnEditorActionListener ja que el OnClickListener amaga el teclat al prèmer ENTER.
    • Us deixo igualment la doc de OnKeyListener sobre el EditText (OJU! no al button o altres llocs!)
      • Encara que detecteu correctament el Enter, us passarà que trobareu un ACTION_UP i un ACTION_DOWN, pel que la callback es cridarà dos cops. Filtreu aquests dos esdeveniments i avalueu l'entrada de l'usuari només en un dels dos (preferentment ACTION_DOWN). També convé recuperar el focus dintre del EditText perquè l'usuari no hagi de clicar de nou el widget cada cop que fa un intent.

Xivatos

En molts moments podem necessitar volcar dades per poder visualitzar com evoluciona la nostra aplicació. NO feu System.out.println!

La funció que busqueu son els Logs d'Android. Ens mostrarà els missatges en la consola inferior del Android Studio (pestanya Logcat). Hi ha diversos nivells segons la «gravetat» del missatge: (d)epuració, (i)nformació, (w)arning, (e)rror.

Un exemple típic és el d'informació:

Log.i("INFO","El número introduït és: " + num);

Rècords "Hall Of Fame"

Per a la taula de rècords crearem una nova Activity i la obrirem amb un objecte Intent. L'objecte més adient per mostrar una llista de rècords seria una RecyclerView (la versió actualitzada de ListView) però té força complexitat utilitzar-la. Farem 3 versions amb dificultat incremental per anar sofisticant la nostra app i anar veient els diferents recursos que podem emprar:

  1. Versió més simple amb una TextView
  2. Versió millorada amb un TableLayout
  3. Versió pro amb RecyclerView

Abans, però, caldrà crear una nova Activity per poder posar-hi el rànking:

  1. Crear nova Activity amb el seu layout associat (arxiu XML):
    File -> New -> Android Activity -> Empty Views Activity
    • ULL! Si creeu la Activity manualment, cal afegir-la també manualment a l'arxiu AndroidManifest.xml
  2. Afegim Intent: aquest objecte ens serveix per posar en marxa una altra Activity. Així podrem passar de la partida a la taula de rècords. Caldrà que l'activem quan acabem la partida, o bé podem afegir un botó nou per visualitzar la taula de rècords. Aquest exemple per obrir una nova activity t'ajudarà.
  3. Per tal que puguem afegir una nova entrada a la taula de rècords caldrà que el MainActivity passi el paràmetre del nº d'intents que el jugador ha realitzat. Per passar paràmetres examina el mètode putExtra() de l'objecte Intent.
  4. Diàlegs o Dialogs: abans d'obrir la RecordsActivity convé que demanem a l'usuari si vol o no afegir el seu rècord a la taula. El widget Dialog ens permetrà fer això, i també demanar el nom de l'usuari. Tenim 2 estratègies diferents per implementar el Dialog que necessitem:
    1. Particularitzar un AlertDialog per afegir-li un EditText programàticament, sense xml, al instanciar la classe. És la forma més senzilla i amb menys codi.
    2. Dialog personalitzat (més complicat): a la documentació oficial hi ha diversos Dialog prefabricats disponibles. Però nosaltres necessitem fer-ne un de personalitzat que ens demani el nom i prou. Aneu a la secció de la documentació dels diàlegs personalitzats.

Model de dades

Abans de mostrar la taula de rècords, cal que tinguem clar quin serà el nostre model de dades segons el paradigma MVC (Model-Vista-Controlador). Haurem de guardar una llista amb, al menys, un parell de valors en cada posició (nom, número d'intents i potser més tard el temps emprat).

La idea és que les dades dels rècords han de ser completament desacoblades de la interfície gràfica (model MVC). És una bona pràctica, però a més, no ens queda mes remei, ja que les diferents activities s'esborren: la informació gràfica i els widgets són efímers i no podem fiar-nos de que estiguin sempre allà.

Tenim diverses alternatives per guardar les dades.

Depenent de si volem permetre entrar el mateix nom 2 cops podrem utilitzar un diccionari (map) o bé una llista. Aquest objecte ha d'estar completament desacoblat de la interfície gràfica, i hauria de ser utilitzable en qualsevol altre entorn.

Variable estàtica

Les dades del rànking les haurem de ubicar com a variable de classe dins d'una de les activities. Això no garateix que les dades es mantinguin, ja que a l'obrir una 2a activity, la primera es tanca i s'esborra. Per garantir que tenim les dades sempre disponibles haurem de fer l'atribut static:

  class RecordActivity extends AppCompatActivity {
      static ArrayList<Record> records = new ArrayList<Record>();

Versió TextView

A la nova Activity només en caldrà una TextView on mostrar els rècords i un botó per tornar a la MainActivity.

La idea és ben simple: en el onCreate() de la nostra RecordActivity iterarem per la llista de rècords que hem elaborat, i farem un String que mostrarem dins de la TextView.

Pot semblar un tant ineficient, però és així com funcionen les activities, cada cop que canviem a una de nova, les altres s'eliminen (per estalviar memòria) i llavors cal recrear tota la llista i tots els widgets.

Versió amb TableLayout

Fins ara hem creat layouts estàtics, que mostren dades dinàmiques, però on el widgets són fixes.

En aquesta versió el repte està en utilitzar TextViews dinàmics segons el nombre de rècords. Aquests els col·locarem en un TableLayout.

Haurem de crear un nombre de files dinàmic i, a cada fila, posar 3 TextView (nom, intents i temps). Per tant, aquests TextView es crearan dinàmicament, de forma programàtica.

Per dur-ho a terme, teniu aquest exemple d'un post de stackoverflow per afegir TextView programàticament.

Versió ListView

Una ListView és una View obsoleta però que ha tingut molta utilització i es manté per backward compatibility. Com que el seu substitut actual, la RecycleView és una mica complicada d'utilitzar, farem primer aquest exemple amb la ListView antiga per entendre el funcionament.

Les ListView son widgets més complicats d'utilitzar perquè ha d'intercalar un nou element anomenat Adapter i que ens gestionarà el reciclat d'elements de la llista. Això es fa per ocupar la mínima RAM possible, molt important en aplicacions mòbils.

Llegeix l'article Android ListView i prova l'exemple proposat per entendre bé com funciona la ListView. Després mira d'integrar-ho al teu projecte d'endevinar el número.

Versió RecyclerView

La clàssica ListView ha quedat obsoleta i ara s'utilitza de forma genèrica la RecyclerView, que pot agafar diferents layouts i disposicions.

Malauradament és més complicada d'utilitzar que la ListView, i el mínim exemple per crear el Adapter resulta més complex que el ArrayAdapter que feiem servir.

Igualment us deixo les referències per si els més agosarats voleu utilitzar-la:

  1. Per si de cas, us deixo aquest altre exemple on s'han esforçat a fer un RecyclerAdapter més simple (igualment no deixa de ser força més llarg que el de la ListView).


Persistència

Pots llegir sobre els diferents espais interns i externs d'emmagatzematge d'Android a l'article Android Imatges.


Modes Portrait i Landscape

Com sabeu, la pantalla del mòbil pot estar en mode Portrait (vertical) o Landscape (apaïsat). Quan una app canvia d'un estat a l'altre és com si reiniciéssim la app de nou, i cal recrear tota l'estructura dels objectes que tenim en memòria.

Així, al passar d'un mode a l'altre, la nostra Activity es destruirà (cridant abans al mètode onSaveInstanceState) i es tornarà a crear (cridant onRestoreInstanceState). Aquests dos mètodes ens permetran guardar i restaurar les dades en memòria, per tal que sembli que l'aplicació manté el seu funcionament amb continuïtat.

Crida a funcions de save/restore quan l'app canvia d'estat.

Android ens dona aquesta eina onSaveInstanceState que ens facilita guardar les dades que ens calgui de la nostra app en un Bundle (una senzilla BD local persistent tipus key-value).

Aquí podeu veure un exemple d'ús d'aquests mètodes, tot i que cal tenir en compte que les dades que tenim són ArrayList, i que no és un tipus primitiu (com int, String, etc.). Caldrà que els nostres objectes implementin Serializable i utilitzar Bundle.getSerializable i Bundle.putSerializable.

Alternativa: utilitzar ViewModel

Els ViewModel és una manera de facilitar aquesta gestió de dades quan es destrueix la View al passar entre diferents modes de visió portrait/landscape. Enlloc de manualment guardar les dades amb el mètode onSaveInstanceState tenim l'alternativa de les ViewModel. Aquests són objectes que estan pensats per sobreviure al canvi d'estat, i són independents de l'Activity que les ha generat.

La idea principal és separar les dades pures o model de la interfície d'usuari. Així, es podrà destruir la interfície, i les dades (descarregades de serveis d'internet, lectura d'arxius, captura de sensors, etc.) seguiran allà.

Pots llegir la documentació oficial de ModelView per més informació i exemples de com utilitzar-ho.

La idea és que el Fragment o Activity demana una instància del ViewModel al ViewModelProvider, que és l'objecte que gestiona la persistència. Després aplica un Observer que facilitarà que quan hi hagi un canvi en el model, es cridi una callback per actualitzar la view.


Fragments

Hi ha moltes aplicacions que requereixen diverses «pestanyes» amb diferents funcions, cadascuna amb el seu pertinent layout. La forma típica de treballar d'Android és utilitzar les classes Fragment i FragmentActivity. Un Fragment representa una porció de la interfície que gestiona una FragmentActivity.

Aquest exemple bàsic de la documentació oficial es pot veure el codi en aquest Exemple de Fragment. Conté:

  1. Una DetailsActivity, imprescindible una Activity contenidora i que disposa del handlers adequats, particularment el FragmentManager.
  2. Un ListFragment (esquerra) per tenir la llista de pestanyes o sub-views i seleccionar-los.
  3. Un DetailsFragment que canvia la view de la dreta quan es clica a la ListFragment.

Els Fragment es comporten similarment a les Activity, i tenen els mateixos mètodes onCreate, on Stop i alguns més propis del Fragment, com podem veure en aquesta imatge del seu lifecycle:

Cicle de vida d'un Fragment i la seva Activity contenidora.

Com es pot veure al fragment lifecycle hi ha algunes crides més que impliquen les views

Potser la manera més pràctica de començar és utilitzar una plantilla de les que ens proporciona Android Studio al principi del projecte, i modificar-la adientment. Tingueu en compte que si ho feu així, la plantilla també conté els objectes ViewModel descrits més amunt quan hem parlat dels modes landscape i portrait.


Captura d'imatges

Pots seguir investigant sobre la captura d'imatges amb la càmera del dispositiu mòbil a l'article Android Imatges d'aquesta mateixa wiki.


Més coses interessants


Launcher Icons

Per personalitzar la icona que es veurà al launcher del dispositiu mòbil podeu llegir aquest post sobre com canviar el launcher icon.

Podeu fer servir aquesta fantàstica eina: https://icon.kitchen


Depuració Wireless

Seguiu aquest article oficial de ADB per connectar-vos al vostre dispositiu mòbil via Wi-Fi.


Android i RMI

Un recurs molt habitual d'utilitzar per comunicació entre dispositius és la llibreria RMI de Java o Remote Method Invocation. Malauradament, aquesta llibreria molt estàndard en altres aplicacions Java no està suportada en Android.

Una possible alternativa és utilitzar LipeRMI, una implementació lleugera de RMI que, malgrat fa anys que no té actualitzacions, funciona per a Android (a data d'Octubre de 2020). Podeu veure un exemple de com utilitzar-la en aquest thread de (com no) Stackoverflow.

Importar LipeRMI a Android Studio

Per importar LipeRMI a Android Studio, descarregueu el jar de LipeRMI i feu les següents passes:

  1. Canviar la folder structure a Project
  2. Anar a la carpeta app/libs, i si no hi és, crear-la.
  3. Arrossegar-hi l'arxiu lipermi-X.Y.jar
  4. Clicar amb el botó dret sobre el nou arxiu lipermi-X.Y.jar i clicar Add as library.
  5. Ja pots fer servir lipeRMI al teu projecte Android.

Implementar codi de servidor i client

Seguiu l'exemple de Stackoverflow per implementar el codi en client (Android) i servidor (podeu fer servir Eclipse o compilar-ho directament amb javac).

Algunes coses a tenir en compte:


Text to Speech

Pots mirar més sobre reconeixement i síntesi de veu a l'article Android Speech.


Flexbox Layout

A part dels tipus de layout treballats durant el curs (ConstrainLayout, LinealLayout, TableLayout) hi ha aquest molt particular que ens pot ser interessant en determinats projectes per imitar el comportament del conegut FLEXBOX de CSS.