Android,
A Complete Course, From
Basics to Enterprise Edition
Android, A Complete Course, From Basics to Enterprise
Edition
1 Communiquer avec l’extérieur de l’activité
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 :
La
propriété intellectuelle du code appartient à :
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
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);
}
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.
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);
}
}
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).
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é.
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
}
}
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);
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"><html><body><table><tr><th width=\"50%\">Jour</th><th>Basse</th><th>Haute</th><th>Tendance</th></tr></string>
<string name="html_loop"><tr><td align=\"center\">%1$s</td><td
align=\"center\">%2$s</td></td><td align=\"center\">%3$s</td><td><img
src=\"%4$s\"></td></tr></string>
<string name="html_foot"></table></body></html></string>
</resources>
Vous remarquerez que pour la gestion des
balises Html entrantes, les < ont été remplacés par des < (Cf. 6.1.1.1 Le cas des chaînes de caractères HTML).
http://jahbromo.blogspot.com/2010/07/android-connection-une-base-distante.html