Programació avançada

Quan hàgiu creat l’esquema d’una senzilla aplicació, és el moment d’afegir funcionalitats avançades, com una interfície d’usuari avançada, l’accés a les dades de l’aplicació (mitjançant bases de dades o proveïdors de continguts) o la persistència de les dades de la mateixa. Així mateix, quan penseu que teniu l’aplicació suficientment desenvolupada, és el moment de publicar l’aplicació en qualsevol de les diferents formes que teniu per fer-ho.

Imports a l'Android Studio

Per facilitar la programació amb l’Android Studio modificarem les preferències per automatitzar els imports a mesura que anem escrivint o que enganxem codi, així obtindrem una major agilitat a l’hora d’escriure codi.

Accedirem a les preferències des de File/Settings i al submenú Editor/Auto Import haurem de marcar els checkboxes Optimize imports on the fly i Add unambiguous imports on the fly. A més, podem modificar el valor d’Insert imports on paste d’Ask a All (vegeu la figura).

Figura

Amb aquestes modificacions l’Android Studio farà tots els imports automàticament, és per això que sempre haurem de comprovar que l’import sigui el correcte per evitar comportaments no desitjats.

Persistència

És probable que vulgueu que la vostra aplicació pugui desar algunes dades entre les diferents execucions de l’aplicació. Per exemple, podeu voler desar les preferències de l’aplicació per tal que la propera vegada que l’executeu tingui la mateixa aparença que la darrera vegada que es va executar. Existeixen diferents formes d’obtenir persistència en les dades de l’aplicació en diferents execucions:

  • Un mecanisme lleuger anomenat preferències compartides (shared preferences) per desar petites quantitats de dades.
  • El sistema de fitxers tradicional.
  • Una base de dades relacional SQLite.

Per desar poca informació, la millor forma de fer-ho és amb les preferències compartides. Android incorpora l’objecte SharedPreferences, que serveix per desar i llegir dades persistents en la forma clau-valor de dades primitives. Podeu fer servir SharedPreferences per desar qualsevol tipus de dades primitives: boolean, float, int, long i string. Aquestes dades es mantindran entre sessions, encara que la vostra aplicació s’hagi tancat. A més, seran desades automàticament en un fitxer XML.

Per obtenir un objecteSharedPreferences a la vostra aplicació podeu fer servir el mètode getSharedPreferences() amb dos arguments: el nom del fitxer de preferències i el mode d’operació. Per exemple:

  1. private SharedPreferences prefs;
  2. //Obtenir l'objecte SharedPreferences
  3. prefs = getSharedPreferences("FitxerPreferences", MODE_PRIVATE);

En el cas que vulgueu fer servir un únic fitxer de preferències, podeu cridar el mètode getPreferences(), on no cal especificar el nom del fitxer.

Per escriure valors al fitxer de preferències:

  1. Crideu el mètode edit() per obtenir un objecte SharedPreferences.Editor.
  2. Afegiu valors amb els mètodes que us permeten escriure valors primitius, com putBoolean() o putString(). Aquests mètodes tenen dos arguments. El primer és un string que defineix la clau, i el segon és el valor que es vol desar.
  3. Confirmeu els valors amb commit().

Per exemple:

  1. SharedPreferences.Editor editor = prefs.edit();
  2.  
  3. editor.putInt("Edat", 23);
  4. editor.putString("NomUsuari", "Fidel");

Per llegir els valors de preferències podeu fer servir els mètodes anàlegs getBoolean(), getString() o getInt() de la classe SharedPreferences. Aquests mètodes tenen dos arguments: el primer és el nom de la clau que esteu cercant al fitxer de preferències. El segon és el valor per defecte que es farà servir en cas que no es trobi la clau en el fitxer de preferències. Per exemple:

  1. //Carregarlespreferències
  2. SharedPreferences prefs = getSharedPreferences("FitxerConfiguracio", MODE_PRIVATE);
  3.  
  4. int Edat = prefs.getInt("Edat", 18);
  5. String nom = prefs.getString("NomUsari", "Usuari");

El lloc més adequat per carregar i desar les preferències de l’aplicació serien els mètodes onCreate() i onStop() (el moment en què l’aplicació s’engega i el moment en què es tanca).

Treballant amb bases de dades

Android proporciona un sistema de bases de dades relacionals basat en SQLite que podeu fer servir a les vostres aplicacions. Quan la quantitat de dades a desar és important, o bé es volen fer cerques sobre les dades, o la informació està relacionada entre si, és més adequat tenir-la estructurada en forma d’una base de dades. Per exemple, podríeu tenir una base de dades amb informació de diferents fabricants relacionada amb els diferents productes que aquests produeixen. Fent servir bases de dades, podeu assegurar la integritat de les dades especificant relacions entre els diferents conjunts de dades.

Android fa servir el sistema de bases de dades SQLite. La base de dades que podeu crear per a la vostra aplicació únicament estarà disponible per a la pròpia aplicació. La resta d’aplicacions del dispositiu no hi podran accedir, per tant no podeu fer servir la base de dades per compartir dades entre aplicacions.

Diem que una aplicació té un accés transparent a les dades quan podem accedir a la informació a través de mètodes sense haver de conèixer la seva implementació. Aquesta manera de treballar ens permet modificar l’estructura interna de la informació sense que les aplicacions que la fan servir hagin de modificar el seu codi per funcionar correctament.

Treballar amb bases de dades a Android pot ser complicat. Per aquest motiu, heu de crear una classe que servirà per encapsular l’accés a les bases de dades i, així, simplificar el codi de la vostra aplicació (que tindrà un accés a les dades transparent a la seva implementació, a través d’aquesta classe).

Classe DBInterface

Podeu descarregar el codi corresponent a aquesta activitat en l’annex anomenat “Bases de dades” de la secció “Annexos”.

Creeu un nou projecte amb les següents dades:

  • Application name: BasesDeDades
  • Company domain: cat.xtec.ioc
  • Blank Activity: BasesDeDadesActivity

Deixeu la resta d’opcions amb els valors per defecte.

Ara creareu una classe que us servirà d’interfície amb l’accés a les bases de dades de l’aplicació. Aquesta classe tindrà el nom DBInterface. Creeu una aplicació, i dins d’aquesta, creeu una nova classe fent clic amb el botó dret dins del paquet del vostre projecte i seleccionant New/Class, com es pot veure a la figura.

Figura Creant una nova classe

Aquesta classe crearà, obrirà, farà servir i tancarà una base de dades SQLite. Creareu una base de dades anomenada BDClients que contindrà una única taula, contactes. Aquesta taula tindrà únicament tres camps: _id, nom i email, tal com es pot veure a la taula.

Taula: Taula de contactes
_id nom email
1 John jcoltrane@atlantic.com
2 Miles mdavis@bluenote.com

