En aquesta pàgina s'indica com implementar aplicacions que es connectin a la xarxa utilitzant el protocol HTTP.
Interessant lectura:
https://packetzoom.com/blog/which-android-http-library-to-use.html
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" />
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:
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:
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:
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:
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:
Recursos:
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:
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.
// 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.
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); }
Vegeu OkHttp
aka android-async-http
Recursos:
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.
A type-safe REST client for Android and Java. Util per aplicacions REST. Vegeu Retrofit
Discontinuada sense manteniment
https://github.com/mttkay/droid-fu
Vegeu OkHTTP
Suporta SPDY. client Android i Java.
Vegeu Desenvolupaments_APIS_web#Android_com_a_REST_Client
Recursos: