IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

En aquesta pàgina s'indica com implementar aplicacions que es connectin a la xarxa utilitzant el protocol HTTP.

Història

Interessant lectura:

https://packetzoom.com/blog/which-android-http-library-to-use.html

Requeriments

Permisos

Cal tenir en compte que per realitzar operacions de xarxa, el fitxer de manifest de la nostra aplicació ha de tenir definits els següents permisos:

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

Clients HTTP

IMPORTANT: A la versió 6.0 d'Android s'ha eliminat el suport per al client Apache HttpClient

La majoria d'aplicacions Android connectades a internet utilitzen el protocol HTTP per enviar i rebre dades.

Android ofereix dos clients HTTP:

HttpURLConnection
Apache HttpClient no disponible de sèrie a Android 6.0

Tots dos suporten HTTPS, streaming uploads i downloads, timeouts configurables, IPv6, i connection pooling. Es recomana l'ús de HttpURLConnection per aplicacions des de la versió Gingerbread o superior.

Per continuar suportant Apache HTTP cal utilitzar una llibreria al fitxer build.gradle:

android {
    useLibrary 'org.apache.http.legacy'
}

Recursos:

Comprovar la connexió de xarxa

http://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html
https://gist.github.com/acacha/b59aa63d8fed9347b929

Necessita els permisos:

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

Abans de realitzar una connexió de xarxa, és una bona pràctica comprovar que el vostre dispositiu està connectat utilitzant el mètode:

getActiveNetworkInfo()

i

isConnected()

De les classes:

ConnectivityManager
http://developer.android.com/reference/android/net/ConnectivityManager.html

i

NetworkInfo
http://developer.android.com/reference/android/net/NetworkInfo.html

Vegem un exemple:

public void myClickHandler(View view) {
    ...
    ConnectivityManager connMgr = (ConnectivityManager) 
        getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {
        // fetch data
    } else {
        // display error
    }
    ...
}

Si us interessa comprovar la connexió WIFI utilitzeu WifiManager i WifiInfo que requereixen dels permisos:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
public static String getCurrentSsid(Context context) {
  String ssid = null;
  ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo networkInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
  if (networkInfo.isConnected()) {
    final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    final WifiInfo connectionInfo = wifiManager.getConnectionInfo();
    if (connectionInfo != null && !StringUtil.isBlank(connectionInfo.getSSID())) {
      ssid = connectionInfo.getSSID();
    }
  }
  return ssid;
}

Recursos:

Registrar mètodes per processar canvis en la connectivitat

El que cal fer és:

El registre és pot fer de dos formes:

Registrant al fitxer manifest:

<receiver android:name=".InternetConnectionReceiver" >
 <intent-filter>
  <action android:name="android.net.wifi.STATE_CHANGE" />
  <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
 </intent-filter>
</receiver>

Programaticament:

NetworkConnectionReceiver receiver = new NetworkConnectionReceiver();
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(receiver, intentFilter);
:*http://www.grokkingandroid.com/android-getting-notified-of-connectivity-changes/

El codi:

 public class InternetConnectionReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Make sure it's an event we're listening for ...
    if (!intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) &&
      !intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION) &&
      !intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
    {
      return;
    }

    ConnectivityManager cm = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));

    if (cm == null) {
      return;
    }

    // Now to check if we're actually connected
    if (cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected()) {
      // Start the service to do our thing
      Intent pushIntent = new Intent(context, ContentCheckService.class);
      context.startService(pushIntent);

      Toast.makeText(context, "Start service", Toast.LENGTH_SHORT).show();
    }
  }
} 

Un altres exemple molt interessant didàcticament de com depurar el receiver (extret de http://www.grokkingandroid.com/android-getting-notified-of-connectivity-changes/):

package com.grokkingandroid.connectivity;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class ConnectivityChangeReceiver 
               extends BroadcastReceiver {

   @Override
   public void onReceive(Context context, Intent intent) {
      debugIntent(intent, "grokkingandroid");
   }
   
   private void debugIntent(Intent intent, String tag) {
      Log.v(tag, "action: " + intent.getAction());
      Log.v(tag, "component: " + intent.getComponent());
      Bundle extras = intent.getExtras();
      if (extras != null) {
         for (String key: extras.keySet()) {
            Log.v(tag, "key [" + key + "]: " +
               extras.get(key));
         }
      }
      else {
         Log.v(tag, "no extras");
      }
   }

}

Recursos:

Intent per obrir la configuració de xarxa del mòbil

Para abrir las configuraciones del wifi:

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName("com.android.settings", "com.android.settings.wifi.WifiSettings");
startActivity(intent);

Para abrir la configuración de datos móviles:

 Intent intent = new Intent();
   intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   intent.setAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS);
   startActivity(intent);

Exemple:

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName("com.android.phone", "com.android.phone.NetworkSetting");
startActivity(intent);

O altres settings com:

DATA_ROAMING_SETTINGS
WIRELESS_SETTINGS

