Programación Android, AsyncTask – Conectar a Internet y leer documentos JSON

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0

json

Esta semana vamos a escribir un ejemplo parecido al de la semana pasada, en el que conectábamos a una URL para recoger un documento XML y analizarlo, utilizando para eso un AsyncTask. En esta ocasión vamos a hacer lo mismo, pero con un documento JSON.
En primer lugar debemos conocer la estructura del documento previamente, para poder analizarlo después desde el código. La URL que vamos a utilizar en esta ocasión es la api de flickr, que nos permite ver las fotos publicadas en un documento JSON: http://api.flickr.com/services/feeds/photos_public.gne?format=json.

Como se puede ver en el documento, hay una etiqueta “items”, que es un array que contiene una serie de documentos JSON a su vez. Cada uno de estos documentos del array son los que contienen los datos de las fotos publicadas.

Crear la Interfaz

Para empezar nuestro ejemplo, como siempre, primero mostramos la interfaz. Vamos a volver a mostrar un ListView, pero en esta ocasión utilizará un Adapter por defecto de Android. En el ListView mostraremos la URL de la foto que se haya publicado, y cuando pulsemos un elemento del ListView abriremos el navegador para mostrar dicha foto:

Layout
Layout

El código XML es muy sencillo:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".ExampleJSON" >

    <ListView
        android:id="@+id/lvFotos"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" />

</RelativeLayout>

Codificar la Activity

Una vez que tenemos la interfaz preparada, vamos a codificar nuestra aplicación en Java. En la Activity vamos a crear la instancia de nuestro ListView y del Adapter que vamos a utilizar en el ListView. El Adapter lo inicializaremos sin datos, después vamos a recoger los datos de Internet, a través de un AsyncTask, y por último vamos a añadir estos datos al Adapter para que se muestren en el ListView:

/**
 * Activity que muestra un ListView con enlaces a imágenes y abre el navegador
 * para mostrar la imagen seleccionada del ListView.
 *
 * @author Proyecto Simio proyectosimio@gmail.com
 *
 */
public class MainActivity extends Activity {
	private ListView lvPhotos;
	private ArrayAdapter lvAdapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		lvPhotos = (ListView) findViewById(R.id.lvFotos);

		// Creamos un Adapter estándar de Android.
		lvAdapter = new ArrayAdapter(this,
				android.R.layout.simple_list_item_1, new ArrayList());

		// Asignamos el Adapter al ListView.
		lvPhotos.setAdapter(lvAdapter);

		// Actualizamos los datos, pasamos el Context para poder mostrar un
		// ProgressDialog durante la carga de datos y el Adapter para
		// actualizarlo.
		((JSONApplication) getApplication()).getData(this, lvAdapter);
	}

}

Crear la clase Application

El siguiente paso es crear nuestra clase Application y el método que va a actualizar los datos del Adapter:

/**
 * Clase Application personalizada.
 *
 * @author Proyecto Simio proyectosimio@gmail.com
 *
 */
public class JSONApplication extends Application {
	// URL que contiene el documento JSON.
	private final static String URL = "http://api.flickr.com/services/feeds/photos_public.gne?format=json";

	public void getData(Context context, ArrayAdapter adapter) {
		// Actualizamos los datos del Adapter a través de un AsyncTask.
		new AsyncConector(context, adapter, URL).execute();
	}

}

Crear el AsyncTask

A continuación creamos nuestro AsyncTask, que será el encargado de realizar la conexión a Internet y añadir los datos del documento JSON al Adapter. En el código vemos que se han creado dos clases nuevas, una que realiza la conexión a Internet para recoger el documento JSON y otra que analiza este documento para devolver la colección que necesitamos:

/**
 * AsyncTask que conecta a la URL que contiene el documento JSON y añade los
 * datos al Adapter.
 *
 * @author Proyecto Simio proyectosimio@gmail.com
 *
 */
public class AsyncConector extends AsyncTask<Void, Void, Void> {
	private ArrayList<String> data;
	private ArrayAdapter adapter;
	private String url;
	private ProgressDialog pd;
	private Context context;

	public AsyncConector(Context context, ArrayAdapter adapter,
			String url) {
		this.adapter = adapter;
		this.url = url;
		pd = new ProgressDialog(context);
		this.context = context;
	}

