Android: WebService mit Android und PHP

In diesem Artikel möchte ich eine kurze Einführung geben, wie man einer Android-App den Zugriff auf einen WebService ermöglichen kann.

Da ich mich derzeit mit der Entwicklung einer App für den privaten Gebrauch beschäftige wollte ich erstmal eine Demo-App entwickeln, welche exemplarisch funktioniert.
Folgende Gegebenheiten:

  • Webserver mit PHP (unter Ubuntu 9.10)
  • PHP-WebService-Bibliothek = nuSoap 0.9.5
  • Andriod-SDK (Eclipse 3.7.1 Indigo, ADT 15.0.0)
  • Android-Deployment-Version 2.1

Der Artikel gliedert sich in zwei Teile:

  1. Aufsetzen des SOAP-WebService mit PHP (unter Zuhilfenahme von nuSoap),
  2. Implementierung einer Android-Client-App, welche den WebService konsumiert (unter Zuhilfenahme von ksoap2-android)

Der Sourcecode mit dem Demo sowie hilfreiche Links befinden sich am Ende des Dokuments.

WebService mit PHP
Ab PHP5 ist zwar SOAP-WebService als Feature implementiert, aber bei meinen Recherchen stellte ich fest, dass man das WSDL-File selber erstellen muß. Das stellt eine zusätzlich vermeidbare Fehlerquelle dar. Daher fiel meine Wahl auf nuSoap welches das WSDL-File automatisch auf Grund der Code-Deklaration erstellt.

nuSoap kann frei bei SourceForge heruntergeladen werden.

Nach dem Download von nuSoap und dem Entpacken in den Unterordner lib/nusoap-0.9.5 kann man loslegen. Der Servercode ist bewußt einfach gehalten und ich habe mich dazu auch eines Beispiels orientiert von diesem HowTo.

Datei index.php

<?php

$libpath = "lib";
$libsoap = $lib."/nusoap-0.9.5/lib";

function suche($suchbegriff)
{
    $ergebnisse = array();
    $ergebnisse[0] = $suchbegriff."0";
    $ergebnisse[1] = $suchbegriff."1";
    // mustn't contain any brackets like "[]", because of ksoap2-android-client...
    return $ergebnisse;
}

require_once($libpath.$libsoap.'/nusoap.php');
$server = new soap_server;
$server->configureWSDL('suche');

// register "array" type
$server->wsdl->addComplexType('ArrayOfString',
                              'complexType',
                              'array',
                              '',
                              'SOAP-ENC:Array',
                              array(),
                              array(
                                  array('ref' => 'SOAP-ENC:arrayType',
                                  'wsdl:arrayType' => 'xsd:string[]'
                                )
                              ),
                              'xsd:string'
                             );

// register the lookup service
$server->register('suche',
array('inputString' => 'xsd:string'),
array('return'      => 'tns:ArrayOfString'));

$HTTP_RAW_POST_DATA = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
$server->service($HTTP_RAW_POST_DATA);

?>

Verzeichnisstruktur:

/AndroidPHPWebserviceDemo_P/
|– index.php
|– lib
|   |– nusoap-0.9.5
|       |– lib
|       |   |– nusoap.php

Der WebService hat eine Methode “suche” mit einem Parameter “sucheBegriff”. Rückgabe ist ein Array ($ergebnisse) mit zwei Elementen.

Getestet werden kann der Service mit der URL

http://<IP oder DNS des Rechers>AndroidPHPWebserviceDemo_P/index.php

Wer auf Nummer sicher gehen will, kann den WebService mit SoapUI testen – dazu einfach ein neues Projekt erstellen und die WSDL-URL einfügen:

Android-Client
Ich gehe hier davon aus, dass die Entwicklungsumgebung für Android bereits installiert ist und funktioniert.

Für die Demo habe ich ein neues Android-Projekt angelegt mit

File -> New Project -> Android -> Android Project

Project Name: AndroidPHPWebservice_A
Build Target: Android 2.1
Application Name: AndroidPHPWebservice_A
Package Name: de.patschwork.AndroidPHPWebservice

ADT generiert daraufhin eine bereits lauffähige “Hello, World” App.
Das Rohgerüst muß man nun ausbauen.

Das Android-Framework bietet derzeit von Haus aus keinen nativen Client für SOAP-Webservices. Daher verwende ich die ksoap2-android Bibliothek (Download hier). Dies ist eine speziell für das Google-Mobile-Betriebssystem angepaßte Java-Komponente um WebService zu konsumieren.

Nachdem die jar-Datei heruntergeladen wurde muß diese noch in das Projekt eingebunden werden.
Dazu in den Project-Properties, unter “Java Build Path” die jar-Datei unter dem Tab Libraries als “External JARs” hinzufügen:

Im Anschluß meckert Eclipse etwas herum. Dies kann man abstellen, indem man den Ordner für den Build-Path noch konfiguriert:

Das waren die Vorbereitungen, aber nun zum Code.
In die Datei src/AndroidPHPWebserviceDemo_AActivity.java fügen wir nun ein:

package de.patschwork.AndroidPHPWebservice_A;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class AndroidPHPWebserviceDemo_AActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText(test());
        setContentView(tv);
    }

    private static final String SOAP_ACTION = "http://patsch2/test/AndroidPHPWebserviceDemo/index.php/suche";
    private static final String METHOD_NAME = "suche";
    private static final String NAMESPACE = "http://patsch2/soap/suche/";
    private static final String URL = "http://patsch2/test/AndroidPHPWebserviceDemo/index.php";

    public String test() {
        try {
            SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
            request.addProperty("sucheRequest", "Test aus Android: ");

            SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
            envelope.setOutputSoapObject(request);
            HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
            androidHttpTransport.call(SOAP_ACTION, envelope);

            Object result = envelope.getResponse();

            return resultToStrArray(result)[1];

        } catch (Exception e) {
            e.printStackTrace();
            return e.toString();
        }
    }

    public String[] resultToStrArray(Object wsResult)
    {
        String wsResultStr = wsResult.toString();

        if (wsResult!=null)
        {
            // Check if result is an Array from nuSoap
            if (wsResultStr.contains(",")&amp;&amp;(wsResultStr.contains("[")))
            {
                String temp = wsResultStr;
                temp=temp.replace("[", "");
                temp=temp.replace("]", "");
                return temp.split(",");
            }
        }
        return null;
    }

}

Die entsprechend grünen static’s zu Beginn Stellen entsprechend der lokalen Konfiguration auszutauschen.

Die Zeile request.addProperty(“sucheRequest”, “Test aus Android: “); ist der entsprechende Parameter für die Methode “suche”.
Jetzt kommt eine Unschönheit ans Tageslicht: In dem PHP-Service haben wir als Rückgabetyp ein komplexen Datentyp deklariert – Array. In der Standardversion kann ksoap2 diesen nicht korrekt interpretieren. Das Ergebniss kommt zwar an, aber würde folgendermaßen als String herauskommen: [Test aus Android: 0, Test aus Android: 1]
Aus diesem Grund kommt die Methode public String[] resultToStrArray(Object wsResult) zum Einsatz, welche aus dem result ein String-Array erzeugt. Schön ist anders, aber ist ja auch nur eine Demo ;-)

Und schon kann es losgehen. Deployen und in der Android-VM anzeigen.

Bumm. Fehler: java.net.SocketException: Permission denied

Aber die zweite Zeile gibt glücklicherweise eine Erklärung: Es fehlen Rechte. Das sind auch diese Rechte, welche als Hinweis erscheinen wenn man eine App aus dem Market installiert.
Um diesen Fehler zu vermeiden muß eine Zeile ergänzt werden in der AndroidManifest.xml:

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

Jetzt sollte einer erfolgreichen Ausführung nichts im Wege stehen:

Nachteile und Alternativen
Die Nachteile seien hier auch erwähnt. Wie bereits oben angedeutet ist im Standard ksoap2 nicht in der Lage komplexe Datentypen auszuwerten. Es gibt allerdings einen Patch, welche dies ermöglicht (siehe weiter unten).

Weiterhin muß man sagen, SOAP ist natürlich auch kein “Leichtgewicht”, durch das XML-Format entsteht viel Overhead, und grade bei schwachen Internet-Mobil-Verbindungen könnte dies zu Performance-Problemen führen.
Eine interessante Alternative könnte hier das “Hessian”-Protokoll bieten, welches auch für PHP und Android (und auch anderen zahlreichen Programmiersprachen) zu Verfügung steht.

Sourcecode:

AndroidPHPWebserviceDemo_P

AndroidPHPWebserviceDemo_A

Hilfreiche Links:

nuSoap Beispiel:
http://joergnapp.de/ein-einfacher-soap-server-in-php/

nuSoap Download:
http://sourceforge.net/projects/nusoap/files/

ksoap2-Android Download:
http://code.google.com/p/ksoap2-android/wiki/HowToUse

ksoap2-Beispiel mit Android:
http://android.amberfog.com/?p=45

java.net.socket-Error: Permission denied…:
http://stackoverflow.com/questions/2378607/what-permission-do-i-need-to-access-internet-from-an-android-application

ksoap2-patch:
http://stackoverflow.com/questions/4951333/serialize-an-array-of-ints-to-send-using-ksoap2
http://people.unica.it/bart/2010/12/03/ksoap2-patch-for-user-defined-objects-and-arrays/

Nachtrag:

Soeben noch einen interessanten Artikel gefunden zur App-Entwicklung: http://www.kammerath.net/android-app-programmieren.html

Powered by ScribeFire.

One thought on “Android: WebService mit Android und PHP

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *


8 − eins =

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>