Android, A Complete Course, From Basics to Enterprise Edition

Communication HTTP.

Android, A Complete Course, From Basics to Enterprise Edition. 1

Communication HTTP. 1

Communiquer avec le système. 1

1       Communiquer avec l’extérieur de l’activité. 1

1.1         Internet. 1

 Cet article est extraie du livre « Android, A Complete Course », disponible sur Android2ee.com.

Les exemples ou les programmes présents dans cet ouvrage sont fournis pour illustrer les descriptions théoriques. Ce code est libre de toute utilisation mais n'est pas distribuable.

La distribution du code est reservée au site :

http://android2ee.com.

La propriété intellectuelle du code appartient à :

Mathias Séguy

L’utilisation de ces codes est sous votre unique responsabilité et personne d’autre que vous ne pourra être tenu responsable des préjudices ou dommages de quelques natures que ce soit pouvant résulter de son utilisation.

Tous les noms de produits ou marques cités dans cet ouvrage sont des marques déposés par leurs propriétaires respectifs.

Publié par http://android2ee.com

Titre Original : Android, A Complete Course, From Basics to Enterprise Edition. Édition Française.

ISBN : 979-10-90388-00-0

Copyright © 2011 by Mathias Séguy

 

Aucune représentation ou reproduction, même partielle, autre que celles prévues à l’article L. 122-5 2° et 3° a) du code de la propriété intellectuelle ne peut être faite sans l’autorisation expresse de Mathias Seguy ou, le cas échéant, sans le respect des modalités prévues à l’article L. 122-10 dudit code

Communiquer avec le système

1         Communiquer avec l’extérieur de l’activité

 

1.1       Internet

1.1.1       Navigateur interne : Le WebKit

Il y a trois éléments à mettre en place :

·         Dans votre layout.xml, il faut déclarer le WebKit, qu’il apparaisse à l’utilisateur

·         Dans votre Activity, il faut charger l’url à afficher, ou charger un code HTML à afficher

·         Dans votre manifest.xml, il faut déclarer que votre application accède à internet (ou pas si vous affichez du code HTML qui vous est propre)

Ce qui donne :

Dans le fichier Layout.xml :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

                android:orientation="vertical"

                android:layout_width="fill_parent"

                android:layout_height="fill_parent"

                >

                <WebView android:id="@+id/webkit"

                               android:layout_width="fill_parent"

                               android:layout_height="fill_parent"

                />

</LinearLayout>

Dans le fichier manifest.xml :

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

  package="com.commonsware.android.browser1">

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

  <application android:icon="@drawable/cw">

    <activity android:name=".WebKitExample" android:label=" WebKitExample ">

      <intent-filter>

        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />

      </intent-filter>

    </activity>

  </application>

</manifest>

Et dans votre Activity :

WebView webKit;

 

  @Override

  public void onCreate(Bundle icicle) {

    super.onCreate(icicle);

    setContentView(R.layout.main);

    //Récupération du WebKit

    webKit =(WebView)findViewById(R.id.webkit);

    //Soit vous loadez une URL

    webKit.loadUrl("http://www.pearson.fr");

    //Soit vous loadez du code HTML en spécifiant le type MIME et son encoding

    webKit.loadData("<html><body>Bonjour !</body></html>",

              "text/html", "UTF-8");

    //Soit vous utilisez la method suivante plus complexe :

    browser.loadDataWithBaseURL("x-data://base", "<html><body>Bonjour !</body></html>", "text/html", "UTF-8", historyURL);

 

  }

1.1.1.1       Gestion des actions usuelles d’un navigateur

Vous pouvez utiliser les méthodes suivantes pour gérer les actions usuelles d’un navigateur :

//Managing the navigation Back and Forward

browser.canGoBack();

int step=1;

browser.canGoBackOrForward(step);

browser.canGoForward();

browser.goBack();

browser.goForward();

step=-2;

browser.goBackOrForward(step);

//Managing the reload and the clear

browser.reload();

boolean includeDiskFiles=true;

browser.clearCache(includeDiskFiles);

browser.clearHistory();

browser.getUrl();

Si vous voulez que l’utilisateur accède à ces méthodes, c’est à vous d’implémenter l’IHM.

L’objet WebView possède une palanquée de méthodes, nous ne présentons que celle-ci.

1.1.1.2       Gérer les évènements du navigateur : l’objet WebViewClient

Cet objet permet de pouvoir être informé des évènements majeurs du navigateur :

//The page starts to be loading

webViewClient.onPageStarted(view, url, favicon);

//The ressource starts to be loading

webViewClient.onLoadResource(view, url);

//the page is loaded

webViewClient.onPageFinished(view, url);

//An error has been received

webViewClient.onReceivedError(view, errorCode, description, failingUrl);

//An authentification request has been send (default is cancel the request)

webViewClient.onReceivedHttpAuthRequest(view, handler, host, realm);

//Is called when a new URL is about to be loaded. If retrun true, the host application handles the URL loading, else the current WebView handles the application

webViewClient.shouldOverrideUrlLoading(view, url);

La méthode shouldOverrideUrlLoading vous permet d’intercepter le chargement d’un URL, soit pour mettre un code qui vous est propre, soit pour que le traitement s’effectue dans une autre activité, soit pour faire une autre action.

Par exemple, vous pouvez vouloir construire une IHM qui ne soit qu’une page web et utiliser des liens pipo pour injecter de la dynamique dans votre page :

/**

 * @author mSeguy

 * @goals This class aims to show an simple example of the WebKit usage

 */

public class WebKitSample extends Activity {

  /**

   * The browser

   */

  WebView browser;

  /**

   * The webViewClient that handles main events on the browser

   */

  Callback webViewClient;

 

  @Override

  public void onCreate(Bundle icicle) {

    super.onCreate(icicle);

    setContentView(R.layout.main);

    browser = (WebView) findViewById(R.id.webkit);

    //instanciate the webBrowser's events listener

    webViewClient = new Callback();

    //link the listener to the browser

    browser.setWebViewClient(webViewClient);

  }

  /****************************************************************************************/

  /** Managing your own navigation ******************************************************/  /***************************************************************************************/

  /**

   * Your Home html page

   */

  public final String homeHtml =

"<html><body>Hello, I can display a message <a href=\"message1\">Message1</a> "

      + "ou vous donnez l'heure<a href=\"clock\">Heure</a> </body></html>";

  /**

   * Your Message html page

   */

  public final String message1Html = "<html><body>My message is Welcome buddy."

      + "<br>Go Back <a href=\"home\">Back</a></body></html>";

  /**

   * Your clock page

   */

  public final String clockHtml = "<html><body>Il est" + new Date().toString()

      + "<br>Go Back <a href=\"home\">Back</a> </body></html>";

  /**

   * The url not found page

   */

  public final String lostHtml = "<html><body>Où êtes vous? Ou voulez vous aller"

      + "<br>Go Back <a href=\"home\">Back</a>" + "<br><a href=\"message1\">Message1</a>"

      + "<br><a href=\"clock\">Heure</a> </body></html> ";

 

  /**

   * @author mSeguy

   * @goals

   * This class aims to define a listener on the events of the webKit and

   * associate a specific behavior when an url has to be loaded

   */

  private class Callback extends WebViewClient {

    /* (non-Javadoc)

     * @see android.webkit.WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,

java.lang.String)

     */

    public boolean shouldOverrideUrlLoading(WebView view, String url) {

      //Call the load URL method that will open a specific URL

      loadPage(url);

      //Say your webKit will manage the URL loading

      return (true);

    }

  }

 

  /**

   * @param url

   */

  void loadPage(String url) {

    //The displayed html page

    String page;

    //Switch case loading the HTML to display depending on the url

    if(url.contains("message1")){

      page=message1Html;

    }else if(url.contains("clock")){

      page=clockHtml;

    }else if(url.contains("home")){

      page=homeHtml;

    }else{

      page=lostHtml;

    }

    //load the html page

    browser.loadDataWithBaseURL("x-data://base", page, "text/html", "UTF-8", null);

    // where this method is loadDataWithBaseURL (String baseUrl, String data, String mimeType, String encoding,

    // String historyUrl);

 

  }

}

