Il miglior modo per effettuare richieste HTTP (ma non solo) in Android è quello di sfruttare thread appositi per evitare di appesantire ulteriormente l'ambiente grafico e limitare l'usabilità dell'applicazione.
Per realizzare richieste sfruttando l'asincronia esposta dalla classe AsyncTask, dobbiamo innanzitutto definire una interfaccia che ci permetterà di scatenare un listener ad hoc nel momento in cui il client riceve la response HTTP. Per la sua realizzazione ci affidiamo ai generics che ci permettono di astrarci completamente dalla tipologia di informazione trattata.
public interface RemoteCallListener<T> { public void onRemoteCallComplete(T data); }
Il metodo onRemoteCallComplete verrà invocato automaticamente dal task una volta eseguito il suo compito (ovvero quello di effettaure una chiamata HTTP).
Analizziamo ora nel dettaglio la classe AsyncTask. Supponiamo di dover scaricare per esempio un file json contenente una lista di oggetti di tipo Persona.
Innanzitutto è necessario definire le nostre entità tramite la classe Persona:
public class Persona { private String nome; private String cognome; private int eta; //getter & setter }
Estendiamo ora la classe AsyncTask sfruttando ulteriormente i generics comunicando alla superclasse la tipologia di dato che abbiamo intenzione di trattare:
public class FetchPersonaListTask extends AsyncTask<String, Void, List<Persona>> { private final static String TAG = FetchPersonaListTask.getClass().getName(); RemoteCallListener<List<Persona>> listener; public FetchPersonaListTask(RemoteCallListener<List<Persona>> listener) { this.listener = listener; } @Override protected List<Persona> doInBackground(String... urls) { List<Persona> personaList = new ArrayList<Persona>(); String url = urls[0]; HttpClient httpClient = new DefaultHttpClient(); HttpGet get = new HttpGet(url); try { HttpResponse response = httpClient.execute(get); if(response.getStatusLine().getStatusCode() == 200) { String responseString = EntityUtils.toString(response.getEntity()); JSONArray rawPersonaList = new JSONArray(responseString); Persona persona; for(int i = 0; i<rawPersonaList.length(); i++) { //"traduco" da JsonArray a Persona personaList.add(persona); } } } catch (Exception e) { Log.d(TAG, e.getMessage()); } return personaList; } protected void onPostExecute(List<Persona> personaList) { listener.onRemoteCallComplete(personaList); } }
I dati comunicati alla superclasse AsyncTask sono in questo ordine:
Una volta comunicate queste informazioni è necessario implementare il metodo doInBackground che riceverà come parametri una lista di String (perchè comunicato nel primo parametro dei generics) e si occuperà di effettuare la chiamata HTTP e la conversione da oggetto json ad oggetto java e il metodo onPostExecute che riceverà come parametro una lista di Persona (perchè comunicato nel terzo parametro dei generics) e che si occuperà semplicemente di delegare l'attività ad un RemoteCallListener.
Una volta definite queste classi per sfruttare questo comportamento basterà far implementare alla nostra Activity l'interfaccia RemoteCallListener e invocare il metodo execute all'oggetto FetchPersonaListTask:
public class PersonaListActivity extends Activity implements RemoteCallListener<List<Persona>> { private FetchChannelListTask task; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String url = "..."; task = new FetchChannelListTask(this); task.execute(url); } @Override public void onRemoteCallComplete(List<Persona> personList) { Toast.makeText(getApplicationContext(), "Scaricate " + personList.size() + " persone" , Toast.LENGTH_SHORT).show(); } }
Chiaro, semplice e ben organizzato :)