Inhaltsverzeichnis
Cient-Server-Kommunikation von Android (Client) zu Java (Server) via http
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 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:
<uses-permission android:name="android.permission.INTERNET" />
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<String> 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<String> aList = new ArrayList<String>(); ListView liste = (ListView)findViewById(R.id.liste); listAdapter = new ArrayAdapter<String>(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<String, Void, String> { /** * ArrayAdapter zur Ausgabe der Namen in der ListView auf der GUI */ private ArrayAdapter<String> ausgabe; public RetrieveHttpTask(ArrayAdapter<String> 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<String> liste = Arrays.asList(result.split(";")); ausgabe.clear(); ausgabe.addAll(liste); ausgabe.notifyDataSetChanged(); super.onPostExecute(result); } }