Android és un sistema operatiu per a dispositius mòbils impulsat per Google. Està basat en Linux, però conté també mòduls privatius.
Referències:
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 |
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:
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:
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:
res→layout
i arxiu activity_main.xml
) per afegir-ho gràficament (millor no complicar-nos la vida abans d'hora ;).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.Toast.makeText
, i després fer-li un show
a l'objecte perquè es visualitzi.
Activity
(Empty Views Activity).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.onCreate
de l'Activity i emmagatzema-ho en una propietat de l'objecte per tenir-ho disponible quan calgui.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.TextView
per anar indicant a l'usuari l'historial dels intents i resultats que ha obtingut.ScrollView
i a dins seu posar el TextView
.Podem millorar la jugabilitat amb alguns detalls més.
EditText
quan l'usuari fa un intent d'endevinar (si no, l'usuari haurà d'esborrar-ho manualment).OnClickListener
amaga el teclat al prèmer ENTER.EditText
(OJU! no al button o altres llocs!)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.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);
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:
TextView
TableLayout
RecyclerView
Abans, però, caldrà crear una nova Activity
per poder posar-hi el rànking:
File -> New -> Android Activity -> Empty Views Activity
Activity
manualment, cal afegir-la també manualment a l'arxiu AndroidManifest.xml
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à.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
.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: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.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.
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>();
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.
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.
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.
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:
ListView
).
Pots llegir sobre els diferents espais interns i externs d'emmagatzematge d'Android a l'article Android Imatges.
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.
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
.
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.
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é:
DetailsActivity
, imprescindible una Activity
contenidora i que disposa del handlers adequats, particularment el FragmentManager
.ListFragment
(esquerra) per tenir la llista de pestanyes o sub-views i seleccionar-los.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:
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.
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.
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
Seguiu aquest article oficial de ADB per connectar-vos al vostre dispositiu mòbil via Wi-Fi.
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.
Per importar LipeRMI a Android Studio, descarregueu el jar de LipeRMI i feu les següents passes:
Project
app/libs
, i si no hi és, crear-la.lipermi-X.Y.jar
lipermi-X.Y.jar
i clicar Add as library.
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:
package
de Java.interface TestService
i enlloc de passar-li i rebre un String
, passar-li i retornar un objecte sempre i quan sigui serialitzable, o sigui que implementi Serializable
. Algo així com:public interface TestService { public MyMessage getResponse(MyMessage msg); }
public class MyMessage implements java.io.Serializable { public String message; public String author; public MyMessage(String _msg, String _author) { message = _msg; author = _author; } }
Pots mirar més sobre reconeixement i síntesi de veu a l'article Android Speech.
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.