Cómo enviar datos a un servidor en Android (I) (Actualizado)

Buenas, hace un tiempo hicimos una serie dedicada a descargar información de un servidor y parsear esa info con un lector de JSON. Esta vez vamos a dedicar unas semanas a hacer lo contrario, crear unos formularios en nuestra aplicación Android y enviarla a un servidor para que la guarde o la trate como queramos.




Básicamente serán tres pasos, primero crearemos una clase genérica en Android que nos permita enviar información a un servidor y obtener una respuesta. Luego crearemos en un servidor con Apache, PHP y MySQL un servicio que recoja una serie de datos y devuelva una respuesta y por último, volveremos a nuestra aplicación Android para crear un formulario y enviar los datos.

Empecemos. Para ello vamos a crear en nuestra aplicación Android una clase llamada HttpClientManager. Como dije antes, se dedicará a hacer una llamada a un servidor enviando datos, esperará por la respuesta y nos devolverá el resultado. Este será el aspecto que va a tener.


public class HttpClientManager {

    private Activity activity;
    private ArrayList<NameValuePair> nameValuePairs;
    private String responseBody = "";
 
    private ProgressDialog m_ProgressDialog = null; 
    private Runnable viewOrders;
   
    private String UrlService = "";
    
    private String strMessageHeadLoading = "Por favor espera";
    public String getStrMessageHeadLoading(){ return strMessageHeadLoading; }
    public void setStrMessageHeadLoading(String message){ strMessageHeadLoading = message; }
    
    private String strMessageBodyLoading  = "Enviando información...";
    public String getStrMessageBodyLoading(){ return strMessageBodyLoading; }
    public void setStrMessageBodyLoading(String message){ strMessageBodyLoading = message; }    
    
    private String getUrlService(){ return UrlService;}
    private void setUrlService(String UrlService){this.UrlService = UrlService;}
 
    public String getResponseBody(){return responseBody;} 
    private void setResponseBody(String ResponseBody){responseBody = ResponseBody;}
 
    public HttpClientManager(Activity activity){
 this.activity = activity;
 nameValuePairs = new ArrayList<NameValuePair>();
    }
 
    private boolean isInternetAllowed(Activity activity){
     ConnectivityManager cm = (ConnectivityManager) activity.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo ni = cm.getActiveNetworkInfo();
        if (ni!=null && ni.isAvailable() && ni.isConnected()) {
         return true;
        } else {
         return false; 
        }     
    }  
 
    public void addNameValue(String name, String value){
 nameValuePairs.add(new BasicNameValuePair(name,value));
    }
 
    public void executeHttpPost(String UrlService){
 setUrlService(UrlService);
  
 viewOrders = new Runnable(){         
  public void run() {
                  try {
   executeHttpPostAsync(activity, getUrlService());
         } catch (Exception e) {}
         }
     };
     Thread thread =  new Thread(null, viewOrders, "Background");
     thread.start();
     m_ProgressDialog = ProgressDialog.show(activity, getStrMessageHeadLoading(), getStrMessageBodyLoading(), true);
  
 }
 
     private void executeHttpPostAsync(Activity activity, String UrlService){
 if(isInternetAllowed(activity)){
    try{
  HttpClient httpclient = new DefaultHttpClient();
  HttpPost httppost = new HttpPost(UrlService);  
  httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
    
  ResponseHandler<String> responseHandler = new BasicResponseHandler();
  setResponseBody(httpclient.execute(httppost, responseHandler));
    
  activity.runOnUiThread(returnRes);
    }catch(HttpResponseException hre){
  ListenerExecuteHttpPostAsync.onErrorHttpPostAsyncListener("Se ha producido un error al conectar con el servidor");
    }catch(Exception e){         
      ListenerExecuteHttpPostAsync.onErrorHttpPostAsyncListener("Se ha producido un error");
    } 
 } else{
  ListenerExecuteHttpPostAsync.onErrorHttpPostAsyncListener("No es posible realizar la conexión. Comprueba tu conexión de datos.");
 }
    }
 
    private Runnable returnRes = new Runnable() {
 public void run() {
  m_ProgressDialog.dismiss();
  ListenerExecuteHttpPostAsync.onExecuteHttpPostAsyncListener(getResponseBody());
 }
    };
 
    public interface OnExecuteHttpPostAsyncListener{
 void onExecuteHttpPostAsyncListener(String ResponseBody);
 void onErrorHttpPostAsyncListener(String message);
    }
 
    private OnExecuteHttpPostAsyncListener ListenerExecuteHttpPostAsync;
 
    public void setOnExecuteHttpPostAsyncListener(OnExecuteHttpPostAsyncListener l){ListenerExecuteHttpPostAsync = l;}
 
}


Vamos a ver en detalle que es lo que hace esta clase. En la primera parte se definen variables privadas, algunas son el mensaje que se va a mostrar mientras esperamos que está predefinido, pero que se puede cambiar setStrMessageBodyLoading.

El constructor va a asignar a nuestra variable activity su valor e inicializa nameValuePairs. Este va a tener su importancia, pues aquí van a ir cada uno de los parámetros que enviaremos al servidor en un array de key-value.

El método isIntenetAllowed es muy importante, ya que si antes de intentar la conexión, verificará si nuestra conexión está lista y disponible. Tenéis que tener presente en todo momento que este es un proceso que no solo va a depender de vuestras aplicación, sino de muchos otros factores ajenos a ella y cualquier comprobación es poca.

El siguiente método addNameValue, simplemente irá añadiendo pares de clave-valor a nuestra variable nameValuesPairs. Los que ya tengáis experiencia en web, sabréis que al enviar un formulario, ya sea a través de POST o GET, podéis enviar parámetros, por ejemplo un email. Sería tan simple como definir en vuestro html un input con su propiedad name seteada a "email" y el usuario escribiría su email. Al hacer click en el botón de enviar, el navegador interpreta el formulario y el input y envía en una variable llamada email el valor del usuario. nameValuesPairs es algo muy parecido.

Esto está explicado de forma un poco informal, pero quiero que se entienda que es la forma de enviar a un servidor los parámetros que queramos desde nuestra aplicación Android.

El método publico que nos interesa viene ahora, executeHttpPost, y recibe como único parámetro la url que invocaremos. Este método en realidad es muy sencillo. Se encarga de abrir la pantalla de loading y lanza en segundo plano la llamada al servidor. Dado que este es un proceso asíncrono, debemos hacerlo así.

El método que realmente hace la petición al servidor es executeHttpPostAsync. Primero de todo va a verificar la conexión. Luego definimos un objeto HttpClient que se va a encargar de realizar la conexión con el servidor. El objeto HttpPost va a ser el que se encargue de enviar por POST los parámetros. Y ResponseHandler va a recoger el resultado y devolvérnoslo ya parseado en forma de String. Nosotros setearemos la variable responseBody.

Si os fijáis, el proceso es bastante sencillo, pero nosotros vamos a añadir un listener con diferentes llamadas en función de la rama por donde vaya el código.

Nuestro listener tiene dos métodos, uno cuando la conexión finaliza correctamente y ya tenemos lista la respuesta: onExecuteHttpPostAsyncListener, aquí enviamos la respuesta para que la clase que nos invoque decida que hacer con esta respuesta, onErrorHttpPostAsynListener nos indicará que se ha producido un error y nos devuelve un texto orientativo, se podría enviar la excepción, pero si esto se va a mostrar a un usuario lo veo innecesario.

Hasta aquí por esta semana. La semana que viene haremos la parte del servidor. Explicaremos como aceptar y tratar los datos que nos envían desde la aplicación y daremos una respuesta para que en la tercera semana de esta serie enviemos con esta clase una petición y la recojamos y tratemos.


ACTUALIZADO - 9 de Julio de 2012


Gracias a un contacto de uno de nuestros usuarios nos hemos dado cuenta que se nos ha pasado mencionar los permisos que hay que añadir en el archivo AndroidManifest.xml para que funcione. Serían dos en este caso el de acceso a Internet y el que nos permite verificar el estado de la red. Os los pongo a continuación:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Comments are closed.