1.1.1.3       Gérer les préférences du navigateur

L’objet WebSetting obtenu par getSettings vous permet de spécifier les options du navigateur. En particulier les fonts (setDefaultFontSize) et le javascript (setJavaScriptEnabled).

A vous de découvrir l’ensemble des options. Bien sûr, si vous voulez les persister, c’est à vous de mettre en place ce mécanisme (cf. le chapitre 6.2 Gestion des préférences).

1.1.2       Communication HTTP

Au-delà d’afficher de simples pages HTML, vous voulez mettre en place un protocole de communication entre votre activité et un serveur Web. Pour cela il vous faut mettre en place le protocole de communication client-serveur spécifique à votre application. Pas de problème, Android intègre la bibliothèque Apache HttpComponents. Bon, d’accord, il n’y a pas d’API SOAP native ni XML-RPC.

Par contre, Android intègre les parseurs DOM, SAX et JSON ce qui va nous permettre de faire transiter des données XML sans trop de difficulté.

1.1.2.1       A simple login exemple

La partie cliente Android

public class LoginActivity extends Activity {

                private HttpClient client;

                int NetworkConnectionTimeout_ms = 1000;

                private String name;

                private WebView browser;

                private String password;

 

                @Override

                public void onCreate(Bundle icicle) {

                               super.onCreate(icicle);

                               browser = (WebView) findViewById(R.id.webkit);

                               // Do something, build the IHM with name and password fields...

                               // And call login() when the user press the login button

                }

 

                /**

                 *

                 */

                private void login() {

                               HttpParams params = new BasicHttpParams();

                               // set params for connection...

                               HttpConnectionParams.setStaleCheckingEnabled(params, false);

                               HttpConnectionParams.setConnectionTimeout(params, NetworkConnectionTimeout_ms);

                               HttpConnectionParams.setSoTimeout(params, NetworkConnectionTimeout_ms);

                               params.setParameter("name", name);

                               params.setParameter("password", password);

                               // we supposed the servlet WelcomeIdentifiedUser exists

                               params.setParameter("login.target", "localHost:8080/welcomeIdentifiedUser");

 

                               client = new DefaultHttpClient(params);

                               String url = "localHost:8080/login";

                               HttpPost postMethod = new HttpPost(url);

                               try {

                                               ResponseHandler<String> responseHandler = new BasicResponseHandler();

                                               String responseBody = client.execute(postMethod, responseHandler);

                                               browser.loadDataWithBaseURL(null, responseBody, "text/html", "UTF-8", null);

                               } catch (Throwable t) {

                                               Toast.makeText(this, "La requete a echouee: " + t.toString(), 4000).show();

                               }

                }

 

}

La partie Servlet sur votre serveur de servlet :

public class LoginHandler extends HttpServlet {

 

                public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException,

                                               IOException {

                               res.setContentType("text/html");

                               PrintWriter out = res.getWriter();

 

                               String account = req.getParameter("account");

                               String password = req.getParameter("password");

 

                               if (!allowUser(account, password)) {

                                               out.println("<HTML><HEAD><TITLE>Access Denied</TITLE></HEAD>");

                                               out.println("<BODY>Your login and password are invalid.<BR>");

                                               out.println("You may want to <A HREF=\"/login.html\">try again</A>");

                                               out.println("</BODY></HTML>");

                               } else {

                                               // Valid login. Make a note in the session object.

                                               HttpSession session = req.getSession();

                                               session.setAttribute("logon.isDone", account);

                                               session.setAttribute("account", account);

                                               session.setAttribute("password", password);

                                               // Try redirecting the client to the page he first tried to access

                                               try {

                                                               String target = (String) session.getAttribute("login.target");

                                                               if (target != null) {

                                                                              res.sendRedirect(target);

                                                                              return;

                                                               }

                                               } catch (Exception ignored) {

                                               }

 

                                               // Couldn't redirect to the target. Redirect to the site's home page.

                                               res.sendRedirect("/");

                               }

                }

 

                /*

                 * (non-Javadoc)

                 *

                 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,

                 * javax.servlet.http.HttpServletResponse)

                 */