Exemple:

 Intent i = new Intent("android.settings.WIRELESS_SETTINGS");
 mContext.sendBroadcast(i);

Més exemples, per línia de comandes:

adb shell am start -n com.android.phone/com.android.phone.NetworkSetting

or (better):

adb shell am start -n com.android.settings/com.android.settings.wifi.WifiSettings

or (maybe the good one for you):

adb shell am start -n com.android.phone/com.android.phone.Settings

Codi complet:

import com.myapp.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;

public class MyActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.my_activity_layout);

        startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
    }
}

Un altre exemple:

final ComponentName cn = new ComponentName("com.android.phone","com.android.phone.MobileNetworkSettings");
final Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
intent.addCategory(Intent.ACTION_MAIN);
intent.setComponent(cn);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Recursos:

Threads i operacions de xarxa

Vegeu Android Threads per a més informació

Les operacions de xarxa poden implicar retards imprevisibles, i per tant, és imprescindible realitzar les operacions de connexió de xarxa en Threads independents del UI thread per evitar que la nostra aplicació es pengi o tingui un comportament que doni una mala experiència final al usuari.

La classe AsyncTask proporciona una de les formes més senzilles de fer-ho.

Anem a veure un exemple on el mètode:

myClickHandler() 

invoca:

DownloadWebpageTask().execute(stringUrl)

La classe DownloadWebpageTask és una subclasse d'AsyncTask. DownloadWebpageTask implementa els següents mètodes:

El codi és:

public class HttpExampleActivity extends Activity {
    private static final String DEBUG_TAG = "HttpExample";
    private EditText urlText;
    private TextView textView;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);   
        urlText = (EditText) findViewById(R.id.myUrl);
        textView = (TextView) findViewById(R.id.myText);
    }

    // When user clicks button, calls AsyncTask.
    // Before attempting to fetch the URL, makes sure that there is a network connection.
    public void myClickHandler(View view) {
        // Gets the URL from the UI's text field.
        String stringUrl = urlText.getText().toString();
        ConnectivityManager connMgr = (ConnectivityManager) 
            getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isConnected()) {
            new DownloadWebpageText().execute(stringUrl);
        } else {
            textView.setText("No network connection available.");
        }
        //http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
    }

     // Uses AsyncTask to create a task away from the main UI thread. This task takes a 
     // URL string and uses it to create an HttpUrlConnection. Once the connection
     // has been established, the AsyncTask downloads the contents of the webpage as
     // an InputStream. Finally, the InputStream is converted into a string, which is
     // displayed in the UI by the AsyncTask's onPostExecute method.
     private class DownloadWebpageText extends AsyncTask {
        @Override
        protected String doInBackground(String... urls) {
              
            // params comes from the execute() call: params[0] is the url.
            try {
                return downloadUrl(urls[0]);
            } catch (IOException e) {
                return "Unable to retrieve web page. URL may be invalid.";
            }
        }
        // onPostExecute displays the results of the AsyncTask.
        @Override
        protected void onPostExecute(String result) {
            textView.setText(result);
       }
    }
    ...
}

Al següent apartat podeu veure el detall del mètode downloadUrl

La seqüència d'esdeveniments és la següent:

  • When users click the button that invokes myClickHandler(), the app passes the specified URL to the AsyncTask subclass DownloadWebpageTask.
  • The AsyncTask method doInBackground() calls the downloadUrl() method.
  • The downloadUrl() method takes a URL string as a parameter and uses it to create a URL object.
  • The URL object is used to establish an HttpURLConnection.
  • Once the connection has been established, the HttpURLConnection object fetches the web page content as an InputStream.
  • The InputStream is passed to the readIt() method, which converts the stream to a string.
  • Finally, the AsyncTask's onPostExecute() method displays the string in the main activity's UI.

Recursos:

Realitzar les operacions de xarxa en un Worker Thread (2)

La forma correcta de realitzar operacions bloquejants com operacions de connexió per xarxa és executar les tasques en un worker thread.

IMPORTANT: Llegiu abans Procesos_Android

La millor forma per executar les operacions de xarxa en un fil d'execució separat és utilitzar la classe AsyncTask. Per a una discusió en més profunditat consulteu:

Multithreading For Performance [1]

Vegem un exemple (snippet):