	@Override
	protected void onPreExecute() {
		// Configuramos el ProgressDialog para mostrar mensaje de que se está
		// cargando el contenido.
		pd.setIndeterminate(true);
		pd.setMessage(context.getResources().getString(
				R.string.MessageProgressDialog));
		pd.setTitle(R.string.TitleProgressDialog);
		// Mostramos el ProgressDialog.
		pd.show();
		super.onPreExecute();
	}

	@Override
	protected Void doInBackground(Void... params) {
		// Creamos un objeto de la clase que se encargará de conectar a la URL y
		// analizar su contenido. Esta clase la creamos en el siguiente paso.
		ConectorHttpJSON conector = new ConectorHttpJSON(url);

		try {
			// Recogemos el documento JSON de Internet.
			JSONObject obj = conector.execute();

			// Analizamos el documento JSON y recogemos todos los links a las
			// fotos.
			data = new JSONToStringCollection(obj).getArrayList();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	@Override
	protected void onPostExecute(Void result) {
		// Añadimos todos los links al Adapter.
		for (String tmp : data)
			adapter.add(tmp);

		// Indicamos al Adapter que ha cambiado su contenido, para que actualice
		// a su vez los datos mostrados en el ListView.
		adapter.notifyDataSetChanged();

		// Eliminamos el ProgressDialog.
		pd.dismiss();
		super.onPostExecute(result);
	}
}

Crear el conector HTTP para recoger el documento JSON

El siguiente paso es crear la clase que realiza la conexión a Internet y analiza el documento JSON para devolver la colección de String que contendrá las URL con todas las fotos. Un dato a tener en cuenta es que la URL que vamos a analizar no devuelve un objeto puro JSON, sino que tiene un comienzo “jsonFlickrFeed(“ y un final “)” que hacen que si tratamos de convertir la cadena entera a documento JSON nos dará una excepción por formato incorrecto. Por lo tanto antes de crear el objeto JSON en código vamos a eliminar el comienzo y final de la cadena:

/**
 * Conecta a la URL para recoger el documento JSON y extrae los enlaces a las
 * fotos publicadas en flickr.
 *
 * @author Proyecto Simio proyectosimio@gmail.com
 *
 */
public class ConectorHttpJSON {
	private String url;

	public ConectorHttpJSON(String url) {
		this.url = url;
	}

	public JSONObject execute() throws ClientProtocolException, IOException,
			IllegalStateException, JSONException {
		/* Creamos el objeto cliente que realiza la petición al servidor */
		HttpClient cliente = new DefaultHttpClient();
		/* Definimos la ruta al servidor. */
		HttpPost post = new HttpPost(url);

		/* Ejecuto la petición, y guardo la respuesta */
		HttpResponse respuesta = cliente.execute(post);

		// Recogemos el String que devuelve el servidor.
		String flickrFeed = inputStreamToString(respuesta.getEntity()
				.getContent());

		// Hacemos un split para eliminar el comienzo de la cadena de texto,
		// para poder extraer únicamente el documento JSON. Aún contendrá un
		// paréntesis al final de la cadena.
		String jsonWithWrongEnd = flickrFeed.split("jsonFlickrFeed\\(")[1];

		// Recogemos el documento JSON a partir de jsonWithWrongEnd, eliminando
		// el último caracter.
		JSONObject object = new JSONObject(jsonWithWrongEnd.substring(0,
				jsonWithWrongEnd.length() - 1));

		return object;
	}

	private String inputStreamToString(InputStream is)
			throws UnsupportedEncodingException {
		String line = "";
		StringBuilder sb = new StringBuilder();
		// Guardamos la dirección en un buffer de lectura
		BufferedReader br = new BufferedReader(new InputStreamReader(is,
				"utf-8"), 8);

		// Y la leemos toda hasta el final
		try {
			while ((line = br.readLine()) != null) {
				sb.append(line.trim());
			}
		} catch (Exception ex) {
			Log.w("Aviso", ex.toString());
		}

		// Devolvemos todo lo leido
		return sb.toString();
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}
}

Convertir el documento JSON en la colección de datos

Por último necesitamos crear la clase que va a analizar el documento JSON y va a extraer el ArrayList que necesitamos para añadir los datos al Adapter:

/**
 * Analiza un documento JSON para devolver la colección de datos con los enlaces
 * a las fotos.
 *
 * @author Proyecto Simio proyectosimio@gmail.com
 *
 */
public class JSONToStringCollection {
	JSONObject object;

	public JSONToStringCollection(JSONObject object) {
		this.object = object;
	}

	/**
	 * Analiza el objeto JSON y extrae la colección de datos.
	 *
	 * @return ArrayList
	 * @throws JSONException
	 */
	public ArrayList<String> getArrayList() throws JSONException {
		ArrayList<String> data = new ArrayList<String>();

		if (!object.equals(new JSONObject())) {
			// Del documento JSON extraemos el array "items", que contiene una
			// colección de publicaciones.
			JSONArray array = object.getJSONArray("items");

			// Recorremos el array para analizar todos los documentos que
			// contiene.
			for (int i = 0; i < array.length(); i++) {
				JSONObject obj = array.getJSONObject(i);

				// Si miramos el documento JSON vemos que la URL de la foto es
				// el valor de la clave "m", que a su vez se encuentra dentro de
				// un documento JSON, cuya clave es "media". Por lo tanto
				// extraemos el documento que se encuentra dentro de "media", y
				// posteriormente el valor de "m".
				data.add(obj.getJSONObject("media").getString("m"));
			}
		}

		return data;
	}
}

Implementar OnItemClickListener y mostrar la imagen en el navegador

Y con este paso ya podemos ejecutar la aplicación y vemos que se llena el ListView con las URLs de las fotos subidas a flickr. El siguiente, y último paso, será abrir el navegador para mostrar la imagen que seleccionemos del ListView. Para ello en la Activity implementamos el Listener OnItemClickListener, para añadirselo al ListView. Cambiamos la firma de la Activity a:

public class MainActivity extends Activity implements OnItemClickListener

Añadimos el Listener al ListView después de haberlo inicializado:

lvPhotos.setOnItemClickListener(this);

Y por último implementamos en la Activity el método que nos obliga el Listener:

@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
	// Recuperamos el link de la publicación seleccionada.
	String link = lvAdapter.getItem(position);

	// Creamos un intent implicito para que el sistema escoja la aplicación
	// que debe utilizar.
	Intent i = new Intent(Intent.ACTION_VIEW);

	// Le añadimos la url que debe mostrar.
	i.setData(Uri.parse(link));

	// Lanzamos la nueva Activity.
	startActivity(i);

}

Una vez implementado el Listener, ya podremos visualizar la imagen cuando pulsemos uno de los elementos del ListView.

*Nota: No olvidemos dar los permisos de Internet y configurar el nombre de nuestra clase Application en el manifiest.xml de nuestro proyecto.

Descargas

Puedes ver el resultado del ejemplo en tu móvil descargando nuestra aplicación desde Google Play.

Puede descargarse el proyecto completo aquí.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

64 thoughts on “Programación Android, AsyncTask – Conectar a Internet y leer documentos JSON