                @Override

                protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,

                                               IOException {

                               doPost(req, resp);

                }

 

                /**

                 * @param account

                 * @param password

                 * @param pin

                 * @return

                 */

                protected boolean allowUser(String account, String password) {

                               return true; // trust everyone

                }

}

1.1.2.2       Envoyer et recevoir un objet en utilisant la sérialisation

Nous prenons l’exemple d’un objet de type Object, mais vous pouvez passer tous les objets que vous voulez.

Ainsi dans votre activité, vous pouvez utiliser le code suivant :

//Pour envoyer un objet:

  Object myObject=null;

  params.setParameter("myObject", myObject);

 

   //Pour récupérer un objet

try {

    httpClient.execute(postMethod, new ResponseHandler<Void>() {

      public Void handleResponse(HttpResponse response) throws ClientProtocolException, IOException {

        HttpEntity resp_entity = response.getEntity();

        if (resp_entity != null) {

          try {

            byte[] data = EntityUtils.toByteArray(resp_entity);

            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));

            Object myObject = (Object) ois.readObject();

          }

          catch (Exception e) {

            Log.e(getClass().getSimpleName(), "problem processing post response", e);

          }

 

        }

        else {

          throw new IOException(

              new StringBuffer()

                  .append("HTTP response : ").append(response.getStatusLine())

                  .toString());

        }

        return null;

      }

    });

  } catch (ClientProtocolException e) {

    e.printStackTrace();

  } catch (IOException e) {

    e.printStackTrace();

  }

Et dans votre servlet vous pouvez utiliser le code suivant :

//Pour récupérer un object

Object myObject = req.getParameter("myObject");

//Pour renvoyer un objet

ObjectOutputStream outObject = new ObjectOutputStream(res.getOutputStream());

outObject.writeObject(myObject);

1.1.2.3       Exemple de récupération et de parsing de fichier xml

Cela permet de récupérer le flux XML d’un web service, de le parser et de reconstruire une page HTML qui l’affiche au sein d’une activité:

import android.app.Activity;

import android.os.Bundle;

import android.webkit.WebView;

import android.widget.Toast;

 

/**

 * @author sti (Julien PAPUT)

 * @goals This class aims to:

 *        <ul>

 *        <li>This class show the Toulouse Forecast</li>

 *        </ul>

 */

public class WebServiceAccessTuto extends Activity {

 

/*****************************************************************************************/

/** Attributes **************************************************************************/

/*****************************************************************************************/

/** Thr url to use */

private final String url = "http://www.google.com/ig/api?weather=Toulouse";

/** The browser to display the result */

private WebView browser;

/** The object used to communicate with http */

private HttpClient client;

/** The Forecast array */

private final List<Forecast> forecasts = new ArrayList<Forecast>();

/** The HTML page */

private String page;

/** The raw xml answer    */

private String responseBody;

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

                super.onCreate(savedInstanceState);

                setContentView(R.layout.main);

                // instanciate the browser

                browser = (WebView) findViewById(R.id.webkit);

                // instanciate the http communication

                client = new DefaultHttpClient();   

                // get Toulouse Forecast

                getForecast();

                // build the HTML page to be displayed to the use

                page = generatePage();                     

                // update the data displayed by the broser

                browser.loadDataWithBaseURL(null,page, "text/html", "UTF-8", null);            

}

 

/**

 * Retrieve the xml stream from the forecast url

 */

private void getForecast() {

                // The HTTP get method send to the URL

                HttpGet getMethod = new HttpGet(url);

                // The basic response handler

                ResponseHandler<String> responseHandler = new BasicResponseHandler();

                try {

                               // Call the URL and get the response body

                               responseBody = client.execute(getMethod, responseHandler);

                               // parse the response body

                               buildForecasts(responseBody);

                } catch (Throwable t) {

                               Toast.makeText(this, "La requete a echouee: " + t.toString(), Toast.LENGTH_LONG).show();

                }

}

 

/**

 * To generate the HTML page

 * @return the page that displays the forecast

 */

