====== Cient-Server-Kommunikation von Android (Client) zu Java (Server) via http ====== {{ :programmieren:java:android:httpclient:uebersicht.png|}} Im folgenden wird das Gerüst der Kommunikation anhand der wichtigsten Code-Fragmente beschrieben. Rechts sehen Sie eine Übersicht der wesentlichen Komponenten. Der Client (Android-Handy) schickt einen http-GET-Request an den Server. Dieser schickt eine strichpunktseparierte Liste zurück, die der Client in einer ListView anzeigt. ===== Serverseitige Implementierung ===== Auf dem Server werden die jar-Dateien der [[http://download.eclipse.org/jetty/|Jetty-Bibliothek]] in den Klassenpfad aufgenommen. Für die Jetty-Version 9.1.1 sind dies folgende Jars: \\ * mysql-connector-java-5.1.29-bin.jar * jetty-server-9.1.1.v20140108.jar * jetty-util-9.1.1.v20140108.jar * servlet-api-3.1.jar * jetty-http-9.1.1.v20140108.jar * jetty-io-9.1.1.v20140108.jar Die main-Methode instanziert ein MyServer-Objekt: import server.MyServer; public class Main { public static void main(String[] args) { /** * Webserver instanzieren und starten */ MyServer server = new MyServer(); } } Dieses wiederum instanziert und startet einen Embedded Jetty-Webserver, der auf Port 8080 horcht: import org.eclipse.jetty.server.Server; public class MyServer { public MyServer(){ /** * Instanziert einen Jetty-Webserver, der später auf Port 8080 horchen soll */ Server server = new Server(8080); /** * Bei jedem Request wird vom Webserver die handle-Methode der Handler-Klasse MyHandler aufgerufen. */ server.setHandler(new MyHandler()); try { /** * Starten des Webservers */ server.start(); server.join(); } catch (Exception e) { e.printStackTrace(); } } } Jeder http-Request führt zur Aufruf der Methode ''MyHandler.handle'' in einem eigenen Thread. Diese Methode gibt über das Response-Objekt abhängig vom Wert des per GET-Request übergebenen do-Parameters eine strichpunktseparierte Liste an den Client zurück: import java.io.IOException; import java.util.Calendar; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; public class MyHandler extends AbstractHandler { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); baseRequest.setHandled(true); /** * Je nach Wert des GET-Parameters do sollen unterschiedliche Daten zurückgegeben werden */ String befehl = request.getParameter("do"); String antwort = ""; switch (befehl) { case "getteilnehmer": antwort = getTeilnehmer(); break; default: break; } response.getWriter().println(antwort); } private String getTeilnehmer() throws Exception { String antwort = Calendar.getInstance().getTime().toString() + ";"; // // antwort += "Martin Pabst;Regina Jasper-Loreck;Max Muster;Frieda Fleißig"; // // System.out.println(antwort); // // return antwort; try { // This will load the MySQL driver, each DB has its own driver Class.forName("com.mysql.jdbc.Driver"); // Setup the connection with the DB connect = DriverManager.getConnection("jdbc:mysql://localhost/dbkurse", "root", null); // Statements allow to issue SQL queries to the database statement = connect.createStatement(); // Result set get the result of the SQL query resultSet = statement .executeQuery("select * from teilnehmer"); // writeResultSet(resultSet); while (resultSet.next()) { String name = resultSet.getString("Name"); String vorname = resultSet.getString("Vorname"); antwort = antwort + name + " " + vorname + ";"; } } catch (Exception e) { throw e; } finally { close(); } return antwort; } private void close() { try { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } if (connect != null) { connect.close(); } } catch (Exception e) { } } } ===== Clientseitige Implementierung ===== Im Manifest des Android-Projekts muss folgende Berechtigung vergeben werden: {{ :programmieren:java:android:httpclient:client-layout.png|Client-Layout}} Das Layout des Client-Programms ist rechts zu sehen. Folgende Ids sind vergeben: * Button: buttonVerbinden * ListView: liste \\ Hier der Code der ''MainActivity'': import java.util.ArrayList; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends Activity { private ArrayAdapter listAdapter; // Hält die Daten des ListView @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /** * Im ListView wird eine - initial leere - Liste von Strings angezeigt: */ ArrayList aList = new ArrayList(); ListView liste = (ListView)findViewById(R.id.liste); listAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1 , aList); liste.setAdapter(listAdapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } /** * * Ereignishandler für Klick auf den Button "Verbinden": * * @param view */ public void verbinden(View view){ /** * Der Http-Request ist blockierend. Damit er nicht die GUI lahmlegt, * muss er in einem eigenen Thread ausgeführt werden: */ RetrieveHttpTask task = new RetrieveHttpTask(listAdapter); task.execute(); } } Da der Http-Request blockierend ist, darf er nicht im UI-Thread ausgeführt werden. Wir benutzen daher einen ''AsyncTask'', um ihn in einem separaten Thread zu starten. ''AsyncTask'' stellt darüber hinaus die Methode ''opPostExecute'' bereit, die nach Ende von ''doInBackground'' automatisch aufgerufen wird und **garantiert** in UI-Thread läuft, so dass darin wieder GUI-Ausgaben stattfinden dürfen: package com.example.webclient; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.os.AsyncTask; import android.util.Log; import android.widget.ArrayAdapter; public class RetrieveHttpTask extends AsyncTask { /** * ArrayAdapter zur Ausgabe der Namen in der ListView auf der GUI */ private ArrayAdapter ausgabe; public RetrieveHttpTask(ArrayAdapter aliste){ this.ausgabe = aliste; } @Override protected String doInBackground(String... params) { String result = "Fehler!"; HttpClient httpclient = new DefaultHttpClient(); /** * Request-Objekt vorbereiten. 10.36.15.207 muss durch die Adresse * des Servers ersetzt werden. */ HttpGet httpget = new HttpGet("http://10.36.15.207:8080?do=getteilnehmer"); HttpResponse response; try { /** * Hier wird der Request ausgeführt. * Bem.: Der Aufruf blockiert, bis der Server antwortet. */ response = httpclient.execute(httpget); Log.i("Response",response.getStatusLine().toString()); HttpEntity entity = response.getEntity(); if (entity != null) { /** * Wir lesen die Antwort des Servers (ohne http-Header!) in * den StringBuilder total ein. */ InputStream instream = entity.getContent(); BufferedReader r = new BufferedReader(new InputStreamReader(instream, "UTF-8")); StringBuilder total = new StringBuilder(); String line; while ((line = r.readLine()) != null) { total.append(line); } instream.close(); result = total.toString(); } } catch (Exception e) { Log.i("Response-Exception", e.toString()); } /** * Nach den return wird automatisch onPostExecute im UI-Thread aufgerufen. */ return result; } @Override protected void onPostExecute(String result) { /** * Die einzelnen Namen werden der strichpunktseparierten Liste entnommen und * einzeln der ListView hinzugefügt: */ List liste = Arrays.asList(result.split(";")); ausgabe.clear(); ausgabe.addAll(liste); ausgabe.notifyDataSetChanged(); super.onPostExecute(result); } }