====== 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?direct&400 }} Referències: * [[https://ioc.xtec.cat/materials/FP/Materials/2252_DAM/DAM_2252_M08/web/html/index.html|Apunts de MP08 de DAM del IOC]]. {{tag> #Dam #DamMp08 #DamMp08Uf1 #DamMp08Uf01 #DamMp08Uf2 Android mobile java }} ===== Altres articles d'Android a aquesta web ===== {{topic> android}} \\ ===== Desenvolupament Android ===== Hi ha diverses opcions per desenvolupar per a Android, fem un breu resum de les més habituals: * **Java** sol ser la opció més comuna per desenvolupar apps per a Android. Es compilen sobre un processador virtual corresponent a la JVM (Java Virtual Machine). És el que s'anomena [[https://es.wikipedia.org/wiki/Android_Runtime|Android Runtime]]. * [[https://stackoverflow.com/questions/9231821/dalviks-effect-on-native-c-code-performance|Es perd eficiència pel fet que Java no sigui codi nadiu?]] * Llegeix [[Java]] per detalls d'instal·lació i //setup// del JDK. * Llegeix [[https://github.com/dogriffiths/HeadFirstAndroid/wiki/How-Android-Apps-are-Built-and-Run|aquest article sobre com es compilen i executen les aplicacions per a Android]] * [[Cordova]] (abans PhoneGap) ens permet treballar amb **HTML, CSS i JavaScript** per construir les aplicacions de forma més "amigable" que amb Java. Cordova funciona sobre el Android Runtime de Java, pel que és necessari comptar amb aquestes eines també. * Altres: Es pot treballar també amb C++ (codi nadiu amb el què es realitza el nucli i //drivers//), C# i el més recent, Kotlin, molt similar a Java però també més amigable. Eines de desenvolupament per a Android: * **[[https://developer.android.com/studio/|Android Studio]]** que no deixa de ser el mateix IntelliJ Idea amb les llibreries per a Android incorporades. Actualment és la eina oficial recomanada per Google. * **[[https://www.jetbrains.com/idea/|IntelliJ Idea]]** és una plataforma de desenvolupament de la companyia JetBrains. * **Eclipse** : el popular IDE de codi obert havia estat prèviament la plataforma oficial recomanada per Google, fins que van decidir canviar a Android Studio. * **Visual Studio** permet treballar amb C++ i C# * **Xamarin** permet treballar en C# i compilar de forma multiplataforma per a Android i iOS Android Studio i les àrees de treball: {{:android-studio-1.png?direct|}} ==== 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 [[https://gist.github.com/emieza/f9080b4826ea9ad9eb29fbab2c3d433a|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 [[https://ioc.xtec.cat/materials/FP/Materials/2252_DAM/DAM_2252_M08/web/html/WebContent/u1/a2/continguts.html|apunts de desenvolupament per a Android de la IOC]]. En particular, para atenció als conceptes següents: * Activity, Fragment, Service * Callback * Layout * Arxiu //resources// a.k.a. "R" * Cicle de vida d'una activitat * Intent === Cicle de vida d'una Android Activity === {{ :cicle_de_vida_activitat_android.png?direct&500 |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: - El software d'Android Studio - 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. - 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 ===== - Mira els apartats de més amunt i l'article [[Java]] per disposar de la instal·lació del IDE i llibreries adequades. - 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. - 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 - Afegeix un **botó** ([[https://developer.android.com/reference/android/widget/Button|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 ;). - 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 [[https://developer.android.com/reference/android/widget/Button|Button]] trobaràs un exemple de com fer-ho. - Afegeix un **Toast**. El primer que podem fer per comprovar que l'event //OnClick// s'està processant és construir un [[https://developer.android.com/guide/topics/ui/notifiers/toasts#java|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" ==== - La app ens oferirà un casella d'entrada de text i un botó. - La app crearà un número aleatori entre 1 i 100 que el jugador haurà d'endevinar. - Cada cop que l'usuari fa una temptativa es compta un nou intent. - Quan l'usuari endevina del número que la app ha "pensat", s'acaba la partida. - 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. - 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. - La app torna a "pensar" un nou número aleatori i comença nova partida. ==== Aplicació principal (joc) ==== - Crea una nova aplicació amb una sola ''Activity'' (Empty Views Activity). - 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. - Crea un botó i un listener perquè l'usuari validi l'entrada del número. Prova que funciona amb un Toast, per exemple. - 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. - 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. - Afegeix un [[https://developer.android.com/guide/topics/ui/dialogs?hl=es-419|AlertDialog]] per avisar l'usuari de quan acaba la partida i felicitar-lo. - Quan s'acaba la partida, es regenera el número aleatori i es torna a jugar. - Posa un ''TextView'' per anar indicant a l'usuari l'historial dels intents i resultats que ha obtingut. - Per aconseguir un //scroll// en la pantalla de l'historial podem posar un ''ScrollView'' i a dins seu posar el ''TextView''. * [[https://stackoverflow.com/questions/1748977/making-textview-scrollable-on-android|Exemple d'us de ScrollView]]. Funciona, però és un post antic, i els objectes han canviat, pel què el desplaçament fins el final està una mica confús. * Com [[https://stackoverflow.com/questions/3080402/android-scrollview-force-to-bottom|desplaçar el ScrollView fins el final]]. Aquest post sí que està actualitzat. - 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. - 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). - Es pot millorar la jugabilitat si implementem que **el joc detecti la tecla Enter** del teclat de pantalla. * S'implementa millor amb un [[https://stackoverflow.com/questions/25595414/stop-keyboard-from-closing-when-enter-is-pressed-in-edittext|OnEditorActionListener]] ja que el ''OnClickListener'' amaga el teclat al prèmer ENTER. * Us deixo igualment la doc de [[https://developer.android.com/reference/android/view/View.OnKeyListener|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 [[https://developer.android.com/studio/debug/am-logcat?hl=es|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: - Versió més simple amb una ''TextView'' - Versió millorada amb un ''TableLayout'' - Versió //pro// amb ''RecyclerView'' Abans, però, caldrà crear una nova ''Activity'' per poder posar-hi el rànking: - 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'' - Afegim [[https://developer.android.com/guide/components/intents-filters?hl=es-419|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. [[https://developer.android.com/training/basics/firstapp/starting-activity?hl=es-419|Aquest exemple per obrir una nova activity t'ajudarà]]. - 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''. - Diàlegs o [[https://developer.android.com/guide/topics/ui/dialogs?hl=es-419|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: - [[http://www.androidsnippets.com/prompt-user-input-with-an-alertdialog.html|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. - 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 [[https://developer.android.com/guide/topics/ui/dialogs?hl=es-419#CustomLayout|secció de la documentació dels diàlegs personalitzats]]. * Caldrà que creis un //layout// xml per definir el camp de text i els botons del vostre quadre de diàleg. * [[https://stackoverflow.com/questions/6626006/android-custom-dialog-cant-get-text-from-edittext|Aquesta pregunta de stackoverflow pot ajudar-te]] a crear un diàleg personalitzat per preguntar el nom. ==== Model de dades ==== Abans de mostrar la taula de rècords, cal que tinguem clar quin serà el nostre model de dades segons el [[https://es.wikipedia.org/wiki/Modelo–vista–controlador|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. * **Array estàtic** : completament desaconsellat ja que no sabem el nombre de rècords que emmagatzemarem, i els anirem incrementant a cada partida. * **Map (diccionari)** : són molt eficients, però en el nostre cas en ofereix diversos problemes. Un, que un diccionari no està ordenat, i nosaltres haurem de mostrar una llista ordenada (rànking). L'altre, que al guardar amb la clau del nom de l'usuari, quan jugui una 2a partida, esborrarà el valor anterior, i això no ens interessa. * **ArrayList** : la millor opció ja que es pot ordenar per diferents criteris (intents, temps, nom) i és dinàmic. 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 records = new ArrayList(); ==== 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 [[https://developer.android.com/guide/topics/ui/layout/grid?hl=es|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 [[https://stackoverflow.com/questions/11342975/android-create-textviews-in-tablerows-programmatically|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. {{recycling-views.png?direct&450}} 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: - [[https://developer.android.com/guide/topics/ui/layout/recyclerview|Documentació oficial Android de RecyclerView]], l'exemple és prou clar. - Per si de cas, us deixo aquest altre [[https://stackoverflow.com/questions/40584424/simple-android-recyclerview-example|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''). - També teniu aquest [[https://www.youtube.com/watch?v=fnavhYGqZD4&t=673s|vídeo per aprendre a utilitzar la RecyclerView]] \\ ===== 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. {{ :instanceStates.png?500 |Crida a funcions de save/restore quan l'app canvia d'estat.}} Android ens dona aquesta eina [[https://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)|onSaveInstanceState]] que ens facilita guardar les dades que ens calgui de la nostra app en un [[https://developer.android.com/reference/android/os/Bundle|Bundle]] (una senzilla BD local persistent tipus //key-value//). [[https://stackoverflow.com/questions/151777/how-to-save-an-android-activity-state-using-save-instance-state|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 [[https://developer.android.com/topic/libraries/architecture/viewmodel#java|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 [[https://developer.android.com/guide/components/fragments|classes Fragment i FragmentActivity]]. Un ''Fragment'' representa una porció de la interfície que gestiona una ''FragmentActivity''. {{fragments.png}} Aquest exemple bàsic de la documentació oficial es pot [[https://developer.android.com/guide/components/fragments#Example|veure el codi en aquest Exemple de Fragment]]. Conté: - Una ''DetailsActivity'', imprescindible una ''Activity'' contenidora i que disposa del //handlers// adequats, particularment el ''FragmentManager''. - Un ''ListFragment'' (esquerra) per tenir la llista de pestanyes o sub-views i seleccionar-los. - 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//: {{:activity_fragment_lifecycle.png|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 [[https://stackoverflow.com/questions/43167057/libgdx-android-icon-not-being-applied|llegir aquest post sobre com canviar el launcher icon]]. Podeu fer servir aquesta fantàstica eina: https://icon.kitchen \\ ==== Depuració Wireless ==== Seguiu aquest [[https://developer.android.com/tools/adb#wireless|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 [[https://es.wikipedia.org/wiki/Java_Remote_Method_Invocation|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 [[http://lipermi.sourceforge.net/|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 [[https://stackoverflow.com/questions/13788738/using-java-rmi-in-android-application|exemple de com utilitzar-la en aquest thread de (com no) Stackoverflow]]. === Importar LipeRMI a Android Studio === Per importar LipeRMI a Android Studio, [[http://lipermi.sourceforge.net/download.php|descarregueu el jar de LipeRMI]] i feu les següents passes: - Canviar la //folder structure// a ''Project'' - Anar a la carpeta ''app/libs'', i si no hi és, crear-la. - Arrossegar-hi l'arxiu ''lipermi-X.Y.jar'' - Clicar amb el botó dret sobre el nou arxiu ''lipermi-X.Y.jar'' i clicar **Add as library**. - Ja pots fer servir lipeRMI al teu projecte Android. === Implementar codi de servidor i client === Seguiu l'[[https://stackoverflow.com/questions/13788738/using-java-rmi-in-android-application/27815977#27815977|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: * Per accedir a la xarxa haureu d'[[https://developer.android.com/training/basics/network-ops/connecting?hl=es-419|activar els permisos Android d'accés a internet]]. * Per tal que el codi no depengui de la IP del servidor, podem fer servir 10.0.2.2 com a //loopback// de la pròpia màquina //host//. Llegiu [[https://developer.android.com/studio/run/emulator-networking|sobre la xarxa de l'emulador d'Android]]. * Els objectes als dos costats (client i servidor) han de ser idèntics i estar dins el mateix ''package'' de Java. * Pos canviar la signatura de la ''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; } } \\ ==== 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. * [[https://github.com/google/flexbox-layout|Documentació oficial de FlexboxLayout]]. * [[https://css-tricks.com/snippets/css/a-guide-to-flexbox/|Un bon tutorial de CSS Flexbox]]. \\