IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

En resum, pels impacients TL:TR, el més important a tenir en compte és:

Cada aplicació s'executa en el seu propi procés i tots els components de l'aplicació s'executen en aquest procés per defecte.
Qualsevol operació lenta o que bloquegui (per exemple una operació que esperi la finalització d'una operació I/O) s'ha de fer en un 
Thread nou (Worker thread o utilitzar AsyncTask), per tal d'evitar que la interfície d'usuari s'alenteixi.

Interessant lectura:

https://medium.com/@rotxed/going-multiprocess-on-android-52975ed8863c#.za4sequoh

Prerequisits

Android és un sistema basat en Linux. És interessant tenir coneixements generals de Processos i específicament de processos linux així de conceptes relacionats amb processos com IPC, thread-safe, etc.

Podeu consultar la web:

http://acacha.org/wiki_privada/index.php/Usuari:Sergi/Temari/Tema_16_Sistemas_operativos:_Gesti%C3%B3n_de_procesos.

Per obtenir teoria general sobre processos en sistemes operatius i les pàgines:

Processos_Linux

i

LPI_103.5._Crear,_monitoritzar_o_matar_processos

Introducció

Quan un component d'una aplicació s'inicia i l'aplicació no té cap component actualment en execució, el sistema operatiu Android inicia un nou procés Linux que es dedica a l'aplicació. Aquest procés té un sol fil d'execució, anomenat el main thread. Per defecte, tots els components de la mateixa aplicació s'executen al mateix procés i fil d'execució, si el component d'una aplicació s'inicia i ja existeix el fil d'execució principal de l'aplicació ( perque un altre component ja s'està executant), aleshores el nou component s'executa dins d'aquest fil d'execució principal.

En tot cas, es pot (i en certs casos que veurem és recomana o fins i tot és obligatori) executar diferents components de la mateixa aplicació en fils d'execució diferents.

Android, com la majoria de sistemes operatius actuals és un sistema operatiu multiprocés, és a dir, es possible executar múltiples processos al mateix temps, ja sigui utilitzant múltiples CPU al mateix temps o multiplexant en temps l'ús de la CPU.

Procesos i Threads (fils d'execució)

http://developer.android.com/intl/es/guide/components/processes-and-threads.html

Es pot controlar a quin procés pertant cada component d'una aplicació Android (vegeu Android Components) modificant el fitxer manifest.xml.

Els elements XML de cada tipus de component android del fitxer de manifest:

<activity>, <service>, <receiver>, <provider> ...

Suporten l'atribut XML:

android:process

Que permet especificar el procés en el que s'executa el component.

Es pot establir aquest atribut a cada components de forma que diversos components comparteixin el mateix procés (ja és el que es fa per defecte) o per que treballin en procesos diferents.

NOTA: També es pot establir el mateix procés per a components de diferents aplicacions, de forma que els components compartiran el mateix user id i els mateixos certificats

L'element:

<application>

també suporta l'atribut:

android:process

per tal d'establir un valor per defecte que s'aplicara a tots els components.

Android pot decidir en qualsevol moment que cal aturar un procés, especialment quan les condicions de memòria lliure són dolentes i cal alliberar recursos per als processos que l'usuari requereixi de forma més immediata. Conseqüentment tots els components que s'executen al mateix procés són destruides.

Quan el sistema operatiu Android decideix quin procés matar, ho fa donant-li un pes relatiu respecte al usuari. Per exemple, s'atura abans un procés que contingui activitats que ja no són visibles a la pantalla en comparació amb aquelles que ho són.La decisió però depèn de l'estat dels components del procés i s'explica en més detall als següents apartats.

Resources:

Cicle de vida dels procesos

http://www.techotopia.com/index.php/Understanding_Android_Application_and_Activity_Lifecycles

Per defecte el sistema operatiu mira de mantenir el procés d'una aplicació tant de temps com sigui possible. Per determinar quins processos són més importants que altres el sistema els clasifica en una jerarquia d'importància basant-se en els components del procés i el seu estat.

Android_process_priorities.png

Hi ha 5 nivells, ordenats per importància:

Procés en primer terme (foreground process)

És el procés actiu, el que esta utilitzant en aquest moment l'usuari. Un procés es considera d'aquesta categoria si es compleix alguna de les següents condicions:

  • Conté una activitat amb la que l'usuari està interaccionant (the Activity's onResume() method has been called).
  • Si el procés conté un Servei que està lligat a l'activitat amb la que l'usuari està interaccionant.
  • Si el procés conté un Servei que s'està executant en "foreground" ( el servei ha cridat el mètode startForeground()).
  • Si el procés conté un Servei que està executant un dels seus mètodes (callbacks) del cicle de vida (onCreate(), onStart(), o onDestroy()).
  • Si el procés conté un BroadcastReceiver que està executant el mètode: onReceive()

