Desarrollar una aplicación de búsqueda en Twitter: Obtener y analizar el feed JSON de Twitter
Spanish (Español) translation by steven (you can also view the original English article)
Esta serie de tutoriales de dos partes te presentará los fundamentos para trabajar con servicios web RESTful utilizando el SDK de Android. En el camino, aprenderás a realizar búsquedas en la API pública de Twitter.
En este tutorial usaremos AsyncTask para buscar los tweets JSON, analizarlos y luego mostrarlos dentro de la interfaz de usuario. Aunque el código de este tutorial es específico de Twitter, los principios involucrados se aplican a la obtención de muchas otras fuentes web y API mediante una arquitectura RESTful.
Esta serie de tutoriales sobre la búsqueda de Twitter con Android se divide en dos partes:
Paso 1: Crear una clase AsyncTask interna
Vamos a utilizar una AsyncTask para llevar a cabo la búsqueda, el análisis y la visualización del feed de tweets en la consulta de búsqueda del usuario. Al usar AsyncTask, podemos cambiar el procesamiento a un subproceso en segundo plano, con los resultados aún accesibles a la interfaz de usuario de la aplicación para su visualización. En tu clase Activity, agrega una clase AsyncTask interna después del método searchTwitter:
1 |
private class GetTweets extends AsyncTask<String, Void, String> { |
2 |
|
3 |
}
|
La clase AsyncTask es abstracta, lo que significa que debes crear una subclase para poder usarla. Tales subclases deben implementar un método particular que agregaremos a continuación. Mientras tanto, Eclipse mostrará mensajes de error (solo ignóralos por ahora).
AsyncTask usa tres tipos genéricos como puedes ver en la línea de apertura de la declaración de clase. El primero especifica el tipo de parámetro; en nuestro caso, es una cadena, ya que vamos a pasar la cadena de la URL que creamos. El tipo medio es para unidades de progreso, que no usaremos en este tutorial, por lo que simplemente especificamos void. Las unidades de progreso se pueden utilizar para indicar el progreso de las tareas en segundo plano. El tercer tipo es otra cadena, que devuelve el proceso en segundo plano. En nuestro caso, este será el texto del feed JSON recuperado.
Paso 2: Realizar el procesamiento en segundo plano
Dentro de tu clase AsyncTask, agrega el siguiente método, que anulamos de la clase principal:
1 |
@Override
|
2 |
protected String doInBackground(String... twitterURL) { |
3 |
|
4 |
}
|
El tipo de parámetro es una cadena, que coincide con el primer tipo que indicamos en la línea de apertura de la declaración de clase. Esta cadena será la URL de la API de búsqueda de Twitter que creamos la última vez. Dentro de este método, especificamos qué proceso en segundo plano queremos que lleve a cabo AsyncTask. El método también devuelve un valor de tipo cadena, para que coincida con lo que especificamos como tercer parámetro en la línea de apertura de la declaración de clase. Esta cadena devuelta se recibirá como parámetro de un segundo método, que implementaremos más adelante.
Sugerencia: Si no estás familiarizado con las clases abstractas o los tipos genéricos, no te preocupes, lo que estamos haciendo aquí es simplemente seguir un patrón. Como ocurre con gran parte de la plataforma Android, puedes aprender a utilizar estos recursos a partir de los ejemplos de la Guía para desarrolladores y, en este caso, la Referencia de API.
Paso 3: Enviar la petición
El resultado de la búsqueda de Twitter será una cadena, que crearemos a partir de la respuesta que recibamos. Dentro del método doInBackground, comienza creando un generador de cadenas:
1 |
StringBuilder tweetFeedBuilder = new StringBuilder(); |
Aunque estamos usando una sola cadena de URL, el método va a recibir una matriz de objetos del tipo especificado (cadena), así que vamos a recorrerlo:
1 |
for (String searchURL : twitterURL) { |
2 |
|
3 |
}
|
En realidad, el ciclo solo se repetirá una vez, pero puedes extender tu código para obtener múltiples fuentes de búsqueda, por lo que usaremos un ciclo por ahora. Dentro del bucle, crea un cliente HTTP para ejecutar la solicitud:
1 |
HttpClient tweetClient = new DefaultHttpClient(); |
Ahora tenemos que detectar las excepciones de entrada/salida, como sucede invariablemente cuando intentas obtener datos desde cualquier lugar fuera de tu aplicación. Agrega bloques de tipo try y catch:
1 |
try { |
2 |
|
3 |
}
|
4 |
catch(Exception e) { |
5 |
tweetDisplay.setText("Whoops - something went wrong!"); |
6 |
e.printStackTrace(); |
7 |
}
|
Si hay un error de entrada/salida, simplemente escribimos un mensaje en la interfaz del usuario. Dentro del bloque try, crea un objeto Get HTTP para emitir la solicitud, pasando la URL de búsqueda:
1 |
HttpGet tweetGet = new HttpGet(searchURL); |
Ahora podemos continuar y ejecutar la solicitud, almacenando los resultados en un objeto HTTP de tipo "response":
1 |
HttpResponse tweetResponse = tweetClient.execute(tweetGet); |
Podremos utilizar el objeto de respuesta para acceder al contenido y estado del mensaje de respuesta recibido de Twitter.
Paso 4: Procesar la respuesta
Antes de intentar procesar el mensaje de respuesta, verifiquemos el estado. Aún dentro del bloque try:
1 |
StatusLine searchStatus = tweetResponse.getStatusLine(); |
Si la respuesta es correcta, podemos continuar e intentar analizar los tweets; de lo contrario, mostraremos un mensaje de error al usuario:
1 |
if (searchStatus.getStatusCode() == 200) { |
2 |
|
3 |
}
|
4 |
else
|
5 |
tweetDisplay.setText("Whoops - something went wrong!"); |
Dentro del bloque de instrucciones if, ahora podemos recuperar la entidad HTTP y el contenido del mensaje como un flujo de entrada:
1 |
HttpEntity tweetEntity = tweetResponse.getEntity(); |
2 |
InputStream tweetContent = tweetEntity.getContent(); |
Ahora podemos comenzar a traer el contenido del mensaje al programa, utilizando un lector de flujo de entrada en lugar de un lector con búfer para administrar los datos entrantes:
1 |
InputStreamReader tweetInput = new InputStreamReader(tweetContent); |
2 |
BufferedReader tweetReader = new BufferedReader(tweetInput); |
Leamos los datos una línea a la vez, agregando cada línea al String Builder que creamos:
1 |
String lineIn; |
2 |
while ((lineIn = tweetReader.readLine()) != null) { |
3 |
tweetFeedBuilder.append(lineIn); |
4 |
}
|
Esto seguirá agregando al String Builder hasta que se hayan procesado todos los datos recibidos. Ahora ve al final del método doInBackground, después del bloque catch, y devuelve la cadena en la que hemos construido los resultados:
1 |
return tweetFeedBuilder.toString(); |
Ese es el método doInBackground completo: lleva a cabo la búsqueda del tweet y lo importa a la aplicación, por lo que ahora podemos analizarlo y mostrarlo.
Paso 5: Analizar el feed JSON de tweets
Ahora que AsyncTask ha recuperado el feed del tweet, podemos analizarlo y mostrarlo en la interfaz de usuario de nuestra aplicación. Para hacer esto podemos implementar otro método de la clase AsyncTask, agregado después de doInBackground, aún dentro de la declaración de la clase AsyncTask:
1 |
protected void onPostExecute(String result) { |
2 |
|
3 |
}
|
Observa que este método recibe un parámetro de cadena, que es lo que especificamos como el tercer tipo en la línea de declaración de la clase de apertura y lo que devolvimos de doInBackground en el último paso. La cadena es el texto JSON que representa los tweets recientes sobre el término de búsqueda del usuario recuperado de Twitter. Necesitaremos analizar este texto para acceder al contenido de los tweets y mostrarlos en la interfaz de usuario.
Construiremos el texto del tweet en una cadena para mostrar, así que inicia el método onPostExecute creando otro String Builder o "generador de cadenas":
1 |
StringBuilder tweetResultBuilder = new StringBuilder(); |
Los métodos de procesamiento JSON pueden generar excepciones, así que agrega los bloques try y catch a continuación:
1 |
try { |
2 |
|
3 |
}
|
4 |
catch (Exception e) { |
5 |
tweetDisplay.setText("Whoops - something went wrong!"); |
6 |
e.printStackTrace(); |
7 |
}
|
Deberías notar un patrón aquí; como con cualquier programa en el que intentes recuperar y procesar datos externos, debes prestar especial atención al manejo de posibles errores.
Dentro del bloque try, crea un objeto JSON, pasando la cadena de texto JSON:
1 |
JSONObject resultObject = new JSONObject(result); |
Dentro de la cadena de texto JSON hay una matriz que contiene los tweets, además de otros datos: realiza una consulta de Twitter pegando la URL de búsqueda en la barra de direcciones de tu navegador como lo hicimos la última vez, por ejemplo:
1 |
http://search.twitter.com/search.json?q=android |
Es posible que debas copiar y pegar el texto resultante en un editor de texto para leerlo con eficacia. Si observas el texto JSON, verás la siguiente sección, que marca el comienzo de la matriz de tweets:
1 |
"results":[ |
Necesitamos recuperar esta matriz de tweets, usando su nombre: "results". Obtén la matriz "results" del objeto JSON:
1 |
JSONArray tweetArray = resultObject.getJSONArray("results"); |
Ahora podemos recorrer los tweets y prepararlos para mostrarlos.
Paso 6: Iterar la matriz de tweets
Si vuelves a mirar el texto JSON recuperado a través del navegador (u, opcionalmente, dentro de Eclipse si lo escribes en el registro de Android mientras ejecutas tu aplicación), puedes ver el contenido devuelto para cada tweet en la matriz. En este tutorial solo escribiremos el nombre de usuario de la cuenta que estamos consultando y el contenido del tuit en sí. Puedes ampliar la aplicación para incluir la información que desees. En el texto devuelto por Twitter, el nombre de usuario se representa como "from_user" y el contenido del tweet se llama "text". Mira el contenido JSON para verificar esto y ver qué otros elementos de datos hay allí.
En tu bloque try, después de obtener la matriz de tweets, agrega un bucle for para recorrer los tweets:
1 |
for (int t=0; t<tweetArray.length(); t++) { |
2 |
|
3 |
}
|
Dentro del ciclo, obtén cada elemento como un objeto JSON:
1 |
JSONObject tweetObject = tweetArray.getJSONObject(t); |
Ahora recupera el nombre de usuario de este objeto, agregándolo al String Builder junto con un texto adicional para fines de visualización:
1 |
tweetResultBuilder.append(tweetObject.getString("from_user")+": "); |
La clase de objeto JSON proporciona un método getString para este propósito; también existe un método get alternativo. Ahora recupera y agrega el contenido del tweet:
1 |
tweetResultBuilder.append(tweetObject.get("text")+"\n\n"); |
Paso 7: Mostrar los resultados
Ahora muévete después del bloque catch dentro del método onPostExecute. Si el procesamiento ha funcionado, mostramos el resultado en la Vista de texto, de lo contrario mostramos un mensaje de error:
1 |
if(tweetResultBuilder.length()>0) |
2 |
tweetDisplay.setText(tweetResultBuilder.toString()); |
3 |
else
|
4 |
tweetDisplay.setText("Sorry - no tweets found for your search!"); |
Nuestra clase AsyncTask ahora está completa.
Paso 8: Crear una instancia y ejecutar AsyncTask
Disponemos de un AsyncTask para realizar nuestro tratamiento; solo tenemos que llamarlo. Vuelve a tu método searchTwitter, dentro del bloque try y después de que construyamos la cadena de URL de consulta de búsqueda. Crea una instancia de la nueva clase AsyncTask y llama al método execute en ella, pasando la cadena de URL:
1 |
new GetTweets().execute(searchURL); |
Observa que pasamos una cadena, que es lo que especificamos en la declaración AsyncTask y el método doInBackground.
Esa es la aplicación básica completa: puedes probarla en el emulador y en dispositivos Android. Pruébalo con diferentes consultas de búsqueda como hashtags. Por supuesto, puedes ampliar la aplicación para mostrar más información de los tweets e incluso para vincular a Twitter en los clics de los usuarios. Tal como están las cosas, el usuario puede desplazarse por el tweet devuelto y realizar búsquedas repetidas como desee.


Conclusión
En estos dos tutoriales, hemos explorado la obtención de tweets desde la API de búsqueda de Twitter. Puedes utilizar los mismos patrones de diseño para obtener datos de otros recursos web. Si tus aplicaciones van a involucrar procesos de recuperación de datos continuos y más complejos, es posible que desees buscar proveedores de contenido y, opcionalmente, servicios para maximizar la eficiencia. Al igual que con cualquier operación externa basada en datos, no puedes asumir que la recuperación de la información que necesitas será exitosa, por lo que un nivel de manejo de errores es esencial. La capacidad de acceder a datos de Internet es, por supuesto, uno de los beneficios clave de la informática móvil, por lo que estas técnicas son un componente vital en la caja de herramientas de cualquier desarrollador de Android.



