HTTP (Hypertext Transfer Protocol) est un protocole de communication client-serveur développé pour le World Wide Web. HTTPS est la variante du HTTP sécurisée par l’usage des protocoles SSL ou TLS.
HTTP est un protocole de la couche application qui utilise le protocole TCP comme couche de transport. Un serveur HTTP utilise alors par défaut le port 80 (443 pour HTTPS).
Il est parfois nécessaire d’effectuer des requêtes HTTP vers un serveur distant.
Afin d’effectuer des opérations réseau depuis l’application, il faut doit inclure les autorisations suivantes dans le fichier AndroidManifest.xml
:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplicationhttp">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Dans la plupart des cas, il est suffisant d’utiliser un navigateur Web standard, comme Chrome, pour fournir le contenu d’une page Web à l’utilisateur.
Sinon, les objets WebView permettent d’afficher un contenu Web dans le layout d’une activité. Evicemment, un WebView
ne dispose pas des fonctionnalités des navigateurs classiques.
Pour en savoir plus : lire la documentation sur le contenu Web.
Un objet de type WebView
qui, comme son nom l’indique, est un object graphique fait pour visualiser une page web. Cependant, les liens web appelent tout de même le navigateur, ce qui est assez génant.
Afin de ne pas lancer le navigateur par défaut, il faut surcharger le client web afin de recoder la méthode agissant lorsqu’un lien est cliqué. La classe est très simple :
private class MyWebViewClient extends WebViewClient
{
Context context;
public MyWebViewClient(Context c)
{
context = c;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
view.loadUrl(url);
//Log.d("MyWebViewClient", "Chargement " + url);
Toast.makeText(getApplicationContext(), "Chargement " + url, Toast.LENGTH_SHORT).show();
return true;
}
}
Du côté de l’activité, il faut remplacer le comportement du client web par défaut.
public class MainActivity extends Activity
{
WebView wv = null;
final private String url = "http://tvaira.free.fr/index.html";
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//requestWindowFeature(Window.FEATURE_NO_TITLE); // enlève la barre de menu
setContentView(R.layout.activity_main);
wv = (WebView)findViewById(R.id.webView1);
wv.setWebViewClient(new MyWebViewClient(this));
wv.getSettings().setJavaScriptEnabled(true);
wv.loadUrl(url);
}
private class MyWebViewClient extends WebViewClient
{
// ...
}
}
On définit le layout activity_main.xml
pour notre WebView
:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
Voir aussi : Application hybride
Android fournit le client HttpURLConnection pour effectuer des requêtes HTTP. Il s’appuie sur la classe abstraite URLConnection.
Le principe d’utilisation d’un HttpURLConnection
est assez simple en soi :
URL url = new URL("http://tvaira.free.fr/index.html");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try
{
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String ligne;
while((ligne = reader.readLine()) != null)
{
sb.append(ligne);
}
Log.v("HttpURLConnection", sb.toString());
}
finally
{
urlConnection.disconnect();
}
Par contre, Android oblige à effectuer les opérations réseau sur un thread autre que le thread UI principal sinon une exception NetworkOnMainThreadException
sera levée.
Voir aussi : Tâches d’arrière-plan [PDF]
Pour effectuer la requête et la traiter, on va donc utiliser une AsyncTask
. L’activité comprend seulement une zone de texte TextView
et un bouton pour envoyer la requête :
public class MainActivity extends AppCompatActivity
{
private final String TAG = "HttpJSON";
private Button boutonEnvoyer;
private TextView texte;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
texte = (TextView) findViewById(R.id.texte);
texte.setText("");
boutonEnvoyer = (Button)findViewById(R.id.boutonEnvoyer);
boutonEnvoyer.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
AsyncTaskHTTP task = new AsyncTaskHTTP(MainActivity.this);
task.execute("http://tvaira.free.fr/index.html");
}
});
}
public class AsyncTaskHTTP extends AsyncTask<String, Void, String>
{
private AppCompatActivity myActivity;
public AsyncTaskHTTP(AppCompatActivity mainActivity)
{
myActivity = mainActivity;
}
@Override
protected String doInBackground(String... strings)
{
URL url = null;
HttpURLConnection urlConnection = null;
String data = null;
try
{
Log.d("AsyncTaskHTTP", "url := " + strings[0]);
url = new URL(strings[0]);
urlConnection = (HttpURLConnection) url.openConnection();
int responseCode = urlConnection.getResponseCode();
Log.d("AsyncTaskHTTP", "responseCode := " + responseCode);
if (responseCode != HttpURLConnection.HTTP_OK)
{
return data;
}
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String ligne;
while((ligne = reader.readLine()) != null)
{
sb.append(ligne);
}
data = sb.toString();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (urlConnection != null)
urlConnection.disconnect();
}
return data;
}
@Override
protected void onPostExecute(String s)
{
if(s == null)
return;
texte.setText(s);
}
}
}
Il est aussi possible d’intégrer des métadonnées dans la partie header de la requête. Exemple en HTTPS :
@Override
protected String doInBackground(String... strings)
{
URL url = null;
HttpsURLConnection urlConnection = null;
String data = null;
try
{
Log.d("AsyncTaskHTTP", "url := " + strings[0]);
url = new URL(strings[0]);
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestProperty("Authorization", "xxxxxxxx");
int responseCode = urlConnection.getResponseCode();
Log.d("AsyncTaskHTTP", "responseCode := " + responseCode);
if (responseCode != HttpURLConnection.HTTP_OK)
{
return data;
}
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String ligne;
while((ligne = reader.readLine()) != null)
{
sb.append(ligne);
}
data = sb.toString();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (urlConnection != null)
urlConnection.disconnect();
}
return data;
}
Avant d’envoyer une requête HTTP, il est possible de vérifier si une connexion au réseau est possible :
private boolean estConnecteReseau()
{
ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo == null || !networkInfo.isConnected() || (networkInfo.getType() != ConnectivityManager.TYPE_WIFI && networkInfo.getType() != ConnectivityManager.TYPE_MOBILE))
{
return false;
}
return true;
}
Il existe d’autres bibliothèques pour effectuer des requêtes HTTP (plus simplement) :
Pour illustrer l’utilisation des différentes bibliothèques, on va effectuer une requête vers un serveur openweathermap.org
pour obtenir la météo d’une ville. Le résultat obtenu est au format JSON :
{
"coord":{"lon":4.83,"lat":44.08},
"weather":[{"id":500,"main":"Rain","description":"légère pluie","icon":"10d"}],
"base":"stations",
"main":{"temp":11.66,"feels_like":5.58,"temp_min":11,"temp_max":13,"pressure":1011,"humidity":81},
"visibility":10000,
"wind":{"speed":8.2,"deg":130},
"rain":{"1h":0.25},
"clouds":{"all":90},
"dt":1582969672,
"sys":{"type":1,"id":6516,"country":"FR","sunrise":1582957146,"sunset":1582997254},
"timezone":3600,
"id":3035679,
"name":"Avignon",
"cod":200
}
Pour les tests, on va juste extraire la température (“temp”) et le timestamp (“dt”).
Voir : OkHttp
Dans app/build.gradle
:
...
dependencies {
...
implementation 'com.squareup.okhttp3:okhttp:3.4.1'
...
}
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Callback
{
private final String TAG = "OkHttp";
private Button boutonEnvoyer;
private TextView texte;
private final String APPID = "xxxxxxxxx";
private final OkHttpClient client = new OkHttpClient();
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
texte = (TextView) findViewById(R.id.texte);
texte.setText("");
boutonEnvoyer = (Button)findViewById(R.id.boutonEnvoyer);
boutonEnvoyer.setOnClickListener(this);
}
@Override
public void onClick(View view)
{
Log.d("OkHttp", "onClick");
Request request = new Request.Builder().url("http://api.openweathermap.org/data/2.5/weather?q=Avignon,FR&units=metric&lang=fr&appid=" + APPID).build();
client.newCall(request).enqueue(this);
}
@Override
public void onFailure(Call call, IOException e)
{
runOnUiThread(new Runnable()
{
@Override
public void run()
{
texte.setText("Erreur !");
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException
{
Log.d("OkHttp", "onResponse := " + response.message());
if (!response.isSuccessful())
{
throw new IOException(response.toString());
}
final String body = response.body().string();
runOnUiThread(new Runnable()
{
@Override
public void run()
{
JSONObject json = null;
try
{
json = new JSONObject(body);
long timestamp = json.getLong("dt");
Log.d("OkHttp", "timestamp := " + timestamp);
Date date = new Date(timestamp*1000);
String strDate = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date);
JSONObject payloadFields = json.getJSONObject("main");
double temperature = payloadFields.getDouble("temp");
Log.d("OkHttp", "Température := " + temperature + " °C");
texte.setText(strDate + "\nTempérature : " + temperature + " °C");
}
catch (JSONException e)
{
e.printStackTrace();
}
}
});
}
}
Il est aussi possible d’intégrer des métadonnées dans la partie header de la requête :
String url = "";
Request request = new Request.Builder().url(url)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "xxxxx")
.build();
Voir : Volley
Dans app/build.gradle
:
...
dependencies {
...
implementation 'com.android.volley:volley:1.1.0'
...
}
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Response.Listener<String>, Response.ErrorListener
{
private final String TAG = "Volley";
private Button boutonEnvoyer;
private TextView texte;
private final String APPID = "xxxxxxxxxxx";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
texte = (TextView) findViewById(R.id.texte);
texte.setText("");
boutonEnvoyer = (Button)findViewById(R.id.boutonEnvoyer);
boutonEnvoyer.setOnClickListener(this);
}
@Override
public void onClick(View view)
{
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest request = new StringRequest(Request.Method.GET, "http://api.openweathermap.org/data/2.5/weather?q=Avignon,FR&units=metric&lang=fr&appid=" + APPID, this, this);
queue.add(request);
}
@Override
public void onErrorResponse(VolleyError error)
{
texte.setText("Erreur !");
}
@Override
public void onResponse(String response)
{
JSONObject json = null;
try
{
json = new JSONObject(response);
long timestamp = json.getLong("dt");
Log.d("Volley", "timestamp := " + timestamp);
Date date = new Date(timestamp*1000);
String strDate = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date);
JSONObject payloadFields = json.getJSONObject("main");
double temperature = payloadFields.getDouble("temp");
Log.d("Volley", "Température := " + temperature + " °C");
texte.setText(strDate + "\nTempérature : " + temperature + " °C");
}
catch (JSONException e)
{
e.printStackTrace();
}
}
}
On peut directement recevoir un JSONObject
en modifiant le code comme ceci :
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Response.Listener<JSONObject>, Response.ErrorListener
{
...
@Override
public void onClick(View view)
{
RequestQueue queue = Volley.newRequestQueue(this);
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, "http://api.openweathermap.org/data/2.5/weather?q=Avignon,FR&units=metric&lang=fr&appid=" + APPID, null, this, this);
queue.add(request);
}
@Override
public void onResponse(JSONObject response)
{
try
{
long timestamp = response.getLong("dt");
Log.d("Volley", "timestamp := " + timestamp);
Date date = new Date(timestamp*1000);
String strDate = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date);
JSONObject payloadFields = response.getJSONObject("main");
double temperature = payloadFields.getDouble("temp");
Log.d("Volley", "Température := " + temperature + " °C");
texte.setText(strDate + "\nTempérature : " + temperature + " °C");
}
catch (JSONException e)
{
e.printStackTrace();
}
}
}
Il est aussi possible d’intégrer des métadonnées dans la partie header de la requête :
String url = "";
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, this, this)
{
@Override
public Map<String, String> getHeaders() throws AuthFailureError
{
Map<String, String> params = new HashMap<String, String>();
params.put("Accept", "application/json");
params.put("Authorization", "xxxxxxx");
return params;
}
};
Voir : Google HTTP Client
Dans app/build.gradle
:
...
android {
...
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}
}
dependencies {
...
implementation 'com.google.api-client:google-api-client:1.30.9'
...
}
public class MainActivity extends AppCompatActivity
{
private final String TAG = "HttpJSON";
private Button boutonEnvoyer;
private TextView texte;
private final String APPID = "xxxxxxxxxxx";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
texte = (TextView) findViewById(R.id.texte);
texte.setText("");
boutonEnvoyer = (Button)findViewById(R.id.boutonEnvoyer);
boutonEnvoyer.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
AsyncMeteoJSON task = new AsyncMeteoJSON(MainActivity.this);
task.execute("http://api.openweathermap.org/data/2.5/weather");
}
});
}
public class AsyncMeteoJSON extends AsyncTask<String, Void, JSONObject>
{
private AppCompatActivity myActivity;
public AsyncMeteoJSON(AppCompatActivity mainActivity)
{
myActivity = mainActivity;
}
@Override
protected JSONObject doInBackground(String... strings)
{
JSONObject json = null;
try
{
HttpTransport httpTransport = new NetHttpTransport();
HttpRequestFactory requestFactory = httpTransport.createRequestFactory();
GenericUrl url = new GenericUrl(strings[0]);
url.put("q", "Avignon,FR");
url.put("units", "metric");
url.put("lang", "fr");
url.put("appid", APPID);
HttpRequest request = null;
request = requestFactory.buildGetRequest(url);
HttpResponse httpResponse = request.execute();
Log.d("AsyncMeteoJSON", "httpResponse := " + httpResponse.getStatusCode() + " " + httpResponse.getStatusMessage());
if(httpResponse.getStatusCode() == 200)
{
try
{
json = new JSONObject(httpResponse.parseAsString());
}
catch (JSONException e)
{
e.printStackTrace();
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
return json;
}
@Override
protected void onPostExecute(JSONObject s)
{
if(s == null)
return;
JSONObject payloadFields = null;
try
{
long timestamp = s.getLong("dt");
Log.d("AsyncMeteoJSON", "timestamp := " + timestamp);
Date date = new Date(timestamp*1000);
String strDate = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date);
payloadFields = s.getJSONObject("main");
double temperature = payloadFields.getDouble("temp");
Log.d("AsyncMeteoJSON", "Température := " + temperature + " °C");
texte.setText(strDate + "\nTempérature : " + temperature + " °C");
}
catch (JSONException e)
{
e.printStackTrace();
}
}
}
}
public class MainActivity extends AppCompatActivity
{
private final String TAG = "MeteoJSON";
private Button boutonEnvoyer;
private TextView texte;
private final String APPID = "xxxxxxxxxxxxxxxxxxxxx";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
texte = (TextView) findViewById(R.id.texte);
texte.setText("");
boutonEnvoyer = (Button)findViewById(R.id.boutonEnvoyer);
boutonEnvoyer.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
AsyncMeteoJSON task = new AsyncMeteoJSON(MainActivity.this);
task.execute("http://api.openweathermap.org/data/2.5/weather?q=Avignon,FR&units=metric&lang=fr&appid=" + APPID);
}
});
}
public class AsyncMeteoJSON extends AsyncTask<String, Void, JSONObject>
{
private AppCompatActivity myActivity;
public AsyncMeteoJSON(AppCompatActivity mainActivity)
{
myActivity = mainActivity;
}
@Override
protected JSONObject doInBackground(String... strings)
{
URL url = null;
HttpURLConnection urlConnection = null;
String dataJSON = null;
try
{
Log.d("AsyncMeteoJSON", "url := " + strings[0]);
url = new URL(strings[0]);
urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String ligne;
while((ligne = reader.readLine()) != null)
{
sb.append(ligne);
}
dataJSON = sb.toString();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (urlConnection != null)
urlConnection.disconnect();
}
JSONObject json = null;
try
{
json = new JSONObject(dataJSON);
}
catch (JSONException e)
{
e.printStackTrace();
}
return json;
}
@Override
protected void onPostExecute(JSONObject s)
{
if(s == null)
return;
JSONObject payloadFields = null;
try
{
long timestamp = s.getLong("dt");
Log.d("AsyncMeteoJSON", "timestamp := " + timestamp);
Date date = new Date(timestamp*1000);
String strDate = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date);
payloadFields = s.getJSONObject("main");
double temperature = payloadFields.getDouble("temp");
Log.d("AsyncMeteoJSON", "Température := " + temperature + " °C");
texte.setText(strDate + "\nTempérature : " + temperature + " °C");
}
catch (JSONException e)
{
e.printStackTrace();
}
}
}
}
© Thierry Vaira <tvaira@free.fr>