En general només uns pocs processos estan en aquest estat en un moment donat. Només es mata aquests processos com a últim recurs, és a dir que no hi ha prou memòria per a continuar.

Procés visible

Un procés que no té cap component en el mode anterior, però que encara pot afectar el que l'usuari veu per pantalla. S'han de complir les següents condicions:

  • Un procés que conté una activitat que no és troba en foreground però que encara és visible per a l'usuari (quan el mètode onPause() ha estat cridat). Això pot passar si l'activitat en primer terme és un dialeg que permet que l'activitat es vegi al fons
  • Un procés que conté un servei que està lligat a una Activitat que sigui visible.

Els procesos visibles són considerats extremadament importants i no seran matats a no ser que sigui necessari per tal de mantenir en execució tots els processos de tipus primer terme.

Procés de tipus servei

Un procés que executa un servei que ha estat iniciat pel mètode startService() i no cau en cap de les categories anteriors.

Encara que aquests tipus de processos no estan relacionats directament amb res que l'usuari vegi directament, si que realitzen normalment tasques que són importants per l'usuari (per exemple executant un audio o baixant un fitxer). Per aquesta raó el sistema mira sempre de no matar aquestes aplicacions excepte que no hi hagui prou memòria per mantenir en execució tots els procesos en primer terme i els processos visibles.

Procés de tipus segon terme

Un procés que conté un Activitats que actualment no és visible per a l'usuari (l'activitat ha cridat el mètode onStop()). Aquests processos no tenen impacta directe en l'experiència de l'usuari i per tant el sistema mata aquests processos en qualsevol moment que necessitti alliberar memòria per a qualsevol procés que estigui dins de les anteriors categories.

Normalment hi ha força processos en aquesta categoria i per aquesta raó es guarden a una estructura de dades de tipus LRU (least recently used) per tal de garantir que el procés de l'activitat que ha estat utilitzat de forma més recent per part de l'usuari sigui l'últim en ser eliminat. Si una activitat implement els seus mètodes de cicla de vida correctament, i guarda el seu estat actual, eliminar el procés no ha de tenir cap implicació en la "user experience", ja que l'usuari navega endarrera cap a l'activitats i l'activitat restaura el seu estat.

Procés buit

Un procés que no conté cap component actiu. L'únic raó de mantenir aquest tipus de procés és per cache, per tal de millorar els temps d'inici de les aplicacions.

Activitats i processos

Cicle de vida de les activitats

Vegeu Desenvolupament_Android#Cicle_de_vida

Activity Stack

Vegeu un tema relacionat Activity Stack

Threads

Quan s'inicia una aplicació, el sistema crea un fil d'execució o thread anomenat main o fil d'execució principal.

Aquest thread és molt importants perquè s'encarrega de gestionar els esdeveniments relacionats amb la interfície gràfica. Aquí s'interactua amb els components del Android UI toolkit(components dels paquets android.widget i android.view. Per aquesta raó ta,bé se conegut com a UI thread o fil d'execució de la interfície gràfica.

Worker threads

El sistema no crea un fil d'execució diferent per a cada instància d'un component Android. Tots els components s'executen al mateix fil d'execució, el fil principal.

Si la nostra aplicació està realitzant operacions intensives en recursos, el sistema es pot veure afectat si tot s'executa a aquest procés principal. Per exemple realitzar operacions lentes com accesos de xarxa o consultes a bases de dades poden blouqejar la interfície gràfica. Quan el fil principal està bloquejat, aleshores noes poden executar les operacions de "redibuixar" widgets i la interfície gràfica no funciona de forma àgil.

Des de la perspectiva de l'usuari l'aplicació sembla penjada. A més. si la interfície es bloquejada durant més de 5 segons pot apareixer el dialeg:

"application not responding" (ANR)

A més cal tenir en compte que la Android UI toolkit no és thread-safe. No es poden fer manipulacions de la interfície gràfica des de fora del fil d'execució principal.

Cal complir les següents dos normes:

  • No bloquejeu mai el fil d'execució principal
  • No accediu al Android UI toolkit desde fora del fil d'execució principal.

Worker threads

Quan tingueu que realitzar una operació lenta cal executar-les en segon terme o en el que es coneix com a Worker thread, un fil d'execució independent del fil d'execució principal.

Podeu tindrà la temptació d'escriure un codi com:

public void onClick(View v) {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
}

L'error serà:


android.os.NetworkOnMainThreadException
12-18 10:04:10.244 1750-1750/com.iesebre.dam2.sergi.androidprocesstinkering W/System.err:     at   
android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1145)