En primer lloc, cal que definiu una sèrie de constants de text que serviran per establir alguns identificadors i camps (i així no caldrà haver-los de repetir per tot el codi, amb el perill d’equivocar-se en algun moment).

  1. package ioc.xtec.cat.basesdedades;
  2.  
  3. import android.content.ContentValues;
  4. import android.content.Context;
  5. import android.database.Cursor;
  6. import android.database.SQLException;
  7. import android.database.sqlite.SQLiteDatabase;
  8. import android.database.sqlite.SQLiteOpenHelper;
  9. import android.util.Log;
  10.  
  11.  
  12. public class DBInterface {
  13. //Constants
  14. public static final String CLAU_ID = "_id";
  15. public static final String CLAU_NOM = "nom";
  16. public static final String CLAU_EMAIL = "email";
  17.  
  18. public static final String TAG = "DBInterface";
  19.  
  20. public static final String BD_NOM = "BDClients";
  21. public static final String BD_TAULA = "contactes";
  22. public static final int VERSIO = 1;
  23.  
  24. public static final String BD_CREATE =
  25. "create table " + BD_TAULA + "( " + CLAU_ID + " integer primary key autoincrement, " +
  26. CLAU_NOM + " text not null, " + CLAU_EMAIL + " text not null);";
  27.  
  28. private final Context context;
  29. private AjudaBD ajuda;
  30. private SQLiteDatabase bd;
  31. }

</newcontent>

Concretament, la constant BD_CREATE conté la cadena que es farà servir per crear la taula contactes dins de la base de dades.

La classe AjudaBD, la creareu més tard. El constructor de la nostra classe, l’únic que fa és crear un objecte AjudaBD i guardar en una variable el context en què s’està executant la classe:

  1. public DBInterface(Context con)
  2. {
  3. this.context = con;
  4. ajuda = new AjudaBD(context);
  5. }

Context és una classe implementada pel sistema Android que dóna accés a recursos i classes específics de l’aplicació.

A Android existeix la classe SQLiteOpenHelper, que és una classe que serveix d’ajuda per gestionar la creació de bases de dades i gestió de versions. Creareu la classe AjudaBD que hereta d’aquesta:

  1. private static class AjudaBD extends SQLiteOpenHelper {
  2. AjudaBD(Context con) {
  3. super(con, BD_NOM, null, VERSIO);
  4. }
  5.  
  6. @Override
  7. public void onCreate(SQLiteDatabase db) {
  8. try {
  9. db.execSQL(BD_CREATE);
  10. } catch (SQLException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14.  
  15. @Override
  16. public void onUpgrade(SQLiteDatabase db, int VersioAntiga, int VersioNova) {
  17. Log.w(TAG, "Actualitzant Base de dades de la versió" + VersioAntiga + " a " + VersioNova + ". Destruirà totes les dades");
  18. db.execSQL("DROP TABLE IF EXISTS " + BD_TAULA);
  19.  
  20. onCreate(db);
  21. }
  22. }

El constructor d'AjudaDB crida el constructor d'SQLiteOpenHelper, el qual crea un objecte d’ajuda per crear, obrir i gestionar la base de dades. Mireu la documentació d'SQLiteOpenHelper per obtenir ajuda sobre els seus mètodes. Aquesta classe s’ocupa d’obrir la base de dades si aquesta existeix o crear-la en cas contrari, i actualitzar-la si és necessari.

El mètode onCreate() crea una nova base de dades. El mètode onUpgrade() és cridat quan s’ha d’actualitzar la base de dades. El que fa és eliminar-la (fer un drop de la taula) i tornar-la a crear.

De tornada a la classe DBInterface, el següent pas és definir els diferents mètodes per obrir i tancar la base de dades.

  1. //Obre la BD
  2.  
  3. public DBInterface obre() throws SQLException {
  4. bd = ajuda.getWritableDatabase();
  5. return this;
  6. }
  7.  
  8. //Tanca la BD
  9.  
  10. public void tanca() {
  11. ajuda.close();
  12. }

El mètode getWritableDatabase() crea i/o obre una base de dades. La primera vegada que es crida s’obre la base de dades i es crida onCreate(). Aquí és on es crea la base de dades, cridant execSQL() amb la cadena de creació de la base de dades. Una vegada creada, la base de dades queda en caché, i per tant es pot cridar aquest mètode per obrir-la.

A continuació definireu els mètodes per modificar la base de dades. Per inserir un contacte fareu servir el mètode insert(String table, String nullColumnHack, ContentValues values). Els arguments són aquests:

  • String table: taula on es vol inserir un element.
  • String nullColumnHack: un argument opcional que deixarem a null. Podeu consultar la Guia del desenvolupador d’Android per consultar el seu ús.
  • ContentValues values: un objecte de la classe ContentValues que serveix per emmagatzemar valors que poden ser processats per un ContentResolver.

Aquest mètode retorna l’ID de la fila que s’ha inserit o un -1 si hi ha hagut una errada. El nostre mètode per inserir un contacte crearà un ContentValues amb els valors de la fila a inserir i farà la crida a insert().

  1. //Insereix un contacte
  2.  
  3. public long insereixContacte(String nom, String email
  4. {
  5. ContentValues initialValues = new ContentValues();
  6. initialValues.put(CLAU_NOM, nom);
  7. initialValues.put(CLAU_EMAIL, email);
  8. return bd.insert(BD_TAULA ,null, initialValues);
  9.  
  10. }

Per esborrar un element de la taula fareu servir el mètode delete (String table, String whereClause, String[] whereArgs). El significat dels arguments és el següent:

  • String table: taula de la qual s’esborrarà el registre.
  • String whereClause: la clàusula WHERE que s’aplicarà per esborrar de la base de dades. Si se li passa null, esborrarà totes les files de la taula.
  • String[] whereArgs: arguments de la clàusula WHERE. Aquest mètode retorna la quantitat de files afectades per la clàusula WHERE.
  1. //Esborra un contacte
  2.  
  3. public boolean esborraContacte(long IDFila)
  4. {
  5. return bd.delete(BD_TAULA, CLAU_ID + " = " + IDFila, null) > 0;
  6. }

El vostre mètode retornarà un valor booleà que indica si s’ha esborrat algun element o no.

Per retornar un contacte fareu servir el mètode query(), que té els següents arguments:

  • boolean distinct: serà true si voleu que cada fila sigui única, o false en cas contrari.
  • String table: defineix la taula respecte a la qual voleu executar la sentència de query.
  • String[] columns: admet una llista de les columnes de la taula que retornarà el mètode.
  • String selection: estableix un filtre que defineix quines files retornar, amb el format de clàusula d’SQL, WHERE (sense incloure la paraula WHERE a l’string).
  • String[] selectionArgs: permet afegir els arguments de la selecció, si no els heu introduït directament a la cadena.
  • String groupBy: estableix un filtre que defineix com s’agrupen les files. Té el mateix format que la clàusula d’SQL: GROUP BY (sense incloure les paraules GROUP BY). Si se li passa un null, les files que es retornin no estaran agrupades.
  • String having: estableix un filtre que declara quins grups de files incloure al cursor. Té el mateix format que la clàusula d’SQL: HAVING (sense incloure la paraula HAVING). Si se li passa un null, s’inclouran tots els grups.
  • String orderBy: indica com ordenar les files. Té el mateix format que la clàusula d’SQL: ORDER BY (sense incloure les paraules ORDER BY). Si se li passa un null retorna les files amb l’ordre per defecte.
  • String limit: especifica el límit de files retornades pel query, amb el format de la clàusula d’SQL: LIMIT. Si se li passa un null, no existeix límit.

Aquest mètode retorna un objecte de la classe Cursor, que proporciona accés de lectura i escriptura al resultat retornat per una consulta (un query) a la base de dades.

  1. //Retorna un contacte
  2.  
  3. public Cursor obtenirContacte(long IDFila) throws SQLException {
  4. Cursor mCursor = bd.query(true, BD_TAULA, new String[] {CLAU_ID, CLAU_NOM, CLAU_EMAIL}, CLAU_ID + " = " + IDFila, null, null, null, null, null);
  5.  
  6. if(mCursor != null) {
  7. mCursor.moveToFirst();
  8. }
  9.  
  10. return mCursor;
  11.  
  12. }

Per obtenir tots els contactes, feu servir una altra versió de query que no inclogui el primer booleà.

  1. //Retorna tots els contactes
  2.  
  3. public Cursor obtenirTotsElsContactes()
  4. {
  5. return bd.query(BD_TAULA, new String[] {CLAU_ID, CLAU_NOM, CLAU_EMAIL}, null, null, null, null, null);
  6. }

Fixeu-vos que Android fa servir un objecte de la classe Cursor per valor de retorn de les consultes a la base de dades (els queries). Penseu en el Cursor com un apuntador al conjunt de resultats obtinguts de la consulta a la base de dades. L’ús d’un Cursor permet a Android gestionar d’una forma més eficient les files i les columnes.

Finalment, per actualitzar un registre de la taula fareu servir el mètodeupdate() amb els següents arguments:

  • String table: estableix la taula a actualitzar.
  • ContentValues values: introdueix un objecte de la classe ContentValues amb el valor de les columnes a actualitzar.
  • String whereClause: inclou la clàusula WHERE opcional que s’ha d’aplicar quan es fa l’actualització. Si se li passa null actualitzarà totes les files.
  • String[] whereArgs: estableix els arguments del WHERE.

El codi resultant és el següent:

  1. //Modifica un contacte
  2.  
  3. public boolean actualitzarContacte(long IDFila, String nom, String email) {
  4. ContentValues args = new ContentValues();
  5. args.put(CLAU_NOM, nom);
  6. args.put(CLAU_EMAIL, email);
  7. returnbd.update(BD_TAULA, args, CLAU_ID + " = " + IDFila, null) > 0;
  8. }

Fent servir la classe ''DBInterface''

Alguns dels layouts s’han omès, els podreu trobar al codi font del projecte.

Quan hàgiu creat la classe que us servirà d’ajuda per treballar amb les bases de dades, és el moment de fer-la servir. El següent exemple mostra una aplicació que fa servir la classe d’interfície de base de dades. És molt senzilla, però serveix per il·lustrar la forma d’ús d’aquesta classe.

L’activitat principal té un layout amb cinc botons, un per a cada opció de l’aplicació, tal com es pot veure a la figura.

Figura Layout de l’aplicació

El codi que correspon a aquest layout és el següent:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:orientation="vertical"
  7. android:paddingBottom="@dimen/activity_vertical_margin"
  8. android:paddingLeft="@dimen/activity_horizontal_margin"
  9. android:paddingRight="@dimen/activity_horizontal_margin"
  10. android:paddingTop="@dimen/activity_vertical_margin"
  11. tools:context=".MainActivity">
  12.  
  13. <Button
  14. android:id="@+id/btnAfegir"
  15. android:layout_width="fill_parent"
  16. android:layout_height="wrap_content"
  17. android:text="Afegir" />
  18.  
  19. <Button
  20. android:id="@+id/btnObtenir"
  21. android:layout_width="fill_parent"
  22. android:layout_height="wrap_content"
  23. android:text="Obtenir" />
  24.  
  25. <Button
  26. android:id="@+id/btnObtenirTots"
  27. android:layout_width="fill_parent"
  28. android:layout_height="wrap_content"
  29. android:text="Obtenir Tots" />
  30.  
  31. <Button
  32. android:id="@+id/btnActualitzar"
  33. android:layout_width="fill_parent"
  34. android:layout_height="wrap_content"
  35. android:text="Actualitzar" />
  36.  
  37. <Button
  38. android:id="@+id/btnEsborrar"
  39. android:layout_width="fill_parent"
  40. android:layout_height="wrap_content"
  41. android:text="Esborrar" />
  42.  
  43. </LinearLayout>

L’aplicació activarà una activitat per a cadascun dels botons, equivalent a les diferents “pantalles” de l’aplicació. Aquestes diferents activitats han d’estar definides a l’AndroidManifest.xml. Recordeu que heu de definir al fitxer de manifest els filtres d’intents per a cada activitat, per tal que les diferents activitats s’activin quan es llancin els intents adequats per a cada una d’elles. Aquest és el document de manifest de l’aplicació:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="ioc.xtec.cat.basesdedades" >
  4.  
  5. <application
  6. android:allowBackup="true"
  7. android:icon="@drawable/ic_launcher"
  8. android:label="@string/app_name"
  9. android:theme="@style/AppTheme" >
  10. <activity
  11. android:name=".BaseDeDadesActivity"
  12. android:label="@string/app_name" >
  13. <intent-filter>
  14. <action android:name="android.intent.action.MAIN" />
  15.  
  16. <category android:name="android.intent.category.LAUNCHER" />
  17. </intent-filter>
  18. </activity>
  19. <activity
  20. android:name=".Afegir"
  21. android:label="@string/title_activity_afegir" >
  22. <intent-filter>
  23. <action android:name="cat.xtec.ioc.AFEGIR" />
  24.  
  25. <category android:name="android.intent.category.DEFAULT" />
  26. </intent-filter>
  27. </activity>
  28. <activity
  29. android:name=".Obtenir"
  30. android:label="@string/title_activity_obtenir" >
  31. <intent-filter>
  32. <action android:name="cat.xtec.ioc.OBTENIR" />
  33.  
  34. <category android:name="android.intent.category.DEFAULT" />
  35. </intent-filter>
  36. </activity>
  37. <activity
  38. android:name=".Esborrar"
  39. android:label="@string/title_activity_esborrar" >
  40. <intent-filter>
  41. <action android:name="cat.xtec.ioc.ESBORRAR" />
  42.  
  43. <category android:name="android.intent.category.DEFAULT" />
  44. </intent-filter>
  45. </activity>
  46. <activity
  47. android:name=".Actualitzar"
  48. android:label="@string/title_activity_actualitzar" >
  49. <intent-filter>
  50. <action android:name="cat.xtec.ioc.ACTUALITZAR" />
  51.  
  52. <category android:name="android.intent.category.DEFAULT" />
  53. </intent-filter>
  54. </activity>
  55. </application>
  56.  
  57. </manifest>

L’activitat principal implementa la interfície OnClickListener per gestionar les accions que es deriven quan l’usuari prem els botons. Tot seguit teniu el codi parcial que correspon a la definició de la classe i de les variables que farem servir a l’activitat:

  1. public class BasesDeDadesActivity extends Activity implements OnClickListener {
  2.  
  3. Button btnAfegir, btnObtenir, btnObtenirTots, btnActualitzar, btnEsborrar;
  4. DBInterface bd;

El mètode onCreate() senzillament carrega el layout, crea l’objecte d’interfície de la base de dades i crea els listeners dels botons.

  1. public void onCreate(Bundle savedInstanceState) {
  2. super.onCreate(savedInstanceState);
  3. setContentView(R.layout.main);
  4.  
  5. bd = new DBInterface(this);
  6.  
  7. //Listeners dels botons
  8. btnAfegir = (Button) findViewById(R.id.btnAfegir);
  9. btnAfegir.setOnClickListener(this);
  10.  
  11. btnObtenirTots = (Button) findViewById(R.id.btnObtenirTots);
  12. btnObtenirTots.setOnClickListener(this);
  13.  
  14. btnObtenir = (Button) findViewById(R.id.btnObtenir);
  15. btnObtenir.setOnClickListener(this);
  16.  
  17. btnEsborrar = (Button) findViewById(R.id.btnEsborrar);
  18. btnEsborrar.setOnClickListener(this);
  19.  
  20. btnActualitzar = (Button) findViewById(R.id.btnActualitzar);
  21. btnActualitzar.setOnClickListener(this);
  22.  
  23. }

A continuació implementarem cadascuna de les accions que corresponen als botons:

  • Afegir
  • Obtenir
  • Obtenir tots
  • Actualitzar
  • Esborrar

Afegir

En primer lloc, cal definir el listener del botó:

  1. //Afegir
  2. if (v == btnAfegir) {
  3. startActivity(new Intent("cat.xtec.ioc.AFEGIR"));
  4. }

Això llançarà un intent per a l’activitat que permetrà afegir elements a la base de dades. Aquesta activitat té un disseny molt senzill, incorpora tan sòls els widgets necessaris per afegir un nou element, com es pot veure a la figura.

Figura Activitat d’afegir contactes

Aquest és el listener del botó Afegir:

  1. @Override
  2. public void onClick(View v) {
  3. if(v == btnAfegir){
  4. //Obrim la base de dades
  5. bd = new DBInterface(this);
  6. bd.obre();
  7.  
  8. //Inserim el contacte
  9. if(bd.insereixContacte(editNom.getText().toString(), editEmail.getText().toString()) != -1) {
  10. Toast.makeText(this, "Afegit correctament", Toast.LENGTH_SHORT).show();
  11. } else {
  12. Toast.makeText(this, "Error a l'afegir", Toast.LENGTH_SHORT).show();
  13. }
  14. bd.tanca();
  15. finish();
  16. }
  17. }

Bàsicament, el que es fa és:

  1. Obrir la base de dades.
  2. Inserir un element amb les dades que obteniu de les caixes de text. Fixeu-vos com comprova el valor de retorn d'insereixContacte() per saber si s’ha pogut inserir el contacte nou correctament o no.
  3. Tancar la base de dades.
  4. Tancar l’activitat.

Obtenir

Tot seguim definirem un nou listener, aquest cop pel botó d’Obtenir.

  1. btnObtenir = (Button) findViewById(R.id.btnObtenir);
  2. btnObtenir.setOnClickListener(this);
  3.  
  4. ...
  5.  
  6. else if (v == btnObtenir) {
  7. startActivity(new Intent("cat.xtec.ioc.OBTENIR"));
  8. }

Això us permetrà llançar una activitat en la que els únics widgets seran una caixa de text i el botó per obtenir el contacte, com es pot veure a la figura.

Figura Activitat per obtenir un contacte

Al codi de l’activitat l’única cosa que es programa és el listener del botó Obtenir. El codi és el següent:

  1. @Override
  2. public void onClick(View v) {
  3. //Botó obtenir
  4. if (v == btnObtenir) {
  5. Cursor c;
  6.  
  7. //Obrim la base de dades
  8. bd = new DBInterface(this);
  9. bd.obre();
  10.  
  11. //Aquest és l'identificador que està escrit a la caixa de text
  12. long id = Long.parseLong(editID.getText().toString());
  13.  
  14. //Crida a la BD
  15. c = bd.obtenirContacte(id);
  16.  
  17. // Comprovem si hi ha hagut algun resultat
  18. if (c.getCount() != 0) {
  19. //Mostrem el contacte
  20. Toast.makeText(this, "id: " + c.getString(0) + "\n" + "Nom: " + c.getString(1) + "\n Email: " + c.getString(2), Toast.LENGTH_SHORT).show();
  21. } else {
  22. Toast.makeText(this, "id inexistent!", Toast.LENGTH_SHORT).show();
  23.  
  24. }
  25. //Tanquem la BD
  26. bd.tanca();
  27.  
  28. //Tanca l'activitat
  29. finish();
  30. }
  31. }

El que hem fet és:

  1. Obrir la base de dades
  2. Obtenir l’identificador que està escrit a la caixa de text
  3. Cridar la base de dades
  4. Comprovar si hi ha hagut algun resultat
  5. Mostrar el contacte
  6. Tancar la base de dades
  7. Tancar l’activitat

El més normal seria mostrar el contacte a través d’una altra activitat dissenyada per mostrar contactes, però per fer el programa més senzill hem optat per mostrar el contacte amb un missatge per pantalla.

Obtenir tots

L’opció Obtenir tots mostrarà tots els contactes per pantalla, un a un. En una aplicació de veritat el més habitual seria introduir els contactes en un ListView o un ContentProvider, però en aquest exemple volem mostrar únicament el funcionament de les bases de dades, per tant qualsevol forma de mostrar els contactes ens serà suficient. Atès que el programa no necessita cap altra informació extra per mostrar tots els contactes (i que, per tant, no es demanarà cap informació a l’usuari), aquesta opció no obrirà una altra activitat, sinó que mostrarà els contactes un a un per pantalla. Dins del listener del botó obtenim tots els contactes de la base de dades i fem un recorregut amb el cursor que se’ns retorna per anar mostrant tots els contactes.

  1. else if (v == btnObtenirTots) {
  2. //Obrir base de dades
  3. bd.obre();
  4.  
  5. //Crida la BD per obtenir tots els contactes
  6. Cursor c = bd.obtenirTotsElsContactes();
  7.  
  8. //Movem el cursor a la primera posició
  9. if (c.moveToFirst()) {
  10. do {
  11. //Mostrem contactes...
  12. MostraContacte(c);
  13. //... mentre puguem passar al següent contacte
  14. } while (c.moveToNext());
  15. }
  16. //Tanquem la BD
  17. bd.tanca();
  18.  
  19. Toast.makeText(this, "Tots els contactes mostrats", Toast.LENGTH_SHORT).show();
  20. }

El que hem fet és:

  1. Obrir la base de dades
  2. Cridar la BD per obtenir tots els contactes
  3. Moure el cursor a la primera posició
  4. Mostrar els contactes mentre es pugui
  5. Tancar la base de dades
  6. Mostrar un missatge per pantalla quan s’hagi acabat de mostrar els contactes.

El mètode MostraContacte() simplement fa una crida a Toast per mostrar el contacte per pantalla.

  1. public void MostraContacte(Cursor c)
  2. {
  3. Toast.makeText(this, "id: "+ c.getString(0) + "\n" + "Nom: "+ c.getString(1) + "\n Email: " + c.getString(2), Toast.LENGTH_SHORT).show();
  4. }

Actualitzar

L’opció d’actualitzar permet modificar les dades d’un contacte a partir del seu identificador. Quan hàgiu creat el listener, es mostrarà una activitat com la que es pot veure a la figura.

Figura Activitat actualitzar
  1. @Override
  2. public void onClick(View v) {
  3. if (v == btnActualitzar) {
  4. long id;
  5.  
  6. //Obtenim la BD
  7. bd = new DBInterface(this);
  8. bd.obre();
  9.  
  10. //Identificador de la caixa de text
  11. id = Long.parseLong(editID.getText().toString());
  12.  
  13. //Crida a la BD
  14. boolean result = bd.actualitzarContacte(id, editNom.getText().toString(), editEmail.getText().toString());
  15.  
  16. //Comprovem el resultat, si s'ha pogut actualitzar la BD o no
  17. if (result)
  18. Toast.makeText(this, "Element modificat", Toast.LENGTH_SHORT).show();
  19. else
  20. Toast.makeText(this, "No s'ha pogut modificar l'element", Toast.LENGTH_SHORT).show();
  21.  
  22. //Tanquem la BD
  23. bd.tanca();
  24.  
  25. //Tanca l'activitat.
  26. finish();
  27.  
  28. }
  29.  
  30. }

El que hem fet és:

  1. Obrir la base de dades
  2. Obtenir l’identificador de la caixa de text
  3. Cridar la BD
  4. Comprovar el resultat per determinar si s’ha pogut actualitzar la BD o no
  5. Tancar la base de dades
  6. Tancar l’activitat

El funcionament del codi és molt semblant. Obteniu les dades que necessiteu des dels widgets de l’activitat (les caixes de text) i feu la crida a la base de dades. En aquest cas, a més a més, es comprova el valor de retorn de la funció per saber si s’ha pogut actualitzar la base de dades correctament.

Esborrar

La funció d’esborrar té una estructura molt similar a les altres funcions. Obté d’una caixa de text l’ID del contacte que es vol esborrar i fa una crida a la base de dades per esborrar el contingut corresponent. Es comprova el valor de retorn per saber si el contacte s’ha eliminat correctament.

  1. @Override
  2. public void onClick(View v) {
  3. if (v == btnEsborrar) {
  4. //Obrim la BD
  5. bd = new DBInterface(this);
  6. bd.obre();
  7.  
  8. //Obtenim l'ID de la caixa de text
  9. long id = Long.parseLong(editID.getText().toString());
  10.  
  11. //Cridem la BD
  12. boolean result = bd.esborraContacte(id);
  13.  
  14. //Comprovem el resultat de l'operació
  15. if (result)
  16. Toast.makeText(this, "Element esborrat", Toast.LENGTH_SHORT).show();
  17. else
  18. Toast.makeText(this, "No s'ha pogut esborrar l'element", Toast.LENGTH_SHORT).show();
  19. //Tanquem la BD
  20. bd.tanca();
  21.  
  22. //Tanquem l'activitat
  23. finish();
  24. }
  25. }

El que hem fet és:

  1. Obrir la base de dades
  2. Obtenir l’identificador de la caixa de text
  3. Cridar la BD
  4. Comprovar el resultat de l’operació
  5. Tancar la base de dades
  6. Tancar l’activitat

Proveïdors de continguts

Els content providers (proveïdors de continguts) donen accés a una sèrie estructurada de dades i serveixen d’interfície de dades estàndard per connectar dades d’un procés amb el codi que s’està executant en un altre procés. Els proveïdors de continguts són la forma recomanada de compartir dades entre aplicacions. Són magatzems de dades que serveixen per compartir dades entre aplicacions. Es comporten de forma similar a una base de dades (podeu fer consultes, editar el contingut, afegir i esborrar), però a diferència d’aquestes fan servir diferents formes per emmagatzemar les seves dades. Aquestes poden estar en una base de dades, en fitxers o fins i tot a la xarxa, però per al procés que les fa servir això és transparent (i ens resulta indiferent).

Android fa servir una sèrie de proveïdors de continguts, estàndards del sistema, que poden fer servir la resta d’aplicacions, per exemple:

  • Browser: emmagatzema dades com els marcadors del navegador, l’historial de navegació, etc.
  • CallLog: emmagatzema dades com crides perdudes, detall de les trucades, etc.
  • ContactsContract: emmagatzema informació dels contactes: nom, email, telèfon, foto, etc.
  • MediaStore: emmagatzema dades multimèdia com imatges, àudio i vídeo.
  • Settings: emmagatzema dades de configuració i preferències del dispositiu com el bluetooth, wifi, etc.

Podeu trobar una llista de proveïdors (del paquet android.provider) a la Guia del desenvolupador d’Android, que podeu trobar aquí:

http://developer.android.com/reference/android/provider/package-summary.html.

A banda d’aquests proveïdors de continguts, podeu crear els vostres propis.

Quan vulgueu accedir a les dades d’un proveïdor de continguts haureu de servir un objecte ContentResolver en el context de la vostra aplicació. Aquest objecte treballa com a client amb un objecte proveïdor, que treballa com a servidor: un objecte ContentProvider. El proveïdor rep les dades dels clients, realitza la tasca i retorna els resultats. Heu de crear un ContentProvider si voleu compartir dades de la vostra aplicació amb altres, però per fer servir dades d’altres aplicacions simplement necessiteu un ContentResolver.

Accedint al proveïdor de continguts

El ContentProvider presenta dades a les aplicacions, com una o més taules similars a les que trobem a les bases de dades relacionals. Cada fila representa un element del proveïdor de continguts, i cada columna una dada concreta del mateix element de la fila.

L’aplicació accedeix a les dades del proveïdor a través de l’objecte client ContentResolver. Aquest objecte conté mètodes que criden altres mètodes (per cert, amb el mateix nom) a la classe ContentProvider. Per exemple, per obtenir una llista de contactes podeu cridar el mètode ContentResolver.query(), i aquest quest cridarà el mètode query() de ContentProvider. El mètode query() té una sèrie d’arguments que serveixen per definir la consulta que es vol fer al proveïdor. Aquests arguments tenen un paral·lelisme amb les opcions d’una consulta query a una base de dades:

  • Uri uri: URI que representa la taula d’on s’obtindran les dades.
  • String[] projection: llista de les columnes que es retornaran per cada fila de la taula.
  • String selection: filtre que indica quines files retornar amb el mateix format que la clàusula WHERE d’SQL (sense la paraula “WHERE”). Si es passa un null retornarà totes les files de l’URI.
  • String[] selectionArgs: en l’argument anterior (selection), en lloc d’incloure el valor de les columnes, directament podeu indicar “= ?” en la selecció. Els “?” seran substituïts pels valors de l’array selectionArgs en l’ordre en què apareixen a selection.
  • String sortOrder: estableix com s’ordenen les files, amb el mateix format que la clàusula d’SQL ORDER BY (excloent les paraules “ORDER BY”). Si es passa null es farà servir l’ordre per defecte.

Es poden consultar els arguments i el seu equivalent en un SELECT d’SQL a la taula.

Taula: Equivalència d’arguments entre query() i una consulta SELECT d’SQL
Argument de query() Equivalent en SELECTd’SQL
Uri uri FROM taula
String[] projection columna, columna, columna
String selection WHERE col = valor
String[] selectionArgs No existeix un equivalent
String sortOrder ORDER BY col, col,…

El mètode retorna un objecte de la classe Cursor, posicionat abans de la primera entrada o null en cas de trobar algun problema.

L’URI especifica les dades en el proveïdor. Els URI inclouen el nom del proveïdor (anomenat authority, ‘autoritat’) i un nom que apunta a una taula (path). El ContentProvider fa servir el path de l’URI per escollir la taula a la qual accedirà (té un path per a cada taula). L’estructura d’un URI és aquesta:

<prefix>://<authority>/<path>/<id>

on:

  • El prefix per als proveïdors de continguts sempre és: content://.
  • authority especifica el nom del proveïdor de continguts. Per als estàndards del sistema, per exemple: media, call_log, browser. Per a proveïdors de tercers, millor introduir un nom de domini complet com: com.android.ioc.
  • El path especifica la taula dins del proveïdor.
  • L’id és un identificador únic per a una fila concreta de la taula especificada al path.

A la taula es poden veure alguns proveïdors de continguts del sistema.

Taula: Alguns exemples de proveïdors de continguts del sistema.
String Descripció
content://media/internal/images Llista de les imatges en la memòria del dispositiu
content://media/external/images Llista de les imatges en la memòria externa del dispositiu (per exemple, en la targeta SD)
content://call_log/calls Llista de trucades fetes amb el dispositiu
content://browser/bookmarks Llista dels marcadors del navegador web

D’altra banda, molts proveïdors de continguts tenen una constant amb l’URI d’accés a ells mateixos. Per exemple, en lloc de crear vosaltres l’URI per accedir al diccionari de paraules del dispositiu podeu fer servir el que ell mateix té definit: UserDictionary.Words.CONTENT_URI. O per accedir a la llista de contactes, ContactsContract.Contacts.CONTENT_URI.

Per obtenir dades d’un proveïdor, la vostra aplicació necessita d’un permís de lectura per part del proveïdor. No es pot demanar aquest permís durant l’execució, així que per obtenir-lo caldrà que feu servir l’element <uses-permission> en el vostre fitxer de manifest especificant el permís que voleu obtenir del proveïdor. En quedar definit al manifest, quan l’usuari instal·la aquesta aplicació li està donant els permisos implícitament. Per saber exactament de quins permisos consta el proveïdor així com seu nom, consulteu la documentació del proveïdor. Per exemple, per demanar que la vostra aplicació tingui permisos de lectura sobre el proveïdor de contactes del dispositiu heu d’indicar-ho al fitxer de manifest de la següent manera, abans de l’etiqueta <application>:

  1. <uses-permission android:name="android.permission.READ_CONTACTS">
  2. </uses-permission>

El següent pas és realitzar una consulta al proveïdor per obtenir les seves dades. El següent codi mostra com fer una consulta per obtenir tots els contactes de la llista de contactes:

  1. //Les columnes que volem obtenir per a cada element.
  2. String[] projection = new String[] {
  3. ContactsContract.Contacts._ID,
  4. ContactsContract.Contacts.DISPLAY_NAME,
  5. ContactsContract.Contacts.HAS_PHONE_NUMBER
  6. };
  7.  
  8. //Condició: volem obtenir totes les files (per això és null).
  9. String where = null;
  10. String[] whereArgs = null;
  11.  
  12. //Ordre: que estiguin ordenats de forma ascendent.
  13. String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
  14.  
  15. Cursor c = getContentResolver().query
  16. (
  17. ContactsContract.Contacts.CONTENT_URI,
  18. projection, // Columnes per obtenir de cada fila
  19. where, // Criteri de selecció
  20. whereArgs, // Criteri de selecció
  21. sortOrder // Ordre
  22. );

Aquest codi obtindrà tota la llista de contactes i retornarà un cursor al resultat, la variable c. Quan hàgiu obtingut el cursor, heu de mirar el seu valor per saber si hi ha hagut un error, o si el cursor té dades o no. Ho podeu comprovar de la següent manera:

  1. //Si hi ha hagut un error
  2. if (c == null){
  3. //Codi per tractar l'error, escriure logs, etc.
  4.  
  5. }
  6. //Si el cursor està buit, el proveïdor no ha trobat resultats.
  7. elseif (c.getCount() < 1) {
  8. //El cursor està buit, el content provider no té elements.
  9. Toast.makeText(this, "No hi ha dades", Toast.LENGTH_SHORT).show();
  10. } else {
  11. //Dades obtingudes
  12. Toast.makeText(this, "OK", Toast.LENGTH_SHORT).show();
  13. }

En aquests moments teniu un cursor a les dades obtingudes del proveïdor, així que podríeu recórrer les dades amb el cursor i treballar-hi. Per exemple, el següent codi mostra com obtenir l’identificador i el nom, i com saber si el contacte té número de telèfon:

  1. //Mentre tenim un nou element al cursor
  2. while(c.moveToNext()) {
  3. //Obtenir ID
  4. String contactId = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
  5.  
  6. //Obtenir nom
  7. String nomContacte = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
  8.  
  9. //Saber si té telèfon
  10. String hasPhone = c.getString(c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
  11.  
  12. //Mostrar
  13. Toast.makeText(this, "id: "+ contactId + "\n" + "Nom: "+ nomContacte + "\n Té telèfon: " + hasPhone , Toast.LENGTH_SHORT).show();
  14. }
  15.  
  16. c.close();

Per obtenir una columna a partir del cursor heu de fer servir el mètode Cursor.getString(int ColumnIndex), al qual se li passa l’índex de la columna que voleu obtenir. Per obtenir el número de columna a partir de l’identificador del camp que voleu, feu servir Cursor.getColumnIndex(String columnName). Tot això ho podeu fer a la vegada:

  1. String nomContacte = c.getString(c.getColumnIndex(ContactsContract.Contacts.//DISPLAY_NAME//));

El codi anterior us mostra per pantalla la informació de tots els contactes del proveïdor tal com estan al cursor. Però, què passa si voleu filtrar aquesta informació? Treballant amb SQL ho podeu fer amb una sentència WHERE columna = valor. Això ho podeu definir al mètode query() amb els arguments where i whereArgs. En realitat, podríeu obtenir fàcilment la mateixa sentència WHERE modificant el valor de l’argument where. Si voleu obtenir al cursor únicament els contactes que tinguin telèfon, per exemple, podeu definir l’argument com:

  1. String where = ContactsContract.Contacts.HAS_PHONE_NUMBER + "= 1";

O, per obtenir únicament els contactes amb nom IOC:

  1. String where = ContactsContract.Contacts.DISPLAY_NAME + "= 'IOC'";

Per qüestions de seguretat i per evitar que l’usuari pugui introduir codi SQL maliciós, es pot fer servir el caràcter “?” a la variable where dins de la sentència. Els caràcters “?” seran substituïts amb els valors de l’array d’strings whereArgs. Per exemple, la sentència anterior seria equivalent a:

  1. String where = ContactsContract.Contacts.DISPLAY_NAME + "= ?";
  2. String[] whereArgs = {"IOC"};

SQL Injection

L’objectiu de whereArgs i de la substitució dels “?” és impedir la inclusió de codi SQL maliciós dins de la sentència (vegeu aquest enllaç a la Wikiquipedia). Imagineu que creeu un codi per accedir a tots els contactes que tinguin un nom igual a una variable que introdueix l’usuari (“WHERE name = valor_variable”). I aquest usuari, en lloc d’introduir el nom d’una persona, introdueix: “nothing; DROP TABLE *”. La sentència SQL resultant, en sers executada: “WHERE name = nothing. DROP TABLE *” aconseguiria esborrar totes les taules de la base de dades (si tingués permís per fer-ho).

Fins ara heu mostrat el nom, l’identificador i una variable que diu si el contacte té o no telèfon. Però obtenir el telèfon i el correu electrònic és una mica més complicat. Atès que a la llista un contacte pot tenir diversos números de telèfon i diverses adreces de correu electrònic, aquestes no estan emmagatzemades com variables estàtiques sinó com un proveïdor de continguts dins del proveïdor de continguts dels contactes. Per tant, per obtenir-les s’ha de fer una altra consulta i obtenir un cursor a la llista de telèfons i correus electrònics, respectivament. El següent codi recorre la llista de telèfons i correus i els va desant a una variable de tipus String (es podrien mostrar o fer qualsevol altra cosa amb ells). Al final, la variable es queda amb l’últim telèfon i correu que serà el que es mostri del contacte (encara que es podria mostrar el primer, o tots).

  1. String telefon = null;
  2. String email = null;
  3.  
  4. if (hasPhone.compareTo("1") == 0) {
  5. // Obtenim els telèfons
  6. Cursor telefons = getContentResolver().query(
  7. ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
  8. null,
  9. ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,
  10. null,
  11. null);
  12.  
  13. //Recorrem els telèfons
  14. while (telefons.moveToNext()) {
  15. telefon = phones.getString(phones.getColumnIndex( ContactsContract.CommonDataKinds.Phone.NUMBER));
  16. }
  17.  
  18. //Tanquem el cursor
  19. telefons.close();
  20. }
  21.  
  22. //Obtenir cursor correus
  23. Cursor emails = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId, null, null);
  24.  
  25. //Recorrem els correus
  26. while (emails.moveToNext()) {
  27. email = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
  28. }
  29. //Tanquem el cursor
  30. emails.close();
  31.  
  32. //Mostrar
  33. Toast.makeText(this, "id: "+ contactId + "\n" + "Nom: "+ nomContacte + "\n Telefon: " + telefon + "\n email: " + email, Toast.LENGTH_SHORT).show();

Inserint dades

La forma d’inserir dades a un proveïdor de continguts és similar a la consulta. Per fer-ho, existeix el mètode ContentResolver.insert(). Aquest mètode insereix una nova fila al proveïdor de continguts i retorna l’URI de la fila creada. Els valors de la fila queden definits amb un objecte de la classe ContentValues. Per exemple, el següent codi serveix per inserir una nova paraula al diccionari personal de l’usuari del dispositiu. Perquè funcioni, s’han de donar permisos d’escriptura al diccionari de dades del dispositiu inserint la següent sentència al document de manifest:

  1. <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY">
  2. </uses-permission>

El següent fragment de codi mostra com s’insereix un nou valor al proveïdor de continguts:

  1. Uri UriNou;
  2.  
  3. ContentValues Valors = new ContentValues();
  4.  
  5. //Creem el valor de la nova entrada
  6. Valors.put(UserDictionary.Words.APP_ID, "com.android.ioc");
  7. Valors.put(UserDictionary.Words.LOCALE, "es_ES");
  8. Valors.put(UserDictionary.Words.WORD, "Hospitalet");
  9. Valors.put(UserDictionary.Words.FREQUENCY, "100");
  10.  
  11. //Inserim
  12. UriNou = getContentResolver().insert(
  13. UserDictionary.Words.CONTENT_URI,
  14. Valors
  15. );

Publicant aplicacions

Publicar és el procés de fer les vostres aplicacions d’Android disponibles per als usuaris. La publicació consta principalment de dues tasques:

  • Preparar l’aplicació per a la distribució, compilant una versió de l’aplicació per a distribució.
  • Distribuir l’aplicació als usuaris, on es publicita, ven i distribueix la versió del programa als usuaris.

El procés de publicació es realitza després d’haver comprovat el funcionament de l’aplicació en un entorn de depuració.

Preparar l’aplicació consta d’una sèrie de passos:

  • Compileu i signeu la versió de distribució de l’aplicació. L’Android Studio proporciona tot el necessari per fer-ho.
  • Traieu les crides a Log. També, quan feu la generació del fitxer .apk assegureu-vos de seleccionar a build type l’opció release i no debug. A més, afegiu o modifiqueu els valors dels atributs versionCode i versionName que estan a l’element defaultConfig del build.gradle
  • Abans de distribuir l’aplicació, heu de comprovar la versió de distribució d’aquesta. Idealment, l’hauríeu de comprovar almenys en un telèfon i una tauleta.
  • Assegureu-vos que tots els recursos (imatges, vídeos…) de l’aplicació estan actualitzats.
  • Prepareu els servidors remots i els serveis si és que la vostra aplicació depèn d’aquests.
  • Creeu una icona per a l’aplicació.
  • Si voleu, podeu preparar un EULA (End User License Agreement, acord de llicència d’usuari final) per protegir la vostra propietat intel·lectual i la vostra persona.

Quan acabeu de preparar l’aplicació, creareu un fitxer signat .apk que podreu distribuir als usuaris.

Preparar l'aplicació

Per publicar una aplicació heu de crear un paquet que els usuaris puguin instal·lar i executar en els seus dispositius Android (versió release). El paquet de publicació de la versió conté els mateixos components que el fitxer .apk de depuració (codi font compilat, recursos, fitxer de manifest…) i es construeix amb les mateixes eines. Però el fitxer .apk que es publica està signat amb el vostre certificat i està optimitzat.

Per preparar l’aplicació per ser publicada, generalment es realitzen les següents tasques:

  1. Preparació de materials i recursos.
  2. Configuració de l’aplicació per a la publicació.
  3. Compilació de l’aplicació per a la publicació.
  4. Preparació dels servidors externs i recursos
  5. Comprovació de l’aplicació per a la publicació.

Claus criptogràfiques

El sistema Android requereix que cada aplicació instal·lada estigui signada digitalment amb un certificat propietat del seu desenvolupador (un certificat del qual el desenvolupador en tingui la clau privada). Android fa servir aquest certificat per identificar l’autor i establir relacions de confiança entre aplicacions.

Un requeriment per publicar a Google Play és que l’aplicació estigui signada amb una clau criptogràfica amb un període de validesa que acabi després del 22 d’octubre de 2033.

Icona de l'aplicació

La vostra aplicació ha de tenir una icona i ha de seguir les recomanacions sobre com han de ser aquestes, tal com podeu trobar a la Guia del Desenvolupador d’Android:

http://developer.android.com/guide/practices/ui_guidelines/icon_design_launcher.html

La icona de l’aplicació serveix per identificar-la i pot aparèixer a la pantalla principal, al launcher del dispositiu, a les meves descàrregues, quan es gestionen les aplicacions instal·lades… A més a més, si publiqueu l’aplicació a Google Play es mostrarà la icona als usuaris (en aquest cas també heu de publicar una versió d’alta resolució de la icona).

Altres continguts

Potser voleu preparar un EULA (End User License Agreement, acord de llicència d’usuari final). També, si voleu publicar la vostra aplicació a Google Play, heu de generar un text explicatiu del que fa i algunes captures de pantalla.

Configurar l'aplicació per a alliberament

Aquestes són recomanacions de configuracions de l’aplicació:

  • Escolliu un bon nom per al package de l’aplicació: no es podrà modificar després d’haver-la desplegat. Es pot configurar des del fitxer de manifest.
  • Netegeu els directoris del projecte de fitxers innecessaris.
  • Actualitzeu les configuracions del fitxer de manifest. Doneu valor als atributs versionCode i versionName del build.gradle tal com està explicat a la Guia del desenvolupador d’Android (http://developer.android.com/tools/building/configuring-gradle.html).
  • Treballeu en la compatibilitat de l’aplicació. Afegiu suport per a diferents pantalles i versions d’Android.
  • Actualitzeu les URL dels servidors i els serveis.

Construir l'aplicació signada

Si esteu fent servir Android Studio, podeu fer servir l’assistent d’exportació per crear una clau i exportar un fitxer .apk signat. Una clau privada adequada per signar l’aplicació ha de complir les següents característiques:

  • És de la vostra possessió
  • Representa la persona, corporació o organització que s’ha d’identificar amb l’aplicació
  • Té un període de validesa que supera el temps de vida esperat de l’aplicació. Es recomana un període de validesa d’almenys 25 anys
  • La clau no és la clau per defecte creada per les eines de l’SDK

Si no disposeu d’una clau, en podeu generar una amb l’ordre keytool. Tant si ho feu amb keytool o amb l’AndroidStudio heu de proporcionar alguns arguments:

  • Alias: un nom o àlies per a la clau.
  • Password: contrasenya de la clau.
  • Validity: temps de validesa. En Android Studio està expressat en anys, i en keytool en dies.
  • First and Last Name: nom i cognoms de la persona que signa.
  • Organization Unit: organització.
  • City: ciutat.
  • State or Province: estat o província.
  • Country Code: codi de país.

Per crear una clau i signar-la amb Android Studio seleccioneu Build/Generate Signed APK…, i ompliu les diferents opcions que us demana, tal com es pot veure a la figura. Podeu escollir una clau que ja tingueu desada al disc o crear-ne una de nova.

Figura Signant una aplicació

En el cas que feu servir llibreries de tercers (per exemple, la llibreria externa de Google Maps), és possible que necessiteu altres claus. En el cas de Google Maps, heu de registrar la vostra aplicació al servei de Google Maps i obtindreu una clau de l’API de Maps.

Comprovació de funcionament

Feu una comprovació del funcionament de l’aplicació, idealment en un dispositiu real (preferiblement un telèfon i una tauleta). A la Guia del desenvolupador d’Android hi trobareu consells de les coses que es poden comprovar de les aplicacions abans de distribuir-les: http://developer.android.com/guide/topics/testing/what_to_test.html.

Distribuir aplicacions

Podeu distribuir la vostra aplicació de diferents maneres. La forma habitual de distribuir una aplicació és a través d’un mercat d’aplicacions (abans anomenat Android Market, ara Google Play). Però també podeu distribuir les aplicacions directament.

Distribuint l'aplicació mitjançant Google Play

Google Play ha substituït el Google Market com la forma més habitual de distribuir aplicacions d’Android. És una plataforma que us permet publicitar, vendre i distribuir la vostra aplicació d’Android a usuaris de tot el món. Quan publiqueu a través de Google Play teniu accés a una sèrie d’eines de desenvolupador que us permeten analitzar les vendes i les tendències del mercat, així com controlar a qui es distribuirà la vostra aplicació. Per distribuir una aplicació a Google Play cal que seguiu aquests tres passos:

Per a una informació més detallada de com publicar a Google Play, podeu consultar la darrera informació publicada per Google a http://developer.android.com/distribute/tools/launch-checklist.html.

  1. Prepareu materials promocionals per vendre l’aplicació com captures de pantalla, vídeos, gràfics i un text explicatiu.
  2. Configureu les opcions i pugeu els continguts. Configurant alguns ajustaments de Google Play podeu escollir en quins països voleu que estigui disponible la vostra aplicació, el llistat de llenguatges que voleu fer servir i el preu que voleu cobrar en cada país. També podeu configurar detalls de l’aplicació com el tipus d’aplicació, la categoria i el tipus de contingut. Quan l’hàgiu acabat, podeu pujar els continguts promocionals de la vostra aplicació com a esborrany.
  3. Publiqueu la versió definitiva de l’aplicació. Si l’esborrany pujat és correcte, podeu fer clic a Publicar i en uns minuts la vostra aplicació estarà disponible per descarregar arreu del món.

Publicar l'aplicació des de la vostra pròpia web

Podeu distribuir la vostra aplicació fent-la descarregable des d’una pàgina web emmagatzemada en un servidor de la vostra elecció. Tot el que heu de fer és crear el fitxer .apk definitiu signat i crear un enllaç directe al fitxer des de la vostra pàgina web. Quan els usuaris visitin la pàgina des d’un dispositiu Android i descarreguin l’aplicació, el sistema Android detectarà el tipus de fitxer i instal·larà l’aplicació al dispositiu. Això únicament es podrà fer si l’usuari ha configurat el seu dispositiu per permetre la instal·lació d’aplicacions desconegudes, com es pot veure a la figura.

Figura Opció d’habilitar aplicacions d’origen desconegut a Android Lollipop

Publicar a través de la vostra pròpia web és una forma senzilla de publicar l’aplicació, però aquesta no tindrà la visibilitat que tenen les aplicacions a Google Play, i si voleu cobrar per ella n’haureu de gestionar vosaltres mateixos el pagament.

Publicant per correu electrònic

La forma més fàcil i ràpida de publicar una aplicació en un dispositiu és enviar el fitxer .apk signat per correu electrònic i obrir-lo des d’un dispositiu Android. El dispositiu reconeixerà el tipus de fitxer i instal·larà l’aplicació. Aquesta és la forma més còmoda de provar les vostres aplicacions en un telèfon o d’enviar la vostra aplicació a un grup reduït de destinataris (per exemple, la resta de membres del vostre grup de treball).

Anar a la pàgina anterior:
Referències
Anar a la pàgina següent:
Activitats