  1. Hola:
    He probado a ejecutar tu código, pero me sale el siguiente error:

    json/com.abelardolg.holainternetxmljson.MainActivity}: java.lang.ClassCastException: android.app.Application cannot be cast to com.abelardolg.holainternetxmljson.JSONApplication

    Este error hace referencia a esta línea:
    // Actualizamos los datos, pasamos el Context para poder mostrar un
    // ProgressDialog durante la carga de datos y el Adapter para
    // actualizarlo.
    ((JSONApplication) getApplication()).getData(this, lvAdapter);

    No sé a qué es debido, pero sospecho que puede estar motivado por alguna actualización de las clases implicadas que Google habrá hecho desde que publicaste este ejemplo hasta hoy.

    Muy buena la página. Ya te sigo.

    Y si respondes, muchas gracias por la solución.

  2. cuando se crea el objeto httpclient me sale error que debo importar en el dependencias de android studio? es que ya se debió actualizar para la fecha y no encuentro cual es el actual para utilizar httpclient.

  3. quiero comentarte que estoy aprendiendo a programar en android y necesito una mano con un proyecto que me paso un amigo de una aplicacion que se trae los contenidos de un portal de wordpress, al parecer se ha programado un plugin para la administracion de los contenidos que trae la aplicacion del portal, lo cual no esta incluido en el proyecto, si me indicas un mail te envio la info

  4. Hola.. nuevamente aquí con dudas, tu disculparas … espero me puedas ayudar de favor

    Tú clase AsyncConector llena un adapter por medio del objeto data, posterior actualiza los datos mostrados en el Listview … adapter.notifyDataSetChanged();

    En mi caso y como lo mencioné en los comentarios anteriores, yo utilizaré la clase AsyncConector como genérica para TODA mi aplicación, es decir, esta clase me ayudará a parsear todos mis objetos JSON y no necesariamente los datos contenidos se desplegarán en un Listview o cualquier otro objeto, algunos JSON por ejemplo, los datos serán usados sólo para realizar o no realizar ciertas acciones, dicho lo anterior, quiero entender que después de ser ejecutada la línea de código
    ((BDApplication) getApplication()).getData(this, lvAdapter, url, params, tag);
    el objeto lvAdapter ya contiene los datos correspondientes?

    Por último, el for para llenar el adapter
    for (String tmp : data)
    adapter.add(tmp);

    Mi objeto data está definido como ..
    private ArrayList<HashMap> data;

    En la línea del for me marca error …”cannot convert from element type HashMap to String cómo puedo solucionar lo anterior?

    Gracias por tú atención.

    1. Para hacer eso tendrías que crearte un constructor para cada tipo de adaptador. Una cosa es poder analizar los datos y otra es utilizar mil objetos externos.

      Lo mejor que puedes hacer es crearte tu propio listener y lanzarlo cuando la tarea haya terminado, pasándole así (con el listener) un string con el documento JSON para analizarlo desde donde tengas el adapter.

      1. OKS y esto es mejor practica? ó mejor en cada clase donde requiero de un parseo de JSON escribo los métodos doInBackground, onPostExecute etc etc?

  5. Hola

    Una pregunta, para mi caso, en la llamada del Web Service requiero enviar parámetros, estos no pueden ir en la URL, para el ejemplo que nos muestras, dónde tendría que hacer los cambios?

    Gracias.

    1. Ya hice las modificaciones necesarias y ya puedo llamar al WebService enviando parámetros(Post).

      El JSON que regreso es el siguiente..
      {“Datos”:[{“suc_nombre”:”Cuautitlán”,”cli_nombre”:”Futcho 7″}],”status”:0,”mensaje”:””}

      Pero tengo un error en la Clase AsyncConector en el método doInBackround

      // Recogemos el documento JSON de Internet.
      JSONObject obj = conector.execute(); /// Hasta aquí obtengo los datos correctos

      Pero en la siguiente línea me da error

      data = new JSONToStringCollection(obj).getArrayList();

      Alguna idea?

      Saludos & Gracias

      1. Tendrías que analizar la respuesta que da el servidor (recibida en el cliente) para comprobar que sea un documento JSON bien formado y con la estructura que necesitas. Después de eso, si todo está bien, revisa que extraigas de forma correcta la información en el método getArrayList.

        Un saludo.

        1. Hola

          La cadena que me da el servidor, si yo uso un visor en línea para JSON(http://jsonviewer.stack.hu/) se comprueba que se encuentra bien formado …. pero como te comenté la línea de código siguiente envía un Exception

          data = new JSONToStringCollection(obj).getArrayList();

          11-10 11:44:33.507: W/System.err(5273): org.json.JSONException: No value for items
          11-10 11:44:33.537: W/System.err(5273): at org.json.JSONObject.get(JSONObject.java:354)
          11-10 11:44:33.537: W/System.err(5273): at org.json.JSONObject.getJSONArray(JSONObject.java:544)
          11-10 11:44:33.537: W/System.err(5273): at com.example.utils.JSONToStringCollection.getArrayList(JSONToStringCollection.java:34)
          11-10 11:44:33.547: W/System.err(5273): at com.example.utils.AsyncConector.doInBackground(AsyncConector.java:74)
          11-10 11:44:33.567: W/System.err(5273): at com.example.utils.AsyncConector.doInBackground(AsyncConector.java:1)
          11-10 11:44:33.567: W/System.err(5273): at android.os.AsyncTask$2.call(AsyncTask.java:185)
          11-10 11:44:33.567: W/System.err(5273): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
          11-10 11:44:33.577: W/System.err(5273): at java.util.concurrent.FutureTask.run(FutureTask.java:138)
          11-10 11:44:33.587: W/System.err(5273): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
          11-10 11:44:33.587: W/System.err(5273): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)

          Según puedo endender del mensaje de error, que no existen valores, pero si yo escribo la sig. línea de código, puedo comprobar que si hay información..

          ljsonArray = obj.getJSONArray(tag);

          el cual obtengo lo sig ….

          [{“suc_nombre”:”Cuautitlán”,”cli_nombre”:”Futcho 7″}]

          Que estoy haciendo mal?

          Saludos

          1. Ten en cuenta que la clase JSONToStringCollection la hemos creado para analizar un documento json, y en el ejemplo sigo una estructura concreta. Al cambiar el documento json (claves o estructura) ya no puedes usar mi código para analizarlo. Seguramente el error estará ahí.

            Un saludo.

        2. Otra pregunta si me puedes ayudar de favor ….

          En mi aplicación, tengo que bajar diferentes datos en JSON dependiendo de la opción del menú elegida, puedo tener una sola clase AsyncConector que me funcione para TODAS mis opciones? o tendría que tener varias clases y cada una con sus respectiva clase para realizar la conexión?

          Gracias & Saludos.

          1. Para eso tendrías que decirle al asynctask las diferentes opciones que pueda tener, por ejemplo pasándole un entero al iniciarlo y a través de un switch podrás usar un método u otro para que te parseen los diferentes documentos de forma correcta.

            Un saludo.

  6. como puedo acceder a estos compos:?
    “title”: “DSC_0144.jpg”,
    “link”: “http://www.flickr.com/photos/41675204@N00/15392998649/”,
    “media”: {“m”:”http://farm4.staticflickr.com/3938/15392998649_b009d89149_m.jpg”},
    “date_taken”: “2010-10-16T09:48:58-08:00″,

    en esta parte del codigo
    data.add(obj.getJSONObject(“media”).getString(“m”));

    es decir si en la patalla no quiero mostrar la URL sino la imagen o el link o date_taken? como haria ?
    que modificaria ?
    otra pregunta : si no quiero abrir el navegador para mostrar la imagen si no que me muestre un alert, otra activite , otra cosa ps que contenga lo siguente como seria :
    “title”: “DSC_0144.jpg”,
    “link”: “http://www.flickr.com/photos/41675204@N00/15392998649/”,
    “media”: {“m”:”http://farm4.staticflickr.com/3938/15392998649_b009d89149_m.jpg”},
    “date_taken”: “2010-10-16T09:48:58-08:00

    espero tu ayuda ;)

    1. Para recoger un String tendrías que hacer jsonObject.getString(“key_del_campo”), cambiando “jsonObject” por el nombre de tu variable y “key_del_campo” por el nombre del campo del que quieres extraer la información, por ejemplo link, title o media.

      Si lo que quieres es mostrar la información en otra activity o en un alertdialog simplemente tienes que extraer la información del json y mostrarla donde lo desees.

      Para mostrar la imagen tendrías que descargarla y posteriormente visualizarla en un imageview, en Internet tienes muchos ejemplos de cómo hacerlo, y librerías que facilitan esta tarea.

      Un saludo.

  7. hola en un servidor tengo la siguiente informacion = id imagen, nombre, fecha nacimiento, imagen etc. lo que busco es leer la estructura de eso y primero poder descdargar las imagenes en un listView y cuando selecciones una imagen se vea : la imagen y la informacion que esta en el servdor, necesito su ayuda x favor me den una idea

    1. Cierto, en el código escrito en el tutorial se me pasó poner una segunda barra, la línea correcta sería jsonWithWrongEnd = flickrFeed.split(“jsonFlickrFeed\\(“)[1];

      Gracias por el aviso, lo corrijo ahora mismo :)

      Un saludo.

  8. Hola:

    Tengo un problema que no he podido solucionar:

    Al recibir los datos de un JSON, lo que hago es lo siguiente, el usuario introduce una descripcion de un producto que se le manda a un json como criterio de búsqueda, ese JSON me regresa una lista de elementos que cumple con tal criterio,lo cual está haciendo correctamente

    Después elijo uno de esos elementos con un clic, los datos están llegando bien al servidor y veo que el JSON si trae datos pues los imprimí con System.out… en la consola y si trae algunos datos vacíos pero no nulos, me muestra en consola por ejemplo: dato= , descripcion=nuevo producto, precio= … cada uno de estos datos los traigo como String aunque algunos en realidad son números, pero me dice que un dato de Fraccion es null

    qué estoy haciendo mal en este código?

  9. Hola a todos,

    Soy nueva en esto y sinceramente empiezo a estar un poco “desesperada” porque no se que otra palabra utilizar, me encuentro haciendo una aplicacion y me encontre con esto de los JSON y el web service que me facilitan no tiene los dichoros arrays, es un objeto dentro de otro y aunque os parezca mentira no encuentro la manera de leerlo, me podeis ayudar?
    Se que os parecera una chorrada pero es asi, unos sabe y otros no sabemos, por eso pido ayuda, necesito hacer el get y el post.
    Creeis que me podeis ayudar?

    Gracias y un saludo.

    1. Tendrías que leerlo como un nuevo documento json.

      Pero como le dije a otro compañero, lo mejor es utilizar un parseador que haga el trabajo automáticamente, que recoja el documento json y o convierta en objetos.

      Puedes buscar algún tutorial de Gson por ejemplo.

      Un saludo.

      1. Eva, si publicas un ejemplo de la respuesta JSON probablemente haya quien pueda publicarte un ejemplo de como “parsearlo”, si es algo complicado al inicio, pero finalmente es de lo mas simple, al tratarse unicamente de “arreglos” de “arreglos”

  10. Hola IAVilaE, buenas tardes, en tu experiencia, que debo usar si requiero estar ejecutando el Asynctask cada 5 o 10 segundos para mantener actualizada la listview tomando cada tanto le info del webservice?? x ahora le puse un boton “refrescar” arriba de la listview y con eso se ejecuta nuevamente, pero me gustaria meterle un “timer” o algo similar para evitar que el usuario tenga que estar clickeando.

    1. Puedes utilizar una instancia de la clase CountDownTimer dentro del propio AsyncTask para que haga la conexión y al finalizar se vuelva a iniciar el CountDownTimer.

      Aunque lo ideal sería que implementaras el GMC en tu aplicación para que sea el servidor el que mande notificación a la aplicación cuando tenga que refrescarse.

  11. Buen día:

    Utilicé tu ejemplo solito y corre perfectamente, pero a la hora de implementarlo en mi proyecto me marca un ClassCastException al mandar llamar la clase JsonApplication.

    En el Activity cree:

    public void buscarAutorizacionJson(){
    lvAdapter = new ArrayAdapter(this,
    android.R.layout.simple_list_item_1, new ArrayList());
    // Actualizamos los datos, pasamos el Context para poder mostrar un
    // ProgressDialog durante la carga de datos y el Adapter para
    // actualizarlo.
    ((JsonApplication) getApplication()).getData(this, lvAdapter);

    }

    Este método lo mando llamar en un onClick(), ya que la búsqueda en el JSON debe hacerse hasta este momento. La clase JsonApplication quedaría:

    public class JsonApplication extends Application{
    // URL que contiene el documento JSON.
    private final static String URL = “http://www.ligaalgo.com/subliga/nombres.json”;

    public void getData(Context context, ArrayAdapter adapter) {
    // Actualizamos los datos del Adapter a través de un AsyncTask.
    new AsyncConector(context, adapter, URL).execute();
    }
    }

    AsyncConector la estoy utilizando tal cual la tienes tu, pero en ConectorHttpJSON si hice cambios:

    le comenté esto
    //String jsonWithWrongEnd = cadenaServ.split(“jsonFlickrFeed\\(“)[1];
    y el del último caracter, ya que mi JSON es diferente

    y de JSONToStringCollection. modifique lo siguiente:

    public ArrayList getArrayList() throws JSONException {
    ArrayList data = new ArrayList();

    if (!object.equals(new JSONObject())) {
    // Del documento JSON extraemos el array “noms”, que contiene una
    // colección
    JSONArray array = object.getJSONArray(“noms”);

    // Recorremos el array para analizar todos los documentos que
    // contiene.
    for (int i = 0; i < array.length(); i++) {
    JSONObject obj = array.getJSONObject(i);

    data.add(obj.getString("rfc")));
    }
    }

    return data;
    }

    Ya no lo estoy pasando a un activity porque sólo requiero validar que el usuario exista en el JSON para continuar con otro procedimiento

    El JSON es
    {
    "noms":
    [

    {"rfc":"EEFA790312",
    "descripcion":"Juan Garcia"},

    {"rfc":"0CFE910218",
    "descripcion":"Alfonso Lopez"}
    ]
    }

    Tienen alguna idea?

  12. Como podría leer json arrays que estan dentro de otros arrays ? por ejemplo asi, esta mal validado el json nadamas lo puse aqui para dar la idea de que es lo que quiero leer, por ejemplo quiero sacar lo que hay en producto eso ya esta entonces ya leo el siguiente arreglo que pertenece al json object principal eso ya esta entonces como hago para leer los siguientes osese el que dice, contenido, y despues leer el de cursos, espero y porfavor me pudieras ayudar muchas gracias por tus tutoriales son muy buenos

    {
    “producto”: {
    “nombre”: “miJson”,
    “configuracion”: [
    {
    “nivel”: “uno”,
    “contenido”: [
    {
    “grado”: “uno”,
    “cursos”: [
    {
    “curso”: “miCurso”,
    “c_corto”: “nombreCorto”
    }
    ]
    }
    }

    1. Para estructuras complejas te recomiendo que aprendas a utilizar la librería GSON, con la que sólo tienes que crear una estructura de clases que imite la estructura del JSON y después el propio GSON rellenará los datos de tu objeto de forma automática, sin tener que leer y analizar el documento completo tú mismo ;)

      Un saludo.

      1. Lo que pasa es que el json lo tengo que leer ya que estara en un servidor estoy utilizando adapters y array list, lo necesito leer para mandar cierto contenido a un activity, no se acomodo bien el json pero la estructura es un json object que contiene un json array y ese json array contiene otro, entonces lo que quiero es ir leyendo en escalón cada uno de los json para mostrarlo en un activity, no se si te puedo dejar mi correo de gmail o para consultarte via chat de hangouts espero tu respuesta muchas gracias

        1. El GSON es una librería que se utiliza en Java, y se utiliza para extraer los datos del documento JSON a un objeto en Java. Para ello tienes que utilizar en tu objeto en Java la misma estructura que tiene el documento JSON, de manera que si en el JSON tienes un array, tendrás que declarar un array en tu clase, si el JSON tiene un entero tendrás que declarar un int en tu clase, etc.

          Te recomiendo que busques algún tutorial para poder usarlo, te va a facilitar mucho la tarea de parsear el documento JSON ;)

  13. Hola Igor, buenas tardes…

    Primero felicitarte por tu blog, muy buen e interesante.

    Segundo es realizarte una consulta…

    En la clase MainActivity linea 29, haces un Cast desde la activity

    ((JSONApplication) getApplication()).getData(this, lvAdapter);

    yo estoy usando un Fragment, y estoy Usando algo asi:

    ((JSONApplication) getActivity().getApplicationContext()).getData(getActivity(), zmAdapter);

    tambbien he probado asi:

    ((JSONApplication) getActivity().getApplication()).getData(getActivity(), zmAdapter);

    Pero nada ha funcionado. tienes idea de que debo hacer para hacer el Cast sin problemas?

    Muchas gracias.

    Saludos

      1. Gracias IAvilaE, quiero agregar algo más, en la clase ConectorHttpJSON la linea 20 usas HttpPost, mi api, nuncaa funcionaba, despues de hacer un debug, me di cuenta que el problema era esa linea, cambie a HttpGet y pude conectarme a mi API.

        Muchas gracias por la ayuda.

        Saludos

        1. Supongo que la diferencia será cómo da la respuesta la API y por eso has necesitado cambiar de método de POST a GET. Me alegro de que pudieras solucionarlo fácilmente :)

          Un saludo.

    1. Es un layout estándar que tiene definido el propio sdk, al igual que otros muchos recursos como por ejemplo iconos, por lo tanto, para este ejemplo, no necesitamos crearlo nosotros :)

      Un saludo.

  14. Hola, tambien me ayudo mucho tu tutorial. Sabras como hacer lo siguiente: quiero poblar en 2 diferentes activitys listviews diferentes.
    Lisview1 con una lista sencilla x lo que no requiere un adapter customizado, sino el arrayAdapter generico.
    Listview2 con varias columnas x lo que hago un adapter customizado.
    Pero no quisiera crear 2 veces las clases de JsonAplication y AsyncConector que ambas reciben el parametros “arrayAdapter”, sino que pudiera meterlo como un “objeto generico” u otra forma de manera de no tener que estar creando cada vez las mismas clases con diferentes nombres

    1. Puedes trabajar con eventos, creas un evento onFinish en tu asynctask y cuando termina la carga se lanza en evento, lo recoges en la activity y cargas los datos al adapter del listview, así tu asynctask no tiene ninguna relación con el adapter.

      Un saludo.

      1. Gracias por tu amable respuesta. Busque ejemplos de implementar un “onFinish” en el AsyncTask pero lo mas que halle fue esto:
        https://code.google.com/p/spotify-watcher/source/browse/src/com/krakelin/SpotifyWatcher/ContentMatcher.java?spec=svn7302b8d4e886741b8da8730429d943a656df314a&name=aa23d065b0&r=7302b8d4e886741b8da8730429d943a656df314a

        Trate de implementarlo pero me marca error en la clase q extiende AsyncTask en la linea:
        getOnFinish().onFinish((result));

        “el metodo ContentEventHandler onFinish(string) no es aplicable para el argumento, si le pongo “void” tambien manda error.

        Seguire buscando, si tienes algun link donde me pueda basar te lo agradecere.

        Saludos
        Juan Arturo

        1. Te comento mi forma de hacerlo (los nombres de clases y métodos orientativos para que se entienda el ejemplo):
          1º Crear un nuevo interface (en su propio archivo.java) y añadir métodos abstractos.
          2º Instanciar un objeto del interface en el AsyncTask.
          3º Crear un método setOnEventListener(interface listener) en el AsyncTask con el que se inicializa el objeto interface.
          4º En el punto en el que quieres lanzar el evento controlas que el listener no sea null, en cuyo caso lanzas evento:

          if(listener != null)
             listener.metodo(arg1, arg2, ...);
          

          5º En la activity en la que quieras recoger el evento implementas a la clase el interface (implements interface) y escribes los métodos que te obliga al implementarlo, al igual que con cualquier otro listener, aquí recoges los cambios que necesites realizar.
          6º Le asignas el listener al objeto asynctask (asynctask.setOnEventListener(this)).
          7º Ejecuta el asynctask. Cuando llegue al punto en que tiene que lanzar el evento, lo lanzará.

  15. Hay algún ejemplo para poblar un Navigation Drawer con json? Termino con errores de runtime cuando quiero generar el listado de esa manera.

        1. Vaya, qué casualidad xDD

          Pues en teoría el funcionamiento es el mismo, tendrías que analizar el JSON para extraer la estructura y después pasársela al Adapter, o bien sacarla directamente en el Adapter. Si estás leyendo bien el documento y montas bien las vistas en el Adapter no debería darte muchos problemas.

          Un saludo.

          1. Bueno, no se que parte del código poner por aquí sin que abulte una burrada, he subido a mega el proyecto en el que estaba trabajando así como el json con el que estaba intentando poblar el listview.

            Lo que estaba intentando realizar es un menú lateral con navigation drawer que se rellene con datos json, a lo mejor le sirve a alguien también:

            https://mega.co.nz/#F!k1pHCJaZ!ZuUbJen5k1HzpwM2Dgoc9g

  16. Hola, esta muy bueno tu tutorial. he tenido un problema y quizas tu me puedas ayudar.
    tengo una fucion synctask la cual me conecta a internet y me ayuda a obtener algunos datos los cuales con json guardo en una variable, el problema viene cuando quiero hacer uso de estos datos en un activity es como si la variable estuviera vacia. que podria hacer?

    1. Pues podría ser por muchos motivos, quizás el JSON no se está formando de forma correcta en el servidor, o no le llega bien a la aplicación, o en el código de la aplicación no lo recoges bien, o no se lee bien…

      Tendrás que depurar justo el momento en el que capturas el documento JSON en la aplicación y comprobar si lo recoge bien, si no es así ya sabes que el fallo está en cómo lo recoges del servidor o en cómo lo genera y envía el servidor.

      Un saludo.

    1. Buenas Luis.

      No tendrías más que crearlo y que fuera ese el objeto que pasaras al método getData() de la clase Application, en lugar del ArrayAdapter que se pasa en el ejemplo, y lo mismo al AsyncTask.

      La lógica de la aplicación sería la misma, salvo el cambio del adapter que estás utilizando.

      Un saludo.

Deja un comentario