Vegem un exemple que descarrega una imatge i la mostra en un ImageView:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}

On:

  private Bitmap loadImageFromNetwork(String url){
      try {
          Bitmap bitmap = BitmapFactory.decodeStream((InputStream)new URL(url).getContent());
          return bitmap;
      } catch (Exception e) {
          e.printStackTrace();
    }
 }

Compte però que aquest codi és parcialment correcte! Amb aquest codi complim la primera norma, que es no bloquejar el fil d'execució principal executant una operació lenta en un fil d'execució a part. A la línia:

mImageView.setImageBitmap(b);

Cal tenir en compte però que estem violant la segona norma:

No accedir al Android UI toolkit des de fils d'execució que no siguin el principal

Això pot provocar un comportament indefinit o inesperat de l'aplicació i que pot ser molt dífícil de detectar i/o depurar. El error que us donarà al executar és el següent:

FATAL EXCEPTION: Thread-91
                                                                                              Process: com.iesebre.dam2.sergi.androidprocesstinkering, PID: 1322
                                                                                              android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Per tal de solucionar aquest problema, tenim diferents formes d'accedir al UI thread des dels altres fils d'execució. Els següents mètodes poden ser d'utilitat:

Activity.runOnUiThread(Runnable) [1]
View.post(Runnable) [2]
View.postDelayed(Runnable, long) [3]

Vegem un exemple de com solucionar el codi anterior utilitzant el mètode View.post(Runnable):

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

Ara aquesta implementació és "thread-safe".

Aquesta solució però no sempre és la millor si la complexitat de l'operació a realitzar és més important que la de l'exemple. Hi ha dos recomanacions per aquests casos:

  • Utilitzar un Handler (gestor) al worker thread per tal de processar els missatges de comunicació entre el thread principal i el fil treballador.
  • Extendre la classe AsyncTask

Utilitzant AsyncTask

http://jarroba.com/asynctask-en-android/

Ciclo-de-vida-del-Asynktask-cancelado-de-Android-www.Jarroba.com_.png

Codigo-Asynktask-de-Android-Facil-www.Jarroba.com_.png

AsyncTask us permet realitzar tasques de forma asíncrona a la vostra interfície d'usuari. Realitza les operacions que bloquejen el sistema en un worker thread independent del UI thread i aleshores publica els resultats al fil principal sense la necessitat de gestionar els threads o els handlers.

Per a implementar AsyncTask cal heretar la classe:

AsyncTask http://developer.android.com/reference/android/os/AsyncTask.html

I implementar el mètode:

doInBackground()

Que s'executa en un pool de threads en segon terme. Per actualitzar la interfície gràfica cal implementar el mètode:

onPostExecute()

que proporciona el resultat del mètode doInBackground() al UI thread. S'utilitza el mètode:

execute()

des de la UI thread per executar la AsyncTask. Vegem un exemple:

 public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
 }
 
 private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }
    
    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
 }

Ara el UI és thread-safe (segur) i el codi és més simple ja que se separa la part que s'ha de fer al worker thread de la part que s'ha de fer al UI thread.

per a més informació llegiu:

http://developer.android.com/reference/android/os/AsyncTask.html

Es poden especificar el tipus de paràmetres, els valors de progress i el valor final utilitzant genèrics (Java generics):

  • El mètode doInBackground() s'executa automàticament en un worker thread.
  • Els mètodes onPreExecute(), onPostExecute(), i onProgressUpdate() s'invoken tots als UI thread
  • El valor de retorn de doInBackground() s'envia a onPostExecute()
  • Es pot cridar el mètode publishProgress() en qualsevol moment al mètode doInBackground() per tal d'executar onProgressUpdate()al UI thread.
  • Es pot cancelar la tasca en qualsevol moment, des de qualsevol thread.

NOTA: Oco! Un problema que ens podem trobar en qualsevol moment al worker thread és un reinici de l'activitat per exemple per un canvi en la configuració en temps d'execució (per eemple, l'orientació de pantalla) i pot destruir el worker thread Per veure com es poden persistit les tasques i com cancelar adequadament les tasques quan l'activitat és destruida, podeu veure l'aplicació exemple Shelves

Com funcionen els paràmetres AsyncTask

http://jarroba.com/asynctask-en-android/

Una tasca asíncrona (asinc tasck) és una operació que s'executa en un fil d'execució secundari ( p. ex. no el UI thread) del qual volem mostra el resultat al UI thread. Es defineixen de la següent forma:

class MiTarea extends AsyncTask<Parametros, Progreso, Resultado> {

      

       @Override protected void onPreExecute() {

              …     

       }

       @Override protected Resultado doInBackground(Parametros... par) {

             …     

       }

  @Override protected void onProgressUpdate(Progreso... prog) {

       …     

 }

  @Override protected void onPostExecute(Resultado result) {

          …    

}

}

on "Parametros, Progreso y Resultado" s'han de reemplazar per classes Java segons els tipus de dades amb els que treballi la tasca.

IMPORTANT: El tres punts indiquen que es pot passar un nombre variable d'objecte de la classe

Els 4 mètodes que podem sobreescriure es corresponen als quatre passos que seguirà la AsyncTask per executar la tasca:

  • onPreExecute(): s'executen les tasques prèvies a la tasca principal. S'utilitza per a configurar la tasca i per a mostrar al UI thread algun tipus d'identificador que ha començat la tasca (p. ex. un Toast, un progressBar, etc). Pot ser opcional.
  • doInBackground(Parametros...): S'executa un cop acabada l'execució de onpreExecute(). Aquí s'executa la tasca principal. És l'únic mètode que no s'executa al UI thread i per tant no en pot fer ús. La clase "Parametros" ha de ser reemplaçada per una clase concreta que serà utilitzada com a paràmetres entrada de la tasca.
  • onProgressUpdate(Progress...): S'utilitza per a mostrar al UI thread el progrés de la tasca. Pot ser opcional. S'executa al UI thread. El progrés de la tasca la de programanr el programador utilitzant el mètode publishProgress(Progress...) a doInBackground(). La classe "Progress" s'utilitza per passar informació. Un ús freqüent utilitzar un Integer i representar el procentatge en un valor de 1 a 100.
  • onPostExecute(Result): S'executa al UI thread i serveix per indicar el final de la tasca. El paràmetre d'entrada (classe "Result") correspont a un objecte retornat pel mètode "doInBackground()".

Un cop definida la tasca es pot executar de la següent forma:

MiTarea tarea = newMiTarea();
tarea.execute(p1, p2, p3);

On p1, p2, p3 és una llista d'objectes de la classe "Parametros" i es poden passar un número variable de paràmetres.

IMPORTANT: Execute és mètode asíncron és a dir que s'executa en paral·lel i no atura l'execució del procés que crida a execute

Consulteu el següent diagrama on es mostra l'ordre d'execució dels mètodes:

hilos.PNG

Recursos:

Exemples

sgoliver
http://www.sgoliver.net/blog/?p=3099
https://github.com/sgolivernet/curso-android-src/tree/master/android-asynctask

Mètodes Thread-safe

En algunes situacions, els mètodes que s'implementen poden ser executats des de més d'un fil d'execució alhora i per tant han de ser escrits per ser thread-safe.

Això és sobretot cert per a mètodes que poden ser cridats remotament com els mètodes associats a un servei (bound service). Quan la crida a un mètode implementat en un IBinder s'origina al mateix procés al que el IBinder s'executa, el mètode s'executa al thread de qui crida el procés. En canvi quan la crida s'origina en un altre procés el mètode és executat en un thread escollit d'un pool de fils d'execució que el sistema manté dins del mateix procés que el IBinder ( que no s'executa al UI thread del procés). Com un servei pot tenir més d'un client, més d'un procés pot executar el mateix tros de codi én un precís instant de temps. Per tant els mètodes IBinder han de ser thread-safe.

també un content provider pot rebre peticions de dades que s'originen a un altre procés. Tot i que les classes ContentResolver i ContentProvider amaguen els detalls de com la comunicació entre processos és executada, els mètodes de ContentProvider:

query(), insert(), delete(), update(), i getType()

són cridats des de un pool de fils d'execució i no al UI thread del procés. Per tant com poden set cridats al mateix temps per diferents processos han de ser thread-safe.

Interprocess Communication (IPC)

Android ofereix un mecanimes de IPC o Interprocess communication utilitzant remote procedure calls (aka RPCs), que permet que un mètode sigui cridat per una activitat (o qualsevol altre component d'una aplicació) però s'executi de forma remota en un altre procés, i el resultat de l'execució es retorni al caller (mètode que executa el mètode remot)

El sistema operatiu Android proveix el codi per realitzar transaccions IPC, i el programador es pot centrar en proporcionar la RPC programming interface.

Per realitzar IPC, la vostra aplicació s'ha de lligar (bind) a un servei utilitzant:

bindService() [4]

Per a més informació vegeu:

Services developer guide

Android services

Vegeu també Android Services

Exemples

Image downloader avançat

Vegeu:

http://android-developers.blogspot.com.es/2010/07/multithreading-for-performance.html

Operacions de xarxa

Vegeu Programació_xarxes_Android#Realitzar_les_operacions_de_xarxa_en_un_Worker_Thread

Apunts alumnes

Vegeu també

Enllaços externs