viernes, 25 de marzo de 2016

Conexión a la red con HttpUrlConnection

Este artículo explicará de qué forma se puede enviar y recibir datos hacia un servidor desde una aplicación Android.

La mayoría de las aplicaciones de Android conectadas a la red utilizan HTTP para enviar y recibir datos. La plataforma Android incluye el cliente HttpURLConnection.


Para que las aplicaciones tengan acceso total a la conexión de tu dispositivo Android es necesario incluir los siguientes permisos en el archivo AndroidManifest.xml:

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

Comprobando la conexión de red
Antes de iniciar el cliente Http hay que comprobar si la conexión del dispositivo está habilitada, ya que puede ser posible que el Wi-fi no esté disponible o simplemente la conexión de red está fuera del rango. Para comprobar el estado de conexión usaremos los métodos getActiveNetworkInfo() e isConnected():

public boolean isConnected(){
  ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
  if (networkInfo != null && networkInfo.isConnected()) {
      return true;
  } else {
      return false;
  }
}

Se ha usado la clase ConnectivityManager para obtener las características actuales de la conexión. Esta información la guardamos en un elemento del tipo NetworkInfo con el método getActiveNetworkInfo(). Luego comprobamos si se retornó algún dato y si además el dispositivo está conectado con isConnected().

Un simple if (isConnected){..} nos permitirá realizar el resto de acciones una vez que hemos combrado que nuestro dispositivo está conectado a internet:

if (isConnected()){
 // Ejecuta las acciones...
 new DownloadWebpageTask().execute("http://canariasvista.hol.es/");
} else {
  Toast.makeText(getApplicationContext(),"Error en la conexión",Toast.LENGTH_SHORT).show();
}

Establecer peticiones HTTP en segundo plano usando AsyncTask

El tiempo que tarda la transmisión de datos en la red depende de muchos factores, como el tamaño de la información a intercambiar, los tiempos de latencia, la congestión del servidor o incluso la ejecución de múltiples tareas distintas en el sistema operativo. Cualquiera de estas situaciones puede prolongar el tiempo de una petición indefinidamente.

Para solucionarlo, las tareas asíncronas nos auxilian en estas situaciones. Solo debemos crear una nueva instancia de la clase AsyncTask e incluir la petición al servidor en el método doInBackground() y luego actualizar los resultados visuales en onPostExecute().

private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
  @Override
  protected String doInBackground(String... urls) {
      try {
          return downloadUrl(urls[0]);
      } catch (IOException e) {
          return "No se puede recuperar la página web. URL puede no ser válida";
      }
  }
  // onPostExecute displays the results of the AsyncTask.
  @Override
  protected void onPostExecute(String result) {
      textView.setText(result);
  }
}

La secuencia de acciones es la siguiente: 
  • Una vez hemos comprobado la conexión, la aplicación pasa el String URL a la subclase DownloadWebpageTask de AsyncTask 
  • Abrimos la conexión hacia la URL en doInBackground() 
  • Si es correcto, se llama al método downloadUrl que toma el parámetro String URL y lo usa para crear el objeto URL
  • A continuación se usa el objeto URL para establecer una HttpURLConnection. 
  • Una vez se ha establecido la conexión, el objeto HttpUrlConnection obtiene el contenido de la página web como un InputStream
  • Pasamos el InputStream al método readIt() que convierte el stream en String. 
  • Por último, el método onPostExecute() de AsyncTask muestra el string en la Activity.

Conecta y descarga la página web

private String downloadUrl(String myurl) throws IOException {
  InputStream is = null;

  try {
      URL url = new URL(myurl);
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
      conn.setReadTimeout(10000 /* milisegundos */);
      conn.setConnectTimeout(15000 /* milisegundos */);
      conn.setRequestMethod("GET");
      conn.setDoInput(true);
      // Inicia la consulta
      conn.connect();
      int response = conn.getResponseCode();
      Log.d(DEBUG_TAG, "La respuesta es: " + response);
      is = conn.getInputStream();
      //Para descargar la página web completa
      BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
      String webPage = "";
      String data = "";
      while ((data = reader.readLine()) != null) {
          webPage += data + "\n";
      }
      return webPage;
      // Se asegura de que el InputStream se cierra después de la aplicación deja de usarla.
  } finally {
      if (is != null) {
          is.close();
      }
  }
}

En el hilo que lleva a cabo las transacciones de red, utiliza HttpURLConnection para realizar un GET y descargar sus datos. Después de llamar a connect(), se puede obtener un InputStream de los datos llamando a getInputStream().

En el siguiente fragmento, el método doInBackground() llama al método downloadURL(). El método de descarga URL() toma la URL dada y lo utiliza para conectarse a la red a través de HttpURLConnection. Una vez que se ha establecido una conexión, la aplicación utiliza el método getInputStream() para recuperar los datos como un InputStream.

Tenga en cuenta que el método getResponseCode() devuelve el código de estado de la conexión. Esta es una manera útil de obtener información adicional acerca de la conexión. Un código de estado 200 indica éxito.

Si queremos asegurarnos que solo ejecutamos las siguientes acciones si recibimos un 200, podemos hacerlo de la siguiente forma:

if (response != 200){
  String contentAsString = "El recurso no está disponible";
  return contentAsString;
} else {
  is = conn.getInputStream();
  // etc...
}
  
En nuestro ejemplo, mostramos el contenido de la página web en un TextView. El código java completo es el siguiente:

package com.example.miguel.connectingtothenetwork;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends AppCompatActivity {

  private static final String DEBUG_TAG = "HttpExample";
  private TextView textView;
  TextView textDebug;


  @Override
  protected void onCreate(Bundle savedInstanceState) {
new DownloadImage().execute(urlImagenJPG);       super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      textView = (TextView) findViewById(R.id.myText);
      textView.setMovementMethod(new ScrollingMovementMethod());
      textDebug = (TextView)findViewById(R.id.textDebug);

      if (isConnected()){
          // Ejecuta las acciones
          new DownloadWebpageTask().execute("http://canariasvista.hol.es/");

      } else {
          Toast.makeText(getApplicationContext(),"Error en la conexión",Toast.LENGTH_SHORT).show();
      }
  }

  public boolean isConnected(){
      ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
      NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
      if (networkInfo != null && networkInfo.isConnected()) {
          return true;
      } else {
          return false;
      }
  }

  private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
      @Override
      protected String doInBackground(String... urls) {

          try {
              return downloadUrl(urls[0]);
          } catch (IOException e) {
              return "No se puede recuperar la página web. URL puede no ser válida.";
          }
      }
      // onPostExecute muestra el resultado de AsyncTask.
      @Override
      protected void onPostExecute(String result) {
          textView.setText(result);
      }
  }

  private String downloadUrl(String myurl) throws IOException {
      InputStream is = null;

      try {
          URL url = new URL(myurl);
          HttpURLConnection conn = (HttpURLConnection) url.openConnection();
          conn.setReadTimeout(10000 /* milisegundos */);
          conn.setConnectTimeout(15000 /* milisegundos */);
          conn.setRequestMethod("GET");
          conn.setDoInput(true);
          // Inicia la consulta
          conn.connect();
          int response = conn.getResponseCode();
          Log.d(DEBUG_TAG, "La respuesta es: " + response);
          is = conn.getInputStream();
          //Para descargar la página web completa
          BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
          String webPage = "";
          String data = "";
          while ((data = reader.readLine()) != null) {
              webPage += data + "\n";
          }
          return webPage;
          // Se asegura de que el InputStream se cierra después de la aplicación deja de usarla.
      } finally {
          if (is != null) {
              is.close();
          }
      }
  }
}


Fuentes:
Connecting to the Network
Operaciones HTTP En Android Con El Cliente HttpURLConnection

Otros artículos interesantes:
Managing Network Usage
AsyncTask: Tareas Asíncronas en Android
Use HttpURLConnection to download file from an HTTP URL
Android - Network Connection Tutorial

No hay comentarios:

Publicar un comentario