Una de las tareas mas habituales que tenemos que hacer cuando programamos aplicaciones Android es acceder a información ubicada de forma remota. Esta operación se suele realizar a traves de una petición Android REST. Al principio todo parece muy sencillo sin embargo cuando nos ponemos a programarlo las cosas se pueden complicar un poco ¿Porque? Vamos a intentar explicarlo. Supongamos que tenemos una actividad que contiene un boton que va a realizar una petición remota.
Recordemos que una Actividad dispone de un Thread (UI Thread) que se encarga de su correcto funcionamiento y de responder a las peticiones del Usuario . Si nosotros solicitamos que el botón realice una tarea que implique una petición remota nos podemos encontrar con la siguiente situación.
En este caso pulsamos un boton y realizamos la petición que remota tardará unos segundos en ejecutarse .Así pues el Thread que se esta encargando de ejecutarla (Thread UI) quedará bloqueado a la espera de una respuesta . Esto nos generará un efecto colateral . El interface de usuario dejará de responder ya que su Thread esta ocupado .Por lo tanto la aplicación quedará bloqueada para el usuario .¿Como podemos solventar esto?
Android REST y AsyncTask
Para solventar estos problemas Android dispone de una clase especial de tipo Genérico que se encarga de realizar peticiones Asincronas en otro Thread y que se denomina AsyncTask. Simplemente deberemos extender de ella y utilizarla. Ahora bien cuando vamos a realizar esta operación nos encontramos con lo siguiente
private class AccesoRemoto extends AsyncTask<String, String, String> { .. }
¿Que significan los distintos parámetros de tipo Genérico que la clase obliga a utilizar? . Parece difícil de entender sin embargo las cosas son mas sencillas de lo que parecen. Cuando una actividad crea una tarea asincrona necesita comunicarse con ella ,ahora bien ¿En que situaciones?. Pues parece lógico que para pasarla información , ver su progreso y obtener un resultado final .
Así pues los parámetros genéricos que recibe la tarea Asincrona son simplemente los tipos de datos a gestionar en cada una de las situaciones . Por ejemplo en nuestro caso queremos realizar una petición Http a un servicio REST . ¿Queremos pasar algún dato? . La respuesta es NO . Por lo tanto el primer parametro será de tipo Void. ¿Queremos obtener información sobre el progreso de la tarea? .La respuesta es NO por lo tanto el segundo parámetro será Void también .¿Queremos recibir algún resultado? La respuesta es Si, una cadena con la respuesta del Servicio ,por lo tanto el tercer parámetro será de tipo String.
private class AccesoRemoto extends AsyncTask<Void, Void, String> { .. }
Una vez aclarados los parámetros .Vamos a construir nuestra TareaAsincrona dentro de una Actividad y rellenar su método doInBackground que será el encargado de realizar una petición Http para mas adelante recibir los resultados.
package com.arquitecturajava; import java.io.BufferedReader; import java.io.InputStreamReader; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; import com.example.android020accesoremoto.R; public class MainActivity extends Activity implements OnClickListener { Button boton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); boton = (Button) findViewById(R.id.button1); boton.setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View arg0) { AccesoRemoto acceso= new AccesoRemoto(); acceso.execute(); } private class AccesoRemoto extends AsyncTask<Void, Void, String> { protected String doInBackground(Void... argumentos) { StringBuffer bufferCadena = new StringBuffer(""); try { HttpClient cliente = new DefaultHttpClient(); HttpGet peticion = new HttpGet( "http://10.0.2.2:8080/Web001/ServletHola"); // ejecuta una petición get HttpResponse respuesta = cliente.execute(peticion); //lee el resultado BufferedReader entrada = new BufferedReader(new InputStreamReader( respuesta.getEntity().getContent())); String separador = ""; String NL = System.getProperty("line.separator"); //almacena el resultado en bufferCadena while ((separador = entrada.readLine()) != null) { bufferCadena.append(separador + NL); } entrada.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return bufferCadena.toString(); } protected void onPostExecute(String mensaje) { Toast.makeText(MainActivity.this, mensaje, Toast.LENGTH_SHORT).show(); } }
Aunque el código parece un poco complejo de entender simplemente se realiza una petición get a “http://10.0.2.2:8080/Web001/ServletHola” que nos devuelve una cadena. Una vez tenemos la cadena se ejecuta el método onPostExecute que la recibe como parametro para mostrarla en un mensaje Toast. Recordemos que tendremos que haber habilitado los permisos de red en el fichero de manifiesto.
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Buen post, veo que utilizan AsyncTask para un splascreen, tambien vi que usan en un request http webservices con este forma e incluso usando retrofit, aun no me queda claro porque lo usan, o cuando si aparecerian el error, porque si tomara mucho tiempo la respuesta por ejemplo usando volley se puede aumentar el tiempo de espera “.setRetryPolicy(new DefaultRetryPolicy(…” con retrofit supongo q tambien se podra. Pero porque usaria esto con peticiones webservices? por el tiempo? la cantidad de datos?.
Gracias
ademas tambien veo nuevos temas como rxjava /rxandroid, esto seria en vez de usar asyntask?. si aclararías mis dudas.
Gracias.
si 🙂 . Hoy por hoy se usa mas rxjava
Buen post, veo que utilizan AsyncTask para un splascreen, tambien vi que usan en un request http webservices con este forma e incluso usando retrofit, aun no me queda claro porque lo usan, o cuando si aparecerian el error, porque si tomara mucho tiempo la respuesta por ejemplo usando volley se puede aumentar el tiempo de espera “.setRetryPolicy(new DefaultRetryPolicy(…” con retrofit supongo q tambien se podra. Pero porque usaria esto con peticiones webservices? por el tiempo? la cantidad de datos?.
Gracias
Muy buen tutorial, simple y directo. Gracias.
Que hago para el resultado, en este caso la variable mensaje, exhibirla en la main UI en un EditView por ejemplo ? Intenté hacerlo en lugar del Toast pero se detiene la app. Con el Toast funciona perfecto. Cabe señalar que la variable mensaje si trae todos los registros de la base de datos y los depliega muy bien en el Toast. Gracias
Excelente aporte Celio, si bien existen librerías como loopj, volley o retrofit, que facilitan la programación encuentro que esta es la mejor solución en caso de querer por ejemplo hacer peticiones cada n segundos, cosa que en las librerías antes mencionadas se complica aplicarlo.
saludos y gracias
gracias 🙂
Muy bueno el ejemplo.
Carlos, lei bien o comentas que para un mayor cantidad de datos AsyncTask no es lo recomendado? Saludos
Es mejor usar Rxjava para esas situaciones 🙂
muy bien explicado, muchísimas gracias.
de nada 🙂
Una pregunta, tengo un java que adecue para obtener información de mi sitio web, es decir, cuando requiero hacer una consulta lanzo esta aplicación, esta la lanzo dentro de un async para poder enviar un mensaje de espera también y que no se trabe la aplicación pero tengo problemas, cuando lo ejecuto desde una conxion wifi funciona al 100% pero cuando lo tengo con datos o red móvil la mayoría de las veces solo me devuelve 1000 caracteres, que pasa? Les dejo mi código para ver si me pueden ayudar por favor. import android.content.Context; import android.net.Uri; import android.os.AsyncTask; import android.text.Html;… Read more »
Has probado a usar Buffered reader?
No estoy seguro, me podrias dar un ejemplo? no se si te refieras a esta funcion.
/** Reads an InputStream and converts it to a String.
* @param stream InputStream containing HTML from targeted site.
* @param len Length of string that this method returns.
* @return String concatenated according to len parameter.
* @throws IOException
* @throws UnsupportedEncodingException
*/
private String readIt(InputStream stream, int len) throws IOException {
Reader reader;
reader = new InputStreamReader(stream, “utf-8”);
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
}
Recomiendo la utilizacion de retrofit para trabajar con rest en android
ver en: http://square.github.io/retrofit/
gracias por el aporte
Muy buena librería para hacer servicios Rest para android
gracias 🙂
Hola
Me gustaría saber que hay de cierto en la literatura en relación a los problemas de AsynTask.
¿que relación hay entre el ciclo de vida de la actividad y el asyntask?
¿el uso de asyntask puede implicar perdidas de memoria?
¿dos asysntask se ejecutan en paralelo?
Etc.
Es que son tantas las cosas que leo, que da hasta miedo utilizarlo.
Un saludo.
Tienes razón para cosas sencillas cubren … pero para cosas muy avanzadas uff … tendras que plantearte otras cosas . Me ha parecido interesante el libro que tiene pactkpub de android async 😉
Podría hacer un tutorial de sincronizacion de base de datos entre SQLite y MySQL usando android y permitiendo que la aplicación almacene los datos en SQLite si el teléfono android no tiene conexión a Internet. y que pregunte si queremos sincronizar las bases de datos una vez que tengamos Internet para que los datos se vean sincronizados en MySQL. me gustaría que pusiera disponible el código fuente. y gracias
muy buena documentación referente a temas que antes desconocía, Gracias por ofrecer buen material.
gracias 🙂 me apunta la idea de Android y SQL Lite a ver si puedo sacar un hueco a futuro
Jajaja no inventes y no quieres que desarrolle tu aplicacion y cobre por ti ?
[…] Usando Android para realizar una petición Android REST apoyandonos en el manejo de tareas Asyncronas. […]