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, 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, per tal d'evitar que 
la interfície d'usuari s'enlenteixi.

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ó)

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 memoria lliure sñon dolents i cal alliberar recursos per als procesos que l'usuari requereixi de forma més immediata. Consequentment 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.

Cicla de vida dels procesos

Per defecte el sistema operatiu mira de mantenir el procés d'una aplicació tant de temps com sigui possible. Per determinar quins procesos 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.

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.

Threads

When an application is launched, the system creates a thread of execution for the application, called "main." This thread is very important because it is in charge of dispatching events to the appropriate user interface widgets, including drawing events. It is also the thread in which your application interacts with components from the Android UI toolkit (components from the android.widget and android.view packages). As such, the main thread is also sometimes called the UI thread.

Worker threads

The system does not create a separate thread for each instance of a component. All components that run in the same process are instantiated in the UI thread, and system calls to each component are dispatched from that thread. Consequently, methods that respond to system callbacks (such as onKeyDown() to report user actions or a lifecycle callback method) always run in the UI thread of the process.

For instance, when the user touches a button on the screen, your app's UI thread dispatches the touch event to the widget, which in turn sets its pressed state and posts an invalidate request to the event queue. The UI thread dequeues the request and notifies the widget that it should redraw itself.

When your app performs intensive work in response to user interaction, this single thread model can yield poor performance unless you implement your application properly. Specifically, if everything is happening in the UI thread, performing long operations such as network access or database queries will block the whole UI. When the thread is blocked, no events can be dispatched, including drawing events. From the user's perspective, the application appears to hang. Even worse, if the UI thread is blocked for more than a few seconds (about 5 seconds currently) the user is presented with the infamous "application not responding" (ANR) dialog. The user might then decide to quit your application and uninstall it if they are unhappy.

Additionally, the Andoid UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:

   Do not block the UI thread
   Do not access the Android UI toolkit from outside the UI thread 

Worker threads


Because of the single thread model described above, it's vital to the responsiveness of your application's UI that you do not block the UI thread. If you have operations to perform that are not instantaneous, you should make sure to do them in separate threads ("background" or "worker" threads).

For example, below is some code for a click listener that downloads an image from a separate thread and displays it in an ImageView:

public void onClick(View v) {

   new Thread(new Runnable() {
       public void run() {
           Bitmap b = loadImageFromNetwork("image.png");
           mImageView.setImageBitmap(b);
       }
   }).start();

}

At first, this seems to work fine, because it creates a new thread to handle the network operation. However, it violates the second rule of the single-threaded model: do not access the Android UI toolkit from outside the UI thread—this sample modifies the ImageView from the worker thread instead of the UI thread. This can result in undefined and unexpected behavior, which can be difficult and time-consuming to track down.

To fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:

   Activity.runOnUiThread(Runnable)
   View.post(Runnable)
   View.postDelayed(Runnable, long)

For example, you can fix the above code by using the View.post(Runnable) method:

public void onClick(View v) {

   new Thread(new Runnable() {
       public void run() {
           final Bitmap bitmap = loadImageFromNetwork("image.png");
           mImageView.post(new Runnable() {
               public void run() {
                   mImageView.setImageBitmap(bitmap);
               }
           });
       }
   }).start();

}

Now this implementation is thread-safe: the network operation is done from a separate thread while the ImageView is manipulated from the UI thread.

However, as the complexity of the operation grows, this kind of code can get complicated and difficult to maintain. To handle more complex interactions with a worker thread, you might consider using a Handler in your worker thread, to process messages delivered from the UI thread. Perhaps the best solution, though, is to extend the AsyncTask class, which simplifies the execution of worker thread tasks that need to interact with the UI.


Utilitzant AsyncTask

AsyncTask allows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.

To use it, you must subclass AsyncTask and implement the doInBackground() callback method, which runs in a pool of background threads. To update your UI, you should implement onPostExecute(), which delivers the result from doInBackground() and runs in the UI thread, so you can safely update your UI. You can then run the task by calling execute() from the UI thread.

For example, you can implement the previous example using AsyncTask this way:

public void onClick(View v) {

   new DownloadImageTask().execute("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);
   }

}

Now the UI is safe and the code is simpler, because it separates the work into the part that should be done on a worker thread and the part that should be done on the UI thread.

You should read the AsyncTask reference for a full understanding on how to use this class, but here is a quick overview of how it works:

   You can specify the type of the parameters, the progress values, and the final value of the task, using generics
   The method doInBackground() executes automatically on a worker thread
   onPreExecute(), onPostExecute(), and onProgressUpdate() are all invoked on the UI thread
   The value returned by doInBackground() is sent to onPostExecute()
   You can call publishProgress() at anytime in doInBackground() to execute onProgressUpdate() on the UI thread
   You can cancel the task at any time, from any thread

Caution: Another problem you might encounter when using a worker thread is unexpected restarts in your activity due to a runtime configuration change (such as when the user changes the screen orientation), which may destroy your worker thread. To see how you can persist your task during one of these restarts and how to properly cancel the task when the activity is destroyed, see the source code for the Shelves sample application.


Thread-safe methods

In some situations, the methods you implement might be called from more than one thread, and therefore must be written to be thread-safe.

This is primarily true for methods that can be called remotely—such as methods in a bound service. When a call on a method implemented in an IBinder originates in the same process in which the IBinder is running, the method is executed in the caller's thread. However, when the call originates in another process, the method is executed in a thread chosen from a pool of threads that the system maintains in the same process as the IBinder (it's not executed in the UI thread of the process). For example, whereas a service's onBind() method would be called from the UI thread of the service's process, methods implemented in the object that onBind() returns (for example, a subclass that implements RPC methods) would be called from threads in the pool. Because a service can have more than one client, more than one pool thread can engage the same IBinder method at the same time. IBinder methods must, therefore, be implemented to be thread-safe.

Similarly, a content provider can receive data requests that originate in other processes. Although the ContentResolver and ContentProvider classes hide the details of how the interprocess communication is managed, ContentProvider methods that respond to those requests—the methods query(), insert(), delete(), update(), and getType()—are called from a pool of threads in the content provider's process, not the UI thread for the process. Because these methods might be called from any number of threads at the same time, they too must be implemented to be thread-safe.


Interprocess Communication

Android offers a mechanism for interprocess communication (IPC) using remote procedure calls (RPCs), in which a method is called by an activity or other application component, but executed remotely (in another process), with any result returned back to the caller. This entails decomposing a method call and its data to a level the operating system can understand, transmitting it from the local process and address space to the remote process and address space, then reassembling and reenacting the call there. Return values are then transmitted in the opposite direction. Android provides all the code to perform these IPC transactions, so you can focus on defining and implementing the RPC programming interface.

To perform IPC, your application must bind to a service, using bindService(). For more information, see the Services developer guide.

Vegeu també


Enllaços externs