public class HttpExampleActivity extends Activity {
    private static final String DEBUG_TAG = "HttpExample";
    private EditText urlText;
    private TextView textView;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);   
        urlText = (EditText) findViewById(R.id.myUrl);
        textView = (TextView) findViewById(R.id.myText);
    }

    // When user clicks button, calls AsyncTask.
    // Before attempting to fetch the URL, makes sure that there is a network connection.
    public void myClickHandler(View view) {
        // Gets the URL from the UI's text field.
        String stringUrl = urlText.getText().toString();
        ConnectivityManager connMgr = (ConnectivityManager) 
            getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isConnected()) {
            new DownloadWebpageText().execute(stringUrl);
        } else {
            textView.setText("No network connection available.");
        }
    }

     // Uses AsyncTask to create a task away from the main UI thread. This task takes a 
     // URL string and uses it to create an HttpUrlConnection. Once the connection
     // has been established, the AsyncTask downloads the contents of the webpage as
     // an InputStream. Finally, the InputStream is converted into a string, which is
     // displayed in the UI by the AsyncTask's onPostExecute method.
     private class DownloadWebpageText extends AsyncTask {
        @Override
        protected String doInBackground(String... urls) {
              
            // params comes from the execute() call: params[0] is the url.
            try {
                return downloadUrl(urls[0]);
            } catch (IOException e) {
                return "Unable to retrieve web page. URL may be invalid.";
            }
        }
        // onPostExecute displays the results of the AsyncTask.
        @Override
        protected void onPostExecute(String result) {
            textView.setText(result);
       }
    }
    ...
}

On el mètode:

myClickHandler()

Invoca:

DownloadWebpageTask().execute(stringUrl)

Un mètode que executa la tasca asíncrona (subclase de AsyncTask) encarregada de descarregar la URL indicada. S'implementen el següents mètodes:

  • doInBackground(): executa la feina que s'ha de fer en un thread a part, s'encarrega de descarregar la pàgina web i quan acaba passa el resultat.
  • onPostExecute(): aquí podem actualitzar la UI amb el resultat de la descàrrega.

Connectar-se i descarregar-se dades (download)

La connexió es realitzar amb una petició HTTP de tipus GET i es descarreguen les dades i es llegeixen amb getInputStream()

Vegem un exemple de mètode downloadUrl:

// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string.
private String downloadUrl(String myurl) throws IOException {
    InputStream is = null;
    // Only display the first 500 characters of the retrieved
    // web page content.
    int len = 500;
        
    try {
        URL url = new URL(myurl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(10000 /* milliseconds */);
        conn.setConnectTimeout(15000 /* milliseconds */);
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        // Starts the query
        conn.connect();
        int response = conn.getResponseCode();
        Log.d(DEBUG_TAG, "The response is: " + response);
        is = conn.getInputStream();

        // Convert the InputStream into a string
        String contentAsString = readIt(is, len);
        return contentAsString;
        
    // Makes sure that the InputStream is closed after the app is
    // finished using it.
    } finally {
        if (is != null) {
            is.close();
        } 
    }
}

Cal tenir en compte que el mètode:

getResponseCode()

retorna el status code de la connexió el que permet depurar la connexió (404 és el coi Not Foud, 200 el codi de tot correcte, 500 un error de servidor, etc...). Vegeu HTTP i HTTP Status codes.

Al següent apartat vegem la conversió a text del InputStream utilitzant el mètode readIt.

HttpURLConnection Connect and Download Data. Example

// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string.
private String downloadUrl(String myurl) throws IOException {
    InputStream is = null;
    // Only display the first 500 characters of the retrieved
    // web page content.
    int len = 500;
        
    try {
        URL url = new URL(myurl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(10000 /* milliseconds */);
        conn.setConnectTimeout(15000 /* milliseconds */);
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        // Starts the query
        conn.connect();
        int response = conn.getResponseCode();
        Log.d(DEBUG_TAG, "The response is: " + response);
        is = conn.getInputStream();

        // Convert the InputStream into a string
        String contentAsString = readIt(is, len);
        return contentAsString;
        
    // Makes sure that the InputStream is closed after the app is
    // finished using it.
    } finally {
        if (is != null) {
            is.close();
        } 
    }
}

Note that the method getResponseCode() returns the connection's status code. This is a useful way of getting additional information about the connection. A status code of 200 indicates success.

Convertir el InputStream a String

Les dades descarregades poden ser de diferents tipus (vegeu mime types). Per exemple, de tipus binari i una imatge:

InputStream is = null;
...
Bitmap bitmap = BitmapFactory.decodeStream(is);
ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setImageBitmap(bitmap);

En canvi per descarregar un text, per exemple un HTML:

// Reads an InputStream and converts it to a String.
public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
    Reader reader = null;
    reader = new InputStreamReader(stream, "UTF-8");        
    char[] buffer = new char[len];
    reader.read(buffer);
    return new String(buffer);
}

Llibreries

OK http

Vegeu OkHttp

loopj android-async-http

aka android-async-http

Recursos:

ION

http-request

Volley

Volley Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster. Volley is available through the open AOSP repository.

Retrofit

A type-safe REST client for Android and Java. Util per aplicacions REST. Vegeu Retrofit

DroidFU

Discontinuada sense manteniment

https://github.com/mttkay/droid-fu

OkHTTP

Vegeu OkHTTP

Suporta SPDY. client Android i Java.

Android com a REST Client

Vegeu Desenvolupaments_APIS_web#Android_com_a_REST_Client

Exemples. Samples

Recursos:

Vegeu també

Enllaços externs