private String generatePage() {

                StringBuffer bufResult = new StringBuffer(getResources().getString(R.string.html_head));

                String tempLine;

                for (Forecast forecast : forecasts) {

tempLine=String.format(getResources().getString(R.string.html_loop),forecast.getDay(),forecast.getLowTemp(),forecast.getHighTemp(),forecast.getIcon());

                               bufResult.append(tempLine);

                }

                bufResult.append(getResources().getString(R.string.html_foot));         

                return bufResult.toString();

}

 

/** This methode build the Forecast correctly using the raw xml stream */

private void buildForecasts(String raw) throws ParserConfigurationException, Exception,

                               Exception {

                // Using the DOM API: Create the document builder

                DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

                // Create the Dom Document by parsing the HTTP response

                Document doc = builder.parse(new InputSource(new StringReader(raw)));

                // Load the nodeList associated to the forcast_condition nodes

                NodeList forecastList = doc.getElementsByTagName("forecast_conditions");

                // Browse those nodes and build the associated forcast by reading the Xml

                for (int i = 0; i < forecastList.getLength(); i++) {

                               Element currentFore = (Element) forecastList.item(i);

                               // The day of the week

                               String day = currentFore.getElementsByTagName("day_of_week").item(0).getAttributes()

                                                               .item(0).getNodeValue();

                               // The low temperature

                               String lowTemp = currentFore.getElementsByTagName("low").item(0).getAttributes()

                                                               .item(0).getNodeValue();

                               // The high temperature

                               String highTemp = currentFore.getElementsByTagName("high").item(0).getAttributes()

                                                               .item(0).getNodeValue();

                               // The associated icon

                               String icon = currentFore.getElementsByTagName("icon").item(0).getAttributes().item(0)

                                                               .getNodeValue();

                               // Build the associated forcast

                               Forecast f = new Forecast();      

                               f.setDay(day);

                               f.setLowTemp(lowTemp);

                               f.setHighTemp(highTemp);

                               f.setIcon(icon);

                               // add it to the forcasts list

                               forecasts.add(f);

                }

}

 

/** The forcast object definition **/

 

class Forecast {

                String day = "";

                Integer lowTemp = null;

                Integer highTemp = null;

                String iconUrl = "";

 

                String getDay() {

                               return (day);

                }

                void setDay(String day) {

                               this.day = day;

                }

                Integer getLowTemp() {

                               return (lowTemp);

                }

                void setLowTemp(String temp) {

                               this.lowTemp = (int) ((Float.parseFloat(temp) - 32) * 5 / 9);

                }

                Integer getHighTemp() {

                               return (highTemp);

                }

                void setHighTemp(String temp) {

                               this.highTemp = (int) ((Float.parseFloat(temp) - 32) * 5 / 9);

                }

 

                String getIcon() {

                               return ("http://www.google.com" + iconUrl);

                }

                void setIcon(String iconUrl) {

                               this.iconUrl = iconUrl;

                }

}

Et le fichier des chaînes de caractères associé :

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <string name="hello">Hello World, WebServiceAccessTuto!</string>

    <string name="app_name">WebServiceAccessTuto</string>

                <!--The top of the web page should include the content web-->

                <string name="html_head">&lt;html>&lt;body>&lt;table>&lt;tr>&lt;th width=\"50%\">Jour&lt;/th>&lt;th>Basse&lt;/th>&lt;th>Haute&lt;/th>&lt;th>Tendance&lt;/th>&lt;/tr></string>

                <string name="html_loop">&lt;tr>&lt;td align=\"center\">%1$s&lt;/td>&lt;td align=\"center\">%2$s&lt;/td>&lt;/td>&lt;td align=\"center\">%3$s&lt;/td>&lt;td>&lt;img src=\"%4$s\">&lt;/td>&lt;/tr></string>

                <string name="html_foot">&lt;/table>&lt;/body>&lt;/html></string>

</resources>

Vous remarquerez que pour la gestion des balises Html entrantes, les < ont été remplacés par des &lt (Cf. 6.1.1.1 Le cas des chaînes de caractères HTML).

1.1.2.4       D’autres exemples

http://jahbromo.blogspot.com/2010/07/android-connection-une-base-distante.html

http://developerlife.com/tutorials/?p=288