Schlagwort-Archive: Android

[Android] Fehler bei Nutzung von Commons Lang 2.5

Wer bei der Android-Entwicklung auf die Unterstützung von Commons Lang nicht verzichten will, und besonders den ReflectionToStringBuilder nutzen möchte, darf nicht eine Version <3.0 verwenden. In meinem Fall hatte ich die Version 2.5 genutzt (weil durch ein anderes Projekt referenziert) und mit folgenden Exceptions bei der Nutzung des ReflectionToStringBuilder zu kämpfen:

java.lang.ExceptionInInitializerError
java.lang.StringIndexOutOfBoundsException

Der Fehler wurde mit Version 3.0 beseitigt.

[Android] RESTEasy und Android

Wurde eine REST-Schnittstelle auf Server-Seite mit RESTEasy implementiert, gab es bisher keine wirkliche Möglichkeit, mit RESTEasy den vorhandenen Code auch auf Android-Seite zu nutzen. RESTEasy funktionierte bisher nicht unter Android. Thomas Diesler hat glücklicherweise ein Projekt auf github (https://github.com/tdiesler/resteasy-mobile) gestartet, welches diesen Teil nachimplementiert. Er hat hierzu auch einen Blogpost verfasst.

Nachfolgende die Kurzform der notwendigen Schritte. Ich habe den Code in drei Projekte aufgeteilt:

  • REST-Interface und Transferobjekte
    In diesem Projekt werden die Transferobjekte und das REST-Interface implementiert. Transferobjekte sind die Klassen, die die Daten zwischen Server und Client kapseln. Wie der Name REST-Interface schon vermuten lässt, wird damit die Schnittstelle zwischen Client und Server definiert (aber noch nicht implementiert). Nutzt Maven.
  • REST-Server-Implementierung
    Die Implementierung der Backend-Logik. Benutzt das REST-Interface und die Transferobjekte. Nutzt Maven.
  • REST für Android
    Die Implementierung der Android-Client-Seite. Benutzt das REST-Interface und die Transferobjekte.

Für das REST-Interface und Transferobjekte wird folgende Dependency in der pom.xml eingetragen:

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>1.2.1.GA</version>
</dependency>

Für die REST-Server-Implementierung werden folgende Dependencies in der pom.xml eingetragen:

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>1.2.1.GA</version>
</dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxb-provider</artifactId>
 <version>1.2.1.GA</version>
 </dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>jaxrs-api</artifactId>
 <version>1.2.1.GA</version>
</dependency>

<dependency>
 <groupId>net.sf.scannotation</groupId>
 <artifactId>scannotation</artifactId>
 <version>1.0.2</version>
</dependency>

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jackson-provider</artifactId>
 <version>2.3.1.GA</version>
</dependency>

<!-- Reference to REST-Interface-Project -->
<dependency>
 <groupId>de.yellowshoes.rest</groupId>
 <artifactId>restinterface</artifactId>
 <version>0.0.1-SNAPSHOT</version>
</dependency>

Auf Android-Seite ist kein Maven im Einsatz. Folgende Libraries werden im Buildpath mit aufgenommen:

  • Jackson Core ASL 1.6.3
  • Jackson JAXRS 1.6.3
  • Jackson Mapper ASL 1.6.3
  • Jackson XC 1.6.3
  • JAXRS API 2.2.1 GA von RESTEasy
  • RESTEasy Mobile 1.0.0

Nachfolgend ein einfaches Beispiel, wie eine konkrete Implementierungen aussehen kann:

REST-Interface

Zuerst die Transferklasse. Hier ein Objekt Book. Ein simples POJO, mehr nicht.

public class Book
 {
 private String titel;
 private String author;

 public String getTitel()
 {
 return titel;
 }

public void setTitel( String titel )
 {
 this.titel = titel;
 }

public String getAuthor()
 {
 return author;
 }

public void setAuthor( String author )
 {
 this.author = author;
 }
}

Nun das REST-Interface. Die @Path-Annotation definiert jeweils, an welche Url die Klasse/die Methode gebunden wird. Hier wird das komplette Interface an /api gebunden, die Methode (durch die Vererbung) an /api/book. Mit den Annotations @Consumes und @Produces wird festgelegt, was für MediaTypes verarbeitet werden können und was zurückgeschickt werden kann. Hier wird jeweils JSON definiert.

@Path( "/api" )
@Consumes( MediaType.APPLICATION_JSON )
@Produces( MediaType.APPLICATION_JSON )
public interface RestResourceInterface
 {
 @GET
 @Path( "/book" )
 public Book getBook();
 }

REST-Implementierung

Auf Server-Seite wird nur eine Klasse benötigt.

public class RestResource implements RestResourceInterface
 {
 public Book getBook()
  {
  Book book = new Book();
  book.setAuthor( "Hans Müller" );
  book.setTitel( "REST gut" );
  return book;
  }
}

REST-Android

Auch auf Android-Seite muss nicht mehr viel gemacht werden. Hier eine einfache Client-Implementierung. Das wichtigste ist die Methode getClient(), die über die ProxyFactory die Schnittstellen-Implementierung initialisiert. Der Code sollte ansonsten selbsterklärend sein.

public class ResteasyClient implements RestResourceInterface
 {
 RestResourceInterface client = null;

 @Override
 public Book getBook()
  {
  RestResourceInterface client = getClient();
  Book book = client.getBook();
  return book;
  }

 private RestResourceInterface getClient()
  {
  String requestURI = "http://192.168.11.86:8080/restest/";

  if( client == null )
   {
   BasicHttpParams params = new BasicHttpParams();
   HttpProtocolParams.setVersion( params, HttpVersion.HTTP_1_1 );
   HttpProtocolParams.setContentCharset( params, HTTP.DEFAULT_CONTENT_CHARSET );
   HttpProtocolParams.setUseExpectContinue( params, false );
   client = ProxyFactory.create( RestResourceInterface.class, requestURI, new ApacheHttpClient4Executor( params ) );
   }
  return client;
  }
 }

 

[Android]: Fehler beim Deployment in Eclipse-Console

Bekommt man in der Eclipse-Console folgende Meldungen angezeigt:

[2011-02-14 15:35:18 - ddms]transfer error: Too many open files [2011-02-14 15:35:18 - Device]Error during Sync: Too many open files [2011-02-14 15:35:22 - ddms]ADB rejected shell command (ls -l /): closed

Dann am Besten den adb-Server neu starten:

 ./adb start-server
adb server is out of date.  killing…
* daemon started successfully *

[Android]: Fehler Reporting in eigene Apps einbauen

Seit Android 2.2 wird ein Fehlerreporting „nativ“ unterstützt. Das heisst, der Benutzer kann einen Fehlerreport an den Entwickler schicken lassen, wenn die App abgestürzt ist. Dies geht allerdings nur, wenn die App über den Google Market verbreitet wird. Da wir bei Dailyplaces gerade eine Alpha-Phase für unsere Android-App am Laufen haben und die Version nicht über den Google Market verbreiten wollten, musste eine Alternative her. Zuerst wollte ich eine eigenen Implementierung eines UncaughtExceptionHandlers beginnen, habe mich dann aber noch mal etwas umgeschaut und bin schliesslich auf ACRA gestossen.
Das besondere an ACRA ist, dass man sich nicht um client- oder serverseitige Implementierungen kümmern muss. Die Fehlerreports werden einfach in ein Google Docs-Sheet eingefügt. Wenn man die Lösung mit Google Docs nicht nutzen möchte kann man aber auch gerne einen eigenen Serverdienst dafür umsetzen.
Eine weitere Besonderheit ist in meinen Augen die gute Dokumentation. Das Einbinden in die eigene App hat out-of-the-box funktioniert, da alle Schritte sauber dokumentiert werden.

[Android]: Aufrufen einer Activity und dabei Daten mit übergeben

Beim Aufrufen einer Activity kann man Daten übergeben, die in der aufgerufenen Activity ausgewertet werden können. Dies können primitive Datentypen sein oder Instanzen von eigenen Klassen.

Der Aufrufer muss eine Intent-Instanz mit den entsprechenden Informationen befüllen und mit diesem Intent dann die Activity aufrufen.

01.   Intent intent = new Intent( context, YourActivityClass.class );
02.   YourContainerClass data = new YourContainerClass( „some usefull data“ );
03.   intent.putExtra( „yourdata“, data );
04.   startActivity( intent );

In Zeile 01 wird eine Intent-Instanz erzeugt. Die Activity-Klasse, die aufgerufen werden soll, heisst hier YourActivityClass.

In Zeile 02 wird von einer Klasse eine Instanz erzeugt, die als Datencontainer dient und die benötigten Informationen vom Aufrufer an YourActivityClass übergibt. Im Beispiel wird davon ausgegangen, dass der String „some usefull data“ innerhalb der Container-Instanz gespeichert wird und dadurch an YourActivityClass übergeben wird.

In Zeile 03 wird die Instanz der Container-Klasse in die Intent-Instanz übergeben. Die Container-Klasse wird dabei unter dem Schlüssel „yourdata“ abgelegt.

In Zeile 04 wird schliesslich die Activity (hier also YourActivityClass) aufgerufen.

Um die Informationen auf Seiten der aufgerufenen Activity auszulesen, wird folgender Code genutzt:

01.    protected void onCreate( Bundle savedInstanceState )
02.    {
03.        super.onCreate( savedInstanceState );
04.        Intent intent = getIntent();
05.        YourContainerClass data = ( YourContainerClass )intent.getExtras().get( „yourdata“ );
06.        // doing some other usefull stuff
07.    }

In Zeile 01 wird der Methodenaufruf von onCreate() gezeigt, möglich wäre aber z.B. auch onStart() oder onResume().

In Zeile 04 wird das Intent-Objekt geholt, welches zum Aufruf der Activity genutzt wurde.

In Zeile 05 wird die Container-Klasse aus dem Intent-Objekt geholt, welches auf Seiten des Aufrufer-Codes unter dem Schlüssel „yourdata“ in dem Intent-Objekt abgelegt wurde.

[Android]: Bilder cachen bei ListViews