andforge.net

The android Blog! News, Tutorials and Reports!

Tutorial: Android Spieleentwicklung – Praxis (1)Tutorial

Posted by Johannes Borchardt August - 27 - 2010 - Friday ADD COMMENTS

So, weiter geht’s mit der Android Spieleentwicklung. Eigentlich wollte ich nur zwei Tutorials schreiben- Theorie und Praxis- habe mich jetzt aber doch entschlossen, den Praxisteil deutlich umfangreicher zu gestalten. Dabei werde ich mit Euch zunächst die Grundlagen für ein Canvas basiertes 2D-Spiel schaffen und anschließend darauf ein Spiel aufbauen. Dabei werden wir eine Client- und eine Serverseite, basierend auf der Google App Engine, implementieren, Das Tutorial wird sich deshalb noch über einige Wochen hin strecken.

Heute implementieren wir erstmal die praktische Grundlage und schreiben dafür eine abstrakte Basisactivity, die zwei Threads verwaltet (einen für das Rendering, einen für die Simulation) und Touchevents und Beschleunigungen wahr nimmt. Außerdem entwerfen wir ein Listenerkonzept, mit dem die Klasse einfach genutzt werden kann.

Für denjenigen der oder diejenige die nicht weiß, worum es bei den im vorherigen Satz genannten Dingen geht, zumindest theoretisch, und/oder noch keine Kenntnisse in Java besitzt, ist dieses Tutorial wahrscheinlich nicht sehr hilfreich. Wer anderer Meinung ist kann’s trotzdem probieren. Los geht’s!

Zunächst einmal grundlegendes zum Android-Stil:

  • Member Variablen (globale Variablen) werden mit einem m am anfang geschrieben, also zum Beispiel mVariable.
  • Statische Variablen mit einem s
  • Felder (static final …) werden groß geschrieben und Wörter mit Unterstrichen (_) getrennt, wie sonst auch in Java üblich.
  • Methoden werden in der Zeile ihrer Deklaration geöffnet (void testMethode(){ … anstatt void testMethode

{ …).

  • final ist ein gutes Wort wenn eine Variable nicht nochmal verändert werden soll.
  • Nicht private Variablen sollten mit Javadoc dokumentiert sein, Methoden auch (wie auch sonst in Java).
  • Außer die Benennung ist absolut selbsterklärend, das ist aber selten der Fall (auch bei Gettern und Settern).
  • Methodenzugriffe kosten (Rechen) Zeit und die ist auf Mobilen Geräten knapp.
  • Der Garbagecollector sollte Während des Spielbetriebs so selten angeworfen werden müssen wie möglich, sonst kann es zu Rucklern kommen.

Nachdem das gesagt ist geht’s gleich los:

Zunächst einmal erstellen wir eine neue abstrakte Klasse die von Activity ableitet, ich habe sie passenderweise GameActivity genannt.

public abstract class GameActivity extends Activity

Wie bei Activities üblich überschreiben wir onCreate(). Dabei initialisieren wir zunächst eine member Variable vom Typ SurfaceView, auf die wir später alles malen werden. Anschließend holen wir uns von der SurfaceView über getHolder() den zugrunde liegenden SurfaceHolder. Über diesen ist es uns möglich, die SurfaceView zu verändern. Anschließend fügen wir dem Holder die eigene Klasse als Callback hinzu (addCallback()). Dazu müssen wir das Interface SurfaceView.Callback implementieren, was uns zu den Methoden surfaceChanged(), surfaceCreated() und surfaceDestroyed() zwingt. Die Namen sind ziemlich selbsterklärend. In surfaceCreated() und surfaceDestroyed() werden wir weiter unten etwas hineinschreiben, surfaceChanged(), was zum Beispiel aufgerufen wird wenn das Handy gedreht wird, behandeln wir nicht. Wir setzen die SurfaceView-Instanz als Content View.

Als nächstes erstellen wir in onCreate() zwei Threads, einen für die Simulation, einen für das Rendering:

        mRenderingThread = new Thread(new Runnable(){
        	public void run() {
        		render();
        	}
        }, "Rendering Thread");        

        mSimulationThread = new Thread(new Runnable(){
        	public void run() {
        		simulate();
        	}
        }, "Simulation Thread");

Das sind die beiden im Theoriekapitel beschriebenen Threads. Gar nicht so kompliziert. Wie Ihr an den Namen erkennen könnt handelt es sich bei den Threads um globale Variablen, da wir sie später aus einer anderen Methode heraus wieder beenden können wollen. Das ist erstmal alles was wir in onCreate() machen.

Jetzt zum Listener-Konzept. Wir benötigen zwei Interfaces, eines für die Simulation und eines für das Rendering.

Dem SimulationListener geben wir drei Methoden: initializeSimulation(), simulationIteration(float delta) und isInitializingSimulation(). Die erste dieser Methoden dient dazu, alles zu laden was für die Simulation notwendig ist, Daten vom Server oder aus einer Datenbank zum Beispiel. Wenn das Laden abgeschlossen ist muss isInitializingSimulation() false zurückgeben. So weiß unsere abstrakte Basisactivity dass das Laden der Simulation abgeschlossen ist und mit dem Durchlaufen der Simulation begonnen werden kann. Dafür wird simulationIteration(float delta) genutzt. Delta ist die Zeit in Sekunden, die seit der letzten Simulationsiteration vergangen ist. So kann sichergestellt werden dass sich alle Spielgegenstände immer gleich schnell bewegen, unabhängig von der geschwindigkeit mit der die Simulationsiterationen durchlaufen werden und auch unabhängig von der Zahl der Frames pro Sekunde. Diese Technik nennt sich Frame Rate Independent Movements, ein Name der hier nicht so recht passt; näheres kann hier nachgelesen werden.

Der RenderingListener, auch ein Interface, erhält die Methoden render(Canvas canvas), initializeRendering() und isInitializingRendering(). Darüber hinaus bekommt er noch die Methoden renderSplash(Canvas canvas) und initializeSplash(). Die ersten drei Methoden machen das Gleiche wie der SimulationListener auch. render(Canvas canvas) bekommt dabei ein Canvas-Objekt übergeben, auf das der Listener dann alle Spielgrafiken zeichnen kann. Die beiden letztgenannten Methoden sind dafür da, einen Splashscreen oder Ladebildschirm zu laden und zu zeichnen. initializeSplash() sollte dabei so schnell wie möglich durchlaufen werden damit der Nutzer nicht denkt dass das Spiel hängt. Sobald isInitializingRendering() und isInitializingSimulation() false zurück geben ist das Laden abgeschlossen und es kann zum Spielablauf mit render(Canvas canvas) übergegangen werden.

Um die Listener auch setzen zu können müssen wir auch noch jeweils einen Setter implementieren, was eigentlich kein Problem sein sollte.

Mit dem obigen gesagt ist auch schon klar, was die beiden Threadmethoden simulate() und render() machen. Fangen wir bei der Umsetzung mit simulate() an:

 private void simulate(){        
   if(mSimulationListener != null){
     mSimulationListener.initializeSimulation();
   }

   while(mMainLoopRunning){
     if(mSimulationListener != null
       && !mSimulationListener.isInitializingSimulation()
       && mRenderingListener != null
       && !mRenderingListener.isInitializingRendering()){
        mDeltaTime = (System.nanoTime() - mLastSimulationIteration)
          / 1000000000.0f;
        mLastSimulationIteration = System.nanoTime();
        mSimulationListener.simulationIteration(mDeltaTime);            
     }
   }
 }

Zuerst führen wir die Initialisierung der Simulation durch. Ist das erledigt geht die main Loop der Simulation los. Das aber nur, wenn auch alle für das Rendering notwendigen Dinge bereits geladen wurden. Wir wollen ja nicht dass das Spiel schon los geht bevor der User überhaupt irgend etwas sieht. In der main Loop messen wir die delta Time. Wie ihr seht benötigen wir dafür zwei weitere Member. Wir ziehen einfach von System.nanoTime() den Zeitstempel der letzten Iteration ab und teilen das Ergebnis durch eine Milliarde um von Nanosekunden auf Sekunden zu kommen. Nun setzen wir den Zeitstempel auf die aktuelle Zeit und rufen die simulationIteration(float delta) des SimulationListeners auf. So einfach ist das. Nun können Klassen die sich als SimulationListener bei unserer Basisactivity einhängen Entfernungen, Bewegungen, Kollisionen usw. in ihrer eigenen Methode simulationIteration(float delta) berechnen und das mit einer konstanten Geschwindigkeit, z.B. 100 Pixel pro Sekunde.

Machen wir mit der Methode render() weiter:

private void render(){
  if(mRenderingListener != null){ 
    mRenderingListener.initializeSplash();
  }
  new Thread(new Runnable(){
    @Override
    public void run() {
      if(mRenderingListener != null){
        mRenderingListener.initializeRendering();
      }
    }
  }).start();
  Canvas c;
  while(mMainLoopRunning){
    c = null;
    try {
      c = mSurfaceHolder.lockCanvas();
      synchronized (mSurfaceHolder) {
        if(mRenderingListener != null){
          if(mRenderingListener.isInitializingRendering()
           || mSimulationListener == null
           || mSimulationListener.isInitializingSimulation()){
            mRenderingListener.renderSplash(c);
        } else{
          mRenderingListener.render(c);
        }
      }
    }
  } finally {
    if (c != null) {
      mSurfaceHolder.unlockCanvasAndPost(c);
    }
  }                    

 //the fps calculation
 mFPSCount++;
 mTimeElapsed += (System.nanoTime() - mLastRenderingIteration)
 / 1000000000.0;
 if(mTimeElapsed >= 1){
   mFPS = mFPSCount;
   Log.d("GameActivity", "FPS: " + mFPS);
   mFPSCount = 0;
   mTimeElapsed = 0;
 }
 mLastRenderingIteration = System.nanoTime();
 }
}

Etwas umfangreicher, etwa 30 Zeilen, aber auch nicht so kompliziert. Das Vorgehen ist ähnlich wie bei der Simulation. Als erstes initialisieren wir den Splashscreen. Das sollte, wie erwähnt, möglichst zügig passieren damit niemand warten muss. Anschließend initialisieren wir das Rendering. Das machen wir nebenläufig machen damit unser Ladebildschirm schonmal angezeigt werden kann. Dann deklarieren wir ein Canvas-Objekt und steigen auch schon in die Rendering main Loop ein. Der Teil mit der Canvas, der jetzt kommt, ist übrigens größtenteils dem LunarLander Codebeispiel entnommen. Zuerst einmal holen wir uns von unserem SurfaceHolder die aktuelle Canvas und sperren diese. Mit dem Sperren hat es auf sich dass die Canvas, wenn sie nicht gesperrt ist, verändert werden kann, auch von Elementen die wir nicht in unserer Hand haben. So stellen wir also sicher dass am Ende auch das auf dem Bildschirm erscheint, was wir wollen. Nun, da wir uns in einem Thread befinden und nicht wollen dass ein anderer Thread uns einen Strich durch die Canvas macht, synchronisieren wir noch unseren SurfaceHolder und geben anschließend unsere Canvas an den RenderingListener weiter. Je nachdem, ob alle für das Spiel notwendigen Elemente bereits geladen sind oder nicht, kriegt entweder renderSplash() oder render() die Canvas zum Malen überreicht. Abschließend entsperren wir unser CanvasObjekt wieder und posten es mit unserem SurfaceHolder auf den Bildschirm. Das machen wir in einem finally-Block, damit unsere Oberfläche, falls es beim Zeichnen in einer der Renderingmethoden zu Exceptions kommt, nicht in einem inkonsistenten Zustand bleibt.
Das war auch schon alles was das Rendering angeht.
Abschließend sollen noch die Frames pro Sekunde gezählt werden.

Frames pro Sekunde (FPS) sagen aus wie viele komplette Bilder innerhalb einer Sekunde gezeichnet werden. Das menschliche Auge beginnt dabei ab etwa 24 FPS zu glauben, es handle sich um Bewegungen. Alles darunter ist zu langsam und sollte den Entwickler zum Nachdenken bringen. Zur Performanceoptimierung komme ich in einem anderen Kapitel. Die in Android maximale Anzahl von FPS liegt bei 61. Wenn man sein Spiel zwischen 50 und 61 FPS bringen kann, kann man sich schon sehr sicher sein dass es auf den meisten Geräten zuverlässig läuft (testen sollte man aber trotzdem).

Zurück zur Implementierung! In jeder Renderingiteration erhöhen wir ganz einfach einen Counter mFPSCount und zählen anschließend die seit der letzten Iteration vergangene Zeit in einer anderen Membervariable zusammen. Wenn diese zweite Variable größer gleich eins ist, ist eine Sekunde vergangen und die FPS können ausgegeben werden. Da man sie vielleicht auch noch anderswo als in der LogCat sehen will, habe ich auch noch einen Member mFPS erstellt, über den andere Klassen auf die aktuellen Frames pro Sekunde zugreifen können. Wir setzen den FPS-Counter und den Zeitnehmer zurück und halten den Zeitstempel der aktuellen Iteration fest. Fertig ist der Inhalt des rendering-Threads!
Würde man jetzt von der Activity ableiten, Listener einhängen und etwas schönes zeichnen wollen, würde nichts geschehen. Warum nicht? Ganz Klar: Die beiden Hauptthreads wurden ja noch gar nicht gestartet. Aber wo machen wir das am besten? Den simulation-Thread können wir schon in der onCreate()-Methode starten. Mit dem rendering-Thread ist das anders, der muss erst darauf warten dass die SurfaceView, auf der er zeichnen will, bereit ist. Da war doch was… Wir hatten ja das Interface SurfaceHolder.Callback implementiert, das uns eine Methode surfaceCreated() zur Verfügung stellt. Allem Anschein nach der richtige Punkt um das Rendering anzustoßen. Der Einfachheit halber habe ich an dieser Stelle auch den Simulationsthread gestartet. Wie Ihr, wenn Ihr aufmerksam wart, sicherlich bemerkt habt, gibt es in den main Loops der beiden Threads noch eine Variable mMainLoopRunning. Diese müssen wir also auch noch auf true setzen, bevor wir die Threads starten.
Jetzt können wir ein Spiel zum laufen bringen. Aber manchmal, auch wenn das Spiel noch so schön ist, will man es auch wieder beenden. Glücklicherweise stellt das Interface SurfaceHolder.Callback auch noch die Methode surfaceDestroyed() zur Verfügung, in der wir die beiden Threads stoppen können. Das sieht dann für den rendering-Thread so aus:

mMainLoopRunning = false;
boolean retry = true;
while (retry) {
  try {
    mRenderingThread.join();
    retry = false;
  } catch (InterruptedException e) {
  }
}

Wir stoppen einfach den rendering-Thread indem wir join() aufrufen. Wenn es dabei zu einer Exception kommt, versuchen wir es einfach nochmal, solange bis es gut geht. Mit dem Simulationsthread machen wir das Selbe, und schon können wir ein Spiel sowohl starten als auch beenden.
Jetzt haben wir alle Komponenten beisammen um eine Spiel zu zeichnen. Fehlt noch die Eingabe. Alle Geräte, die den Android Market nutzen wollen, müssen über mindestens einen Beschleunigungssensor und einen Touchscreen verfügen. Also kümmern wir uns darum, dass wir diese beiden sicheren Eingabemethoden nutzen können.

Um auf Touch Events reagieren zu können implementieren wir das Interface OnTouchListener, wodurch wir dazu gezwungen werden, die Methode boolean onTouch(MotionEvent event) zu implementieren. Den Listener hängen wir in unserer onCreate() – Methode in unsere SurfaceView ein. Nun wird onTouch() bei jedem Drücken auf unsere Spieloberfläche aufgerufen. Das MotionEvent das dabei übergeben wird, enthält dabei einige wichtige Informationen. Wir verarbeiten von diesen Informationen die Art und die Position des Events. Damit auch ableitende Klassen darauf zugreifen können, erstellen wir darum die drei Membervariablen mIsTouched, mTouchX und mTouchY.

Wenn unser MotionEvent (über die Methode getAction()) nun die Information beinhaltet, dass ein Drücken oder eine Bewegung eines Fingers auf dem Bildschirm stattgefunden hat, halten wir die Position fest und setzen mIsTouched auf true. Hat das Event die Action MotionEvent.ActionUp, so ist kein Finger mehr auf dem Bildschirm und wir setzen mIsTouched auf false. In code sieht das so aus (Getter und Setter nicht vergessen):

if(event.getAction() == MotionEvent.ACTION_DOWN
 || event.getAction() == MotionEvent.ACTION_MOVE){
  mTouchX = (int)event.getX();
  mTouchY = (int)event.getY();
  mIsTouched = true;
}

if( event.getAction() == MotionEvent.ACTION_UP ){
  mIsTouched = false;
}
return true;

Wenn mIsTouched true zurückgibt, befindet sich gerade ein Finger auf dem Bildschirm. Ist es false, befindet sich dort keiner. So können Bewegungen erkannt werden und Beispielsweise Objekte über das Spielfeld gezogen oder Pfade gezeichnet werden.

Nach den Touchevents bleibt uns nur noch eins zu tun, bevor wir mit unserer Basisactivity anfangen können Spiele zu schreiben. Wir sprechen die Beschleunigungssensoren an. Dafür brauchen wir mal wieder ein Interface um uns als Listener einzuhängen – ein Konzept, dass sich durch so ziemlich alles in Android durchzieht. Wir implementieren SensorEventListener und erhalten onAccuracyChanged() und onSensorChanged(SensorEvent event). Die erste dieser beiden Methoden ignorieren wir. Die zweite Methode wird aufgerufen, wenn der Beschleunigungssensor irgend etwas mitgekriegt hat. Wie schon zuvor enthält das Event die entsprechenden Informationen. Für das Weitergeben an ableitende Klassen erstellen wir das float-Array mAcceleration mit der Größe drei. Mit

System.arraycopy( event.values, 0, mAcceleration, 0, 3 );

Kopieren wir die Werte des Events ab der Position 0 in unser Array ab der Position 0, das die Länge 3 hat. Die drei Werte, die sich jetzt in unserem Array befinden, sind die der Beschleunigung auf der X-, Y- und Z-Achse im Raum. Auch diese geben wir über Getter nach außen weiter:

public float getAccelerationOnXAxis( ){
  return mAcceleration[0];
}

Für die anderen beiden Achsen machen wir das entsprechend.

Jetzt haben wir einen super Listener implementiert, haben diesen aber noch nirgends eingehängt. Dafür springen wir zurück in die onCreate()- Methode. Zunächst holen wir uns dort einen SensorManager aus dem SystemService. Das geht wie folgt:

SensorManager manager =
 (SensorManager)getSystemService(Context.SENSOR_SERVICE);

Nun fragen wir ab, ob überhaubt ein Beschleunigungssensor vorhanden ist, indem wir uns die Liste vorhandener Sensoren geben lassen.

if(manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() > 0)

Wenn es Sensoren gibt,  holen wir uns einfach den erstbesten Sensor aus der Liste (weil in der Regel nicht mehr als einer da ist) und registrieren uns bei diesem als Listener:

final Sensor accelerometer =
 manager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0);
manager.registerListener(
 this, accelerometer, SensorManager.SENSOR_DELAY_GAME);

Dabei legen wir über SensorManager.SENSOR_DELAY_GAME fest, mit welcher Präzision wir über Beschleunigungen informiert werden wollen. In unserem Fall wählen wir eine Geschwindigkeit, die für Spiele ausreichend ist, und fertig sind wir.

Der Teil über die Eingabemethoden ist übrigens stark an das Tutorial von Mario Zechner auf AndroidPit angelehnt.

Damit ist es geschafft! Wir haben eine abstrakte Basisactivity geschaffen, die es uns erlaubt, über ein Listenerkonzept 2D-Spiele für Android zu implementieren, inklusive Eingabemechanismen, Splashscreen und zwei-Thread-System.

Wenn Ihr an manchen Stellen nicht mitgekommen seid oder einfach gleich mit dem Spieleentwickeln anfangen wollt, findet ihr hier den Code zu der abstrakten Klasse, hier den Code zum Rendering- und hier den Code zum SimulationListener. Leitet dafür einfach eine Klasse von der GameActivity ab, erstellt Klassen die von SimulationListener und RenderingListener ableiten und hängt diese bevor(!) Ihr in Eurer abgeleiteten Klasse super.onCreate() aufruft als Listener über setRenderingListener() und setSimulationListener() ein. Bastelt darauf basierend Euer eigenes Spiel oder wartet noch auf das nächste Tutorial, da werden wir die erste Figur über den Bildschirm wandern lassen.

Wie Ihr seht mache ich die Tutorials relativ umfangreich, bitte habt deshalb Nachsicht wenn es ein paar Wochen dauert bis der nächste Teil erscheint.

Tutorial: Android Spieleentwicklung – Theorie

Posted by Johannes Borchardt August - 12 - 2010 - Thursday 2 COMMENTS

Spiele: Hat wahrscheinlich jeder schonmal gemacht, die meisten wahrscheinlich auch schonmal auf dem Computer, einige auch auf ihrem Android-Device.

Spielen ist das Eine, Spiele entwickeln das Andere. Und Spiele machen für die Android-Plattform ist auch nochmal was anderes. Im wesentlichen kann man dabei Spiele mit Animationen, also alle Spiele, bei denen sich etwas bewegt, oder Spiele ohne Animationen, zum Beispiel textbasierte Spiele, unterscheiden. Ich will hier auf erste eingehen.

Animierte Spiele können in Android entweder in 2D mit Hilfe von Canvas und SurfaceViews oder in 3D mit Hilfe von OpenGL ES geschrieben werden. Dabei sind mit OpenGL ES auch 2D-Spiele ohne weiteres möglich. Bei der Entscheidung OpenGL ES oder nicht sollte man sich darüber im Klaren sein, dass OpenGL die wahrscheinlich anspruchsvollere Variante ist, da mehr mit Matrizen und Vektoren umgegangen werden muss als in Canvas-basierten Spielen und die dritte Dimension durchaus einen Unterschied macht. Für  OpenGL ES – basierte 2D – Spiele kann man sich zum Beispiel die andengine anschauen (sehr zu empfehlen).

Worüber ich jetzt schreiben werde hat aber weder mit OpenGL ES, noch mit Canvas-basierten Spielen zu tun, es ist etwas genereller, es geht um Loops und Threads.

Generell heißt es, dass ein Spiel eine main Loop haben soll. Diese main Loop ist eine Schleife in der alles geschieht, was sich um die Animation (Rendering), Simulation und Eingabenverarbeitung dreht und die so lange läuft wie das Spiel selber. Das heißt zum Beispiel: ein ball ist in der Mitte des handys, die main Loop macht eine weitere Iteration, stellt dabei fest, dass das Handy nach rechts geneigt wurde, weshalb in der Simulation berechnet wird, dass der Ball drei Pixel weiter rechts dargestellt werden soll, was dann in der Animation gezeichnet wird. Diese generelle Aussage ist falsch oder sollte zumindest so nicht umgesetzt werden.

Das Stichwort heißt Nebenläufigkeit: Anstatt in einem einzigen Thread alles auszuführen sollte klar zwischen den drei Spielelementen, Eingabeverarbeitung, Simulation und Rendering, getrennt werden.

Zunächst einmal zum Rendering-Thread: In diesem wird alles gezeichnet. In OpenGL macht man das ganz einfach, indem man das Interface GLSurfaceView.Renderer implementiert und dann alles in der onDraw()-Methode malen lässt, OpenGL ES kümmert sich dann um einen eigenen Thread. Bei SurfaceViews schaut man sich am besten auf dem offiziellen Blog und im SDK-Beispiel LunarLander um. Das Rendern auf normalen Views ist für viele Zwecke viel zu langsam, weshalb hier nicht näher darauf eingegangen wird.

Als nächstes benötigt man einen Thread, der sich um die Simulation kümmert, das heißt der alle Objekte, deren Verhalten und Position verwaltet.

Als drittes müssen noch die Eingabemethoden ausgelesen werden. Legt man die Werte aus diesen nicht in plain old dataobjects ab (was man aber oft tut), muss hier beim Zugriff auf die Threadsynchronisation geachtet werden.

Das gilt auch für den Zugriff auf die Simulationsobjekte: Auf diese wird sowohl vom Simulationsthread, der diese berechnet, als auch vom Renderingthread zugegriffen, es ist also für Synchronisation zu sorgen! Das bedeutet: Wenn ein Thread eine Methode oder ein Objekt, die oder das als synchronized gekennzeichnet ist, bearbeiten will, kann er das nur, wenn das kein anderer Thread tut.

Hier ein konkretes Beispiel, im Simulationsthread:

final List<GameObject> objects = simulation.objects;
for(int i=0; i++; i<objects.size()){
     GameObject object;
     synchronize(object){
        object.update(deltaTime);
     }
}

und im Rendering Thread:

final List<GameObject> objects = simulation.objects;
for(int i=0; i++; i<objects.size()){
     GameObject object;
     synchronize(object){
        //in diesem Fall wird eine GLSurface View übergeben.
        //es wäre auch möglich das Rendering außerhalb des
        //GameObjects durchzuführen, z.B. in einer Renderer-Klasse.
        object.render(gl);
     }
}

Eigentlich alles ziemlich einfach, oder?

Wir iterieren zum Einen im Simulationsthread einmal über alle Simulationselemente, dies machen wir mit einer normalen for-schleife, weil diese bei Listen performanter sind als foreach-Konstrukte. Wir übergeben einer update-Methode aller Spielelemente eine delta-Zeit und das war’s. Die Deltazeit bezeichnet dabei die Zeit, die seit dem letzten Zeichnen des Bildschirms vergangen ist. Somit kann gewährleistet werden, dass ein Objekt sich immer in der gleichen Zeit gleich weit bewegt, auch wenn unterschiedlich viele Frames pro Sekunde gezeichnet werden können. In der Update-Methode der jeweiligen Spielobjekte muss dann alles berechnet werden, was das Verhalten des Objekts ausmacht, wie zum Beispiel die Position.

Im Rendering-Thread das Selbe: Eine Iteration über alle Spielelemente, wobei jedes Element über eine render()- Methode verfügt, die mit einer übergebenen GLSurfaceView umgehen kann und sich selbst darauf zeichnet. Das funktioniert natürlich auch mit normalen Views oder SurfaceViews. Hier wird auch die Wichtigkeit von Synchronisation deutlich: Stellt Euch vor ein GameObject hätte eine Position{x,y,z}. Gerade als der Rendering-Thread ein Objekt zeichnen will und bereits x und y verarbeitet hat, manipuliert der Simulationsthread den z-Wert, weshalb das Objekt an einer ganz anderen Stelle gezeichnet wird als es eigentlich vorgesehen gewesen wäre. Darum wird das Objekt während es gezeichnet wird einfach von gelockt, weshalb die Simulation nicht mehr darauf zugreifen kann bis das Malen abgeschlossen ist.

Fehlt nur noch… Die Nutzereingabe, richtig! Diese wird normalerweise gepollt, d.h. man fragt zum Beispiel immer nach dem Ort, auf den der Nutzer das letzte mal gedrückt hat ab. Wenn sich dieser nicht verändert hat, bewegt man zum Beispiel eine Figur dort hin, wenn er sich verändert hat, bewegt man sie zu dem neuen Punkt.

So viel zur generellen Theorie der Spielentwicklung. Wirkt eigentlich gar nicht so schwer, muss es auch nicht immer sein. Teil zwei wird ein kurzes praktisches Beispiel der obigen Theorielektion auf Basis einer SurfaceView sein, lasst Euch überraschen.

http://de.wikipedia.org/wiki/Thread_%28Informatik%29n

Apps auf SD-Karte mit Froyo

Posted by Lukas.Jarosch May - 25 - 2010 - Tuesday 5 COMMENTS

Wie ich in meinen vorherigen Post geschrieben habe, kann man nicht per default Apps auf die SD-Karte verschieben.

Dem kann jetzt ganz einfach abgeholfen werden

1: Über ADB...
Handy mit Aktiviertem USB-Debugging Modus mit dem PC verbinden und ADB Starten.

  • adb shell
  • pm setInstallLocation 2

2: Direkt auf N1
“therminal emulator” vom Market laden.
Therminal Emulator Starten und folgendes eingeben:

  • su
  • pm setInstallLocation 2

Getestet und funktioniert, viel Spaß.

P.S. kopiert nur Spiele und andere große apps die ihr nicht dauernd braucht. Widgets etc sollten auf dem Handy bleiben.

Live Wallpaper Tutorial für Android 2.1

Posted by Mohammed El Batya January - 26 - 2010 - Tuesday 6 COMMENTS

Wie viele von euch sicherlich bereits mitbekommen haben hat Google vor ein paar Wochen das Nexus One veröffentlicht und damit auch die neue Android-Version 2.1. Eines der tollen neuen Features nennt sich “Live Wallpaper” (auch manchmal “Living Wallpaper” genannt). Das sind coole dynamische Desktop-Hintergründe, welche schicke Animationen darstellen. Oft reagieren diese auch auf Benutzerinteraktionen.

Ich möchte mit diesem Artikel einen grundlegenden Überblick über die Programmierung eines solchen Live Wallpapers vermitteln.
Dieser Artikel enthält keinen voll funktionstüchtigen Code, da bereits ein gutes Beispiel im SDK 2.1 (namens “Cube”) mitgeliefert wird.

Vom Prinzip her ist so ein Live Wallpaper ( ab jetzt LWP abgekürzt ) das selbe wie eine normale Activity, welche auf einem 2D Surface einfache Dinge wie Kreise und und Linien zeichnet. Es ist vergleichbar mit der Programmierung eines kleinen 2D Spiels.

Voraussetzungen für dieses Tutorial:

  • Du hast schon mal eine normale Activity programmiert.
  • Du hast das SDK 2.1 installiert.

0. Grundlagen

Ein LWP besteht im Grunde aus 5 Teilen:

  1. einem WallpaperService
  2. einer “Engine”, welche sich um das Zeichnen kümmert
  3. einer “Einstellungen”-Activity zur Anpassung des Wallpapers durch den User (z.B. Farben)
  4. einer XML-Datei um zu konfigurieren wie das LWP aufgelistet wird (z.B. Icon)
  5. das übliche Android-Manifest

1. Der WallpaperService

Zunächst benötigst du eine Klasse, welche von “WallpaperService” erbt. In dieser Klasse musst du nur die Methode “onCreateEngine()” überschreiben und deine eigene Engine zurückgeben.

public class AndforgeWallpaper extends WallpaperService {
	@Override
	public Engine onCreateEngine() {
		return new AndforgeLWPEngine();
	}
}

2. Die Engine

Die Engine übernimmt das eigentliche Zeichnen und muss innerhalb deines WallpapersServices definiert werden. Also definieren wir als nächstes eine innere Klasse, welche von WallpaperService.Engine erbt.

public class AndforgeWallpaper extends WallpaperService {
	@Override
	public Engine onCreateEngine() {
		return new AndforgeLWPEngine();
	}

	 class AndforgeLWPEngine extends Engine{
		// TODO: ausprogrammieren
 	}
}

Die Engine besitzt einen SurfaceHolder, welcher wiederum ein Canvas besitzt. Auf diesem Canvas kann man letztendlich seine Animationen zeichnen. Mit der Methode getSurfaceHolder() bekommt man in der Engine das Surface. Wendet man auf das Surface die Methode lockCanvas() an bekommt man das Canvas zum zeichnen.

Achtung: Es ist wichtig das Canvas über die Methode lockCanvas() zu erhalten, da es sonst zu seltsamen Fehlern kommen kann. Dies hat unter anderem damit zu tun, dass “DoubleBuffering” eingesetzt wird.

DoubleBuffering?
Kurz gesagt: Es werden zwei Bilder generiert, eines auf dem gerade gezeichnet wird und eines, welches auf dem Bildschirm zu sehen ist. Diese Bilder werden nach jedem Zeichenvorgang ausgetauscht, was bedeutet, dass man immer eine “alte” Version des Bildes zum Zeichnen bekommt. Ruft man nun lockCanvas() auf bekommt man das Canvas, welches gerade nicht angezeigt wird um darauf zu zeichnen. Ist man fertig mit dem Zeichnen kann man mit unlockCanvasAndPost(canvas) das Canvas wieder freigeben und ein Austauschen des angezeigten Canvas mit dem frisch bemalten Canvas anstoßen.

Das Zeichnen sieht dann ungefähr so aus:


Canvas canvas = getSurfaceHolder().lockCanvas();

//TODO: auf dem canvas zeichnen

getSurfaceHolder().unlockCanvasAndPost(canvas);

Wo stecken wir nur unseren Code zum Zeichnen hin? Theoretisch kann man jetzt eine beliebige Methode der Engine überschreiben und dort etwas zeichnen (was nicht immer sinnvoll ist ^^). Es ist wichtig zu verstehen, dass die Engine einen Lifecycle (Lebenszyklus) durchlebt. Das bedeutet, dass es z.B. Momente gibt in denen es gar keinen SufaceHolder gibt ( also der SurfaceHolder null ist). Hier sind die Methoden onSurfaceDestroyed(), onSurfaceCreated(), onSurfaceChanged(), onDestroy(), onCreate(), usw. entsprechend zu beachten.

Aber wo kommt jetzt endlich der Code zum Zeichnen hin?

Eine mögliche (aber nicht besonders sinnvolle) Stelle wäre die onSurfaceCreated() Methode:

@Override
		public void onSurfaceCreated(SurfaceHolder arg0) {

			Canvas canvas = getSurfaceHolder().lockCanvas();

			// Zeichne einen Kreis
			canvas.drawCircle(200, 200, 10, color);

			getSurfaceHolder().unlockCanvasAndPost(canvas);
		}

Dies hat zur Folge, dass ein Kreis gezeichnet wird sobald der SurfaceHolder erzeugt wird, also einmal und dann nie wieder.

Was wir aber für bewegte Animationen benötigen ist eine Möglichkeit das Zeichnen regelmäßig anzustoßen. Das Cube Beispiel aus dem SDK verwendet hierfür eine recht elegante Lösung unter Verwendung eines Handlers, welcher sich selbst immer wieder auf die “Queue” schmeißt. Ich möchte hier nicht weiter auf die Implementierung der Engine eingehen und auf das Cube Beispiel verweisen, da dies sonst den Rahmen des Artikels sprengen würde. Ich bin mir aber sicher, dass man mit dem hier gelernten Wissen das Cube Beispiel relativ gut verstehen kann. (Falls jemand Interesse an einem ausführlicheren Artikel zur Implementierung eines Wallpapers hat, kann er/sie gerne einen Kommentar hinterlassen.)

3. Die “Einstellungen”-Activity

Dies ist eine ganz normale Activity, welche dem Nutzer ermöglichen soll das Wallpaper zu personalisieren. Vorstellbar wären z.B. Farbe, Geschwindigkeit und diverse Effekte des Wallpapers. Hierfür sollte man wie im Cube Beispiel auf SharedPreferences zurückgreifen um die Einstellungen auch beim Zeichnen mitzubekommen. Zusammengefasst: Diese SettingsActivity lässt den Nutzer die SharedPreferences editieren, welche wiederum in der Engine beim Zeichnen berücksichtigt werden.

Hier sollte man beachten, dass diese Activity normalerweise nicht im AppMenü von Android auftauchen soll. Daher sollte man das Manifest (siehe Punkt 5) entsprechend anpassen. Diese App wird gestartet wenn der Nutzer in der LWP-Liste neben dem LWP auf Einstellungen klickt.

4. LWP lwp_config.xml

Diese XML Datei dient dazu, zu konfigurieren wie das LWP im LWP-Menü aufgelistet wird. Sie muss sich im Verzeichnis res/xml befinden, wenn dieses Verzeichnis nicht existiert muss man eins erstellen. Eine typische Konfiguration würde folgende Werte enthalten:

  • android:thumbnail = mini Vorschaubild in der LWP-Liste
  • android:description = kurze Beschreibung für die LWP-Liste
  • android:settingsactivity = Packagepfad zu der Settings-Activity


5. Das Manifest

Im Folgenden wird der WallpaperService und die SettingsActivity eingerichtet.
Man beachte, dass kein Menüeintrag erstellt wird.



	

		
			
				
			
			
		

		
		

	
	


Ich hoffe ich konnte einen Überblick und ein Grundverständnis über die Erstellung eines Android Live Wallpapers bieten. Ich habe bewusst ein paar Details vorenthalten und versucht mich auf das Wesentliche zu konzentrieren. Bei offenen Fragen einfach einen Kommentar hinterlassen.

How to: Serverkommunikation in Android (REST + JSON)

Posted by Johannes Borchardt January - 26 - 2010 - Tuesday 2 COMMENTS

Es gibt viele Bücher über Android, mindestens drei davon sogar in der deutschen Sprache. Die meisten dieser Bücher sind sich ziemlich ähnlich: Sie erklären Android-Grundkonzepte und manchmal noch ein bisschen mehr (z.b. OpenGL/ES) und das wars dann. Die Frage, die sich mir in diesen Büchern immer gestellt hat, war: Wenn meine Anwendung mit mehr als dem Handy kommunizieren lassen will, was dann? Wie bringe ich meinen Android dazu, mit einem Server zu reden? Dieser Teil wurde in der Regel als “HTTP-Grundlagen” oder ähnliches ausgelassen, zumindest in den Büchern die mir untergekommen sind.

Um ein bisschen Abhilfe zu verschaffen erkläre ich  hier zwei Methoden, mit Hilfe derer Android-Clients REST-Server ansprechen und JSON-Format übertragen, bzw. verarbeiten können.

Zuerst der Theoretische Teil: REST steht für Representional State Transfer und wurde im Jahr 2000 von Roy Fielding vorgestellt. Wie der Name bereits sagt, können Stadien transferiert werden, sprich auf solche zugegriffen werden. Oder andersrum: Ein REST-konformer Server kennt keinen Zustand. Rest baut auf dem HTTP-Protokol auf und verwendet (mindestens) vier der Basiskomponenten:

GET für Datenanfragen,

POST für das Ablegen von Daten,

DELETE für das Löschen von Daten und

PUT für das Updaten von Daten.

Internetnutzer sollten zumindest mit GET-Befehlen vertraut sein. Internetseiten, die Daten anfordern, übertragen, wenn sie so programmiert wurden, wie es von den HTTP-Schaffern (zu denen auch Fielding gehört) gedacht war, in der URL Parameter. Diese Parameter werden mit einem ? eingeleitet (z.B. http://www.andforge.net/?m=201001).

Als Übertragungsformat hat sich auf mobilen Endgeräten aufgrund des geringen Datenoverheads die JavaScript Object Notation, kur JSON, als passend erwiesen. JSON stellt Objekte Beispielsweise wie folgt dar:

{
  "Name" : "Mustermann",
  "Vorname"      : "Max",
  "Adresse"       :
  {
    "Strasse"        : "Musterstrasse",
    "Nummer"     : "123",
    "PLZ"  : "98765",
    "Ort"   : "Musterstadt
  }
}

Das Format ist also durchaus noch lesbar, aber in seinen Zusatzzeichen, die eine Interpretierbarkeit ermöglichen, sehr sparsam. Dies ist für die eventuell schwachen Verbindungen, die es auf Mobiltelefonen geben kann, hilfreich.

Nach dieser kurzen Einführung wenden wir uns DER Frage zu: Wie geht das in Android?

HTTP GET

Zunächst einmal wird ein REST-konformer Webservice, der im JSON-Format antwortet benötigt. Hat man diesen, kann es losgehen:

public class Xxx{

//Bezeichnung der Klasse:

private final String TAG="Xxx";

public void getJSONObject(String url)
{

HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse response;

try {
response = httpClient.execute(httpGet);

// TODO: HTTP-Status (z.B. 404) in eigener Anwendung verarbeiten.

Log.i(TAG,response.getStatusLine().toString());

HttpEntity entity = response.getEntity();

if (entity != null) {

InputStream instream = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(instream));
StringBuilder sb = new StringBuilder();

String line = null;
while ((line = reader.readLine()) != null)
sb.append(line + "n");

String result=sb.toString();

Log.i(TAG,result);

instream.close()

JSONObject json=new JSONObject(result);

JSONArray nameArray=json.names();
JSONArray valArray=json.toJSONArray(nameArray);

for(int i=0;i<valArray.length();i++)
{
//TODO: Inhalte der Arrays verarbeiten.
}
}
catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}finally{
httpGet.abort();
}
}

Zuerst erzeugen wir einen HTTPCLient. Dann wird ein HTTPGet-Objekt erzeugt. Diesem Objekt wird eine URL mitgegeben, die bereits alle Parameter der Anfrage enthält. Da Wir eine Antwort vom Server erwarten, erzeugen wir zudem ein HTTPResponse-Objekt. Nun führen wir den HTTPGet-Request aus und schreiben die Antwort in unser HTTPResponse-Objekt.

Nun machen wir uns an die angehängten Daten. Diese befinden sich in der HTTPEntity der Antwort. Wir lesen sie mit Hilfe eines buffered readers und eines stringbuffers (aus Performancegründen) aus und erzeugen aus dem so generierten String ein JSONObjekt, welches alle übertragenen Daten enthält, sofern sie im JSON-Format waren. Mit diesen Daten kann nun gearbeitet werden.

So einfach lassen sich Daten mittels REST und JSON in Android abfragen.

HTTP POST

Die nächste Frage, die sich aufdrängt: Daten abfragen funktioniert, aber wie ist das mit den drei anderen Dingern?

Fangen wir mit einer Methode zum Abschicken eines HTTP POST- Objektes an:

public void postJSONObject(String url, JSONObject data, String objectName)
{
HttpPost postMethod = new HttpPost(url);

try {
HttpParams params = new BasicHttpParams();
params.setParameter(objectName, data.toString());

postMethod.setParams(params);
httpClient.execute(postMethod);
Log.i(TAG, "Post request, data: " + params.toString());

} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();

} finally {
postMethod.abort();
}
}

Wie zuvor wird der Methode eine Ziel-URL übergeben. Des Weiteren wird ein JSONObject mit den zu übertragenden Daten und die Bezeichnung der Daten (z.B. “User”) übergeben. Da wir einem HTTPPost nicht einfach so ein JSONObject anhängen können, wandeln wir dieses zuerst in HTTP-Parameter um und hängen diese dann an.  Nun führen wir den POST aus und alles ist gut (hoffentlich). Natürlich sollten an dieser Stelle noch eventuelle Fehlercodes oder -Meldungen abgefangen werden, dies kann analog zur ersten Methode geschehen, wobei Fehlermeldungen natürlich für jeden Server individuell behandelt werden müssen (üblicherweise gillt: wenn ein JSON-Objekt mit “error:” beginnt, ist was schief gelaufen).

HTTP DELETE und HTTP PUT

DELETE und PUT können so wie POST implementiert werden, nur dass der Objekttyp von postMethod in HTTPDelete, bzw. HTTPPut, geändert wird.

Et voilà: So schnell geht’s und schon fangen die Server an zu flüstern, mehr als das, sie hören sogar zu =)

3D Hologramm Effekt auf Android Geräten

Posted by Mohammed El Batya January - 2 - 2010 - Saturday 1 COMMENT

Folgendes Video von TATMobile zeigt sehr schön, wie ein User-Interface der Zukunft mit 3D Hologramm Effekt aussehen könnte und am Ende dieses Artikels gibt es noch eine weitere sehr schöne Demo.

[ad#google_article_mo]

Der Trick hierbei ist, dass das Bild  auf den momentanen Blickwinkel des Benutzers angepasst wird.  Das im Video gezeigte User-Interface ist leider nicht echt und soll nur zeigen wie so etwas theoretisch aussehen könnte.

Das Prinzip ist jedoch seit langem bekannt und wurde bereits durch folgendes Video sehr berühmt. Es zeigt eine beeindruckende Umsetzung dieser optischen Täuschung für die Nintendo Spielekonsole Wii.

3D Hologramm Effekt für Android

Theoretisch ist es technisch möglich solch einen Effekt auch für Android Geräte zu entwickeln. Ich habe mir daher ein paar Gedanken gemacht und möchte nun beschreiben wie man das ganze auf einem Android Gerät realisieren könnte. Für diesen Effekt ist es essentiell, dass die Positionen von Betrachter und Gerät relativ zueinander ermittelt werden können. Daher habe ich die verschiendenen Bewegungsmöglichkeiten im Raum  in die folgende Fälle unterteilt und sie isoliert betrachtet.

1. Handy kann nur kippen

Zunächst möchte ich davon ausgehen, dass sich der Kopf des Benutzers und das Android Gerät fest im Raum befinden. Sie können sich in keinerlei Richtung Bewegen. Das Gerät kann aber in alle möglichen Richtungen gekippt werden.

Möchte man nun unter diesen Umständen einen 3D Effekt erzeugen, muss man nur die Lage des Gerätes anhand der Beschleunigungssensoren (welche momentan so gut wie alle Android Geräte besitzen) ermitteln. Mit den Daten kann man dann das Bild auf dem Gerät so anpassen, dass man einen wunderbaren Effekt erzielt. Wenn man sich hierbei ein Auge zuhält wirkt der Effekt noch besser, da dann jegliches Gefühl für “Tiefenwahrnehmung” verschwindet .

2. Handy kann sich nur Horizontal/Vertikal bewegen

Jetzt kommt der etwas kompliziertere Fall drann. Ich möchte jetzt annehmen, dass sich das Gerät auf einer Art Schiene im Raum nach oben, unten, links und rechts bewegen kann. Es kann sich jedoch in keine Richtung drehen/kippen. Bei diesen Bewegungsabläufen helfen uns die Beschleunigungssensoren leider nicht mehr weiter, da wir die Position des Gerätes relativ zur Position der betrachtenden Augen benötigen. Dies könnte man mit einer Front-Kamera, wie es heutzutage viele Video-Telefonie-Taugliche Geräte haben realisieren. Die Kamera müsste dann anhand eines Augen-Erkennungs-Algorithmusses die Position der Augen des Benutzers ermitteln. Es gibt bereits einige solcher Algorithmen, welche oft in  Digitalkameras eingesetzt werden.

3. Handy kann sich nur auf der Z-Achse bewegen

Dieser Fall ist ähnlich zu lösen wie der vorherige. Hier wird angenommen, dass sich das Gerät nur nach vorne und hinten bewegen kann. Es kann wieder nicht gekippt werden. Wenn sich das Handy exakt auf einer Linie vor dem Betrachter befindet hat diese Bewegung keine Auswirkung auf den Bildschirminhalt.

Befindet sich das Handy, jedoch leicht versetzt neben dem Betrachter und bewegt sich dann auf der Z-Achse muss dies auch erfasst werden um den Effekt  möglichst realistisch wirken zu lassen. Auch hierfür benötigt man einen Augen-Erkennungs-Algorithmus. Um die Länge der Strecke zwischen Handy und Betrachter zu ermitteln muss man diesmal den Abstand zwischen den beiden Augen messen. Liegen die Augen relativ nah zusammen ist der Betrachter etwas weiter entfernt. Vergrößert sich dann der Abstand zwischen den Augen bewegt sich der Betrachter auf das Android Gerät zu. Das selbe funktionier natürlich auch umgekehrt.

Die Mischung machts!

Kombiniert man nun alle drei Fälle kann man mit den ermittelten Daten einen wunderbaren 3D Hologramm Effekte auf Android Geräten erzeugen, welcher den Effekt aus dem TATMobile Video noch weit übertreffen könnte. Ich bin mir sicher, dass in den nächsten Monaten noch viele coole Experimente auf diesem Gebiet passieren werden.

[ad#google_article_mo_2]

4. Das Ultimative 3D Erlebnis !!!

Das Android Gerät besitzt einen weiteren nicht zu unterschätzenden Sensor. Den Kompass! Mit diesem liese sich der 3D Effekt um einen besonders coolen Aspekt erweitern. Man könnte die auf dem Bildschrim dargestellten “Pseudo-3D-Objekte” von allen möglichen Seiten betrachten.

Das folgende Video zeigt diesen Effekt sehr schön. Es ist eine Kombination aus den Punkten 1 und 4. Man beachte, dass sich weder Kamera noch das Gerät von der Stelle bewegen.

Der 3D Hologramm Effekt ist machbar!

Mit den hier vorgestellten technischen Möglichkeiten sind solche Effekte theoretisch nur eine Frage der richtigen Implementierung. Es müssen nämlich aller der 4 Fälle berücksichtigt werden um die perfekte Illusion zu erzeugen. Ein Android Phone mit Front-Kamera wäre natürlich auch recht hilfreich :) .

Ich übersehe unter Umständen ein paar Aspekte oder es geht noch einfacher als ich es hier beschrieben habe. Daher würde ich mich über Feedback und weitere Ideen zu diesem Thema sehr freuen.

Nachtrag:
Ich habe gerade ein weiteres Video zu diesem Thema entdeckt. Wieder ist es eine iPhone App, welche offensichtlich mit Kompass und den Beschleunigungssensoren arbeitet. Wie bei der “Katze” von oben wirkt der Effekt nur solange wie der Betrachter seine relative Position zum Gerät nicht verändert.

Ernste Sicherheitslücke gepatched!

Posted by Mohammed El Batya August - 1 - 2009 - Saturday ADD COMMENTS

Das letzte “Over The Air” Update für Android brachte auf den ersten Blick keine großartigen Neuerungen. Kürzlich sind jedoch immer mehr Informationen zu diesem Update aufgetaucht.

Es wurde eine relativ gefährliche Sicherheitslücke behoben. Diese Sicherheitslücke erlaubt es einem Angreifer per SMS das Gerät des Empfängers außer Gefecht zu setzen. Diese Art von Angriff gehört zur Rubrik der DOA (Denial of Service) Attacken, welche versuchen das Ziel-System unbrauchbar zu machen.

Offensichtlich war auch das IPhone für diese Art von Angriff anfällig. Windows Mobile Geräte scheinen bis jetzt noch betroffen zu sein.

Ausführlichere Infos gibts auf informationweek.com.

adp1 market kein zugriff ???

Posted by Mohammed El Batya July - 28 - 2009 - Tuesday 1 COMMENT

Laut Google Analytics landen in letzter Zeit immer mehr Besucher über die Keywords “adp1 market kein zugriff” auf andforge.net obwohl wir zu diesem Thema noch keinen Artikel veröffentlicht haben.

Ich möchte nun mit diesem Artikel versuchen das Thema zu klären und orientierungslosen Besuchern wenigstens ein paar hilfreiche Tips geben.

Zunächst einmal werde ich versuchen die Keywords richtig zu interpretieren:

adp1 = “Android developer phone 1″ oder auch “DevPhone 1″
market = Android Market
kein zugriff = Ich vermute, dass hiermit das Fehlen einiger Applikationen im Android Market gemeint ist.

Falls ich das ganze falsch interpretiert habe würde ich mich natürlich über Kommentare diesbezüglich freuen.

[ad#google_article_mo]

Das Problem

So viel ich weiß (ohne weitere Nachforschungen anzustellen) können einige Aplikationen auf einem DeveloperPhone (adp1)  nicht aus dem Android Market heruntergeladen werden.

Es gibt da zwei potenitelle Ursachen:

1. Wenn mich meine Erinnerung nicht täuscht ist es so, dass eine Zeit lang kostenpflichtige Apps auf dem ADP1 im Android Market nicht angezeigt worden sind. Ob das immernoch so ist weiß ich leider nicht.

2. Wenn man im Android market eine App veröffentlicht, hat man die Möglichkeit diese mit einem speziellen Kopierschutz zu versehen. (siehe Screenshot) Was da genau passiert weiß ich spontan nicht aber eine Folge davon ist anscheinend, dass man von einem adp1 keinen Zugriff mehr auf diese App hat.

Android Market - copy protection

Android Market - copy protection

Diese Aussagen beruhen eher auf Halbwissen und Vermutungen und sind ohne Gewähr. Für Infos jeglicher Art bin ich natürlich sehr dankbar.

Die Lösung

Ich persöhnlich besitze auch ein adp1 und sollt daher das Problem eigentlich kennen.  Nur ist es so, dass ich seit längerem nicht die originale Firmware von Google installiert habe sondern die etwas modifizierte Version von Jesus Freak. Diese bringt neben vielen tollen neuen Features anscheinend auch die Möglichkeit problemlos auf alle Apps im Android Market zuzugreifen.

Ich möchte jetzt erstmal keine Anleitung für die Installation von Jesus Freaks Firmware schreiben da man recht viel dazu im Internet findet. Wer trotzdem Hilfe benötigt kann gerne einen Kommentar hinterlassen.

Ich hoffe ich konnte dem ein oder anderen “adp1 market kein zugriff” Suchenden ein wenig weiterhelfen!

[ad#google_article_mo_2]

Android DevPhone – Das bessere G1!

Posted by Mohammed El Batya June - 19 - 2009 - Friday 11 COMMENTS

Diesen Artikel möchte ich dem “Android Developer Phone 1″  widmen. Es ist im Internet auch oft unter den Abkürzungen “adp1″ oder “DevPhone” zu finden.

Viele Android-Fans kennen das G1 von T-Mobile oder das HTC Magic von Vodafone und es steht noch ein ganzer Haufen neuer Android Phones in den Startlöchern. Jedoch wissen nur wenige, dass es schon lange bevor das G1 offiziell in Deutschland erhältlich war ein weiteres sehr interessantes Gerät zu kaufen gab und noch gibt.

Das DevPhone

Dieses  Gerät besitzt die selbe Hardware wie das offizielle G1 von T-Mobile. Oberflächlich betrachtet könnte man sogar sagen es sei  genau das selbe Gerät. Bei genauerer Betrachtung erkennt man jedoch ein paar ganz beachtliche Unterschiede.

[ad#google_article_mo_2]

Spezielle Optik

Das Google DevPhone 1

Das Google DevPhone 1

Das G1 von T-Mobile

Das G1 von T-Mobile

Das DevPhone sieht dem offiziellen schwarzen G1 zum verwechseln ähnlich. Der einzige optische Unterschied ist das recht nette Muster mit dem kleinen Android Roboter auf der Rückseite.

Nachtrag: Das DevPhone gibt es leider nur mit englischer Tastatur, was nicht jedermans Sache ist. Mit etwas Übung kommt man damit jedoch innerhalb weniger Wochen problemlos klar.

In Deutschland gibt es bei T-Mobile nur die weiße Version des G1 zu kaufen, welches auf Grund seiner hellen und freundlichen Optik auch ohne Muster etwas attraktiver wirkt. Das großes Manko der weißen Version ist aber die ebenfalls weiße Tastatur. Die sehr helle Beschriftung der Tasten führt leider zu einer sehr unangenehmen und schlechten Lesbarkeit.

Das Innenleben

  • Root-Rechte
  • Entsperrter Boot-Loader
  • Kein Sim-Lock

Besonders interessant ist, dass das DevPhone Standartmäßig mit Root-Rechten ausgeliefert wird. Viele sehr nützliche Anwendungen für Android setzen Root-Rechte vorraus. (z.B. Tethering mit aNetshare)

Dank eines speziellen entsperrten Boot-Loaders kann man das Gerät mit einem beliebigem unsignierten Betriebssystem bespielen. Damit kann man problemlos Android Betriebssysteme auf das Handy laden, welche nicht von Google stammen und spezielle Features enthalten.

Auch ist das DevPhone frei von Sim-Lock oder ähnliche Sperrmechanismen. Daher kann man das Handy mit einer Sim-Karte von einem beliebigem Mobilfunk-Anbieter verwenden. Man muss auch keinen Vertrag mit T-Mobile oder einem anderen Anbieter abschließen um an das Gerät zu kommen.

[ad#google_article_mo]

Bei Google einkaufen!

Da das DevPhone für Entwickler gedacht ist kann man es nur bestellen, nachdem man sich bei Google als solcher registriert hat. Hierfür muss man sich für den Android Market anmelden, welche Entwicklern die Möglichkeit gibt ihre Anwendungen zu veröffentlichen.

http://www.android.com/market/

Jeder kann sich Registrieren. Die Registrierung kostet 25 Dollar. Hat man dann den Zugang zum Android Market kann man nach dem einloggen ein DevPhone für 399 Dollar bestellen. Alle Kosten für Versand, Zoll und Co. sind in den “Versandkosten” von ca. 140 Dollar  enthalten. Eine Lieferung in die U.S.A. ist sogar kostenlos. Gezahlt wird per Kreditkarte. Man sollte auch beachten, dass man sich pro Account nur ein Gerät bestellen darf.

Leider ist der Preis relativ hoch. Es ist daher Ratsam einen Blick in eBay zu werfen um sich über den aktuellen realen Marktwert zu informieren und Schnäppchen zu finden.

Wenn man dann noch den Dollar/Euro Kurs im Hinterkopf behält steht dem Kauf eines Android DevPhones nichts mehr im Weg.

Nachtrag: Abschließend könnte man sagen, dass das normale G1 für den “Durchschnitts-Anwender” von den Funktionen her vollkommen ausreichend ist. Wer jedoch zu den Benutzern zählt, welche die technischen Möglichkeiten des Gerätes voll aureizen wollen (z.B. MultiTouch), sollte sich zu mindest einmal mit dem Gedanken beschäftigt haben ein DevPhone zu kaufen.

Nachtrag: Ein paar weitere recht interessante Aspekte gibts in den Kommentaren zu diesem Artikel zu lesen.

http://www.android.com/market/

Android VPN Client – Was ist da los?

Posted by Mohammed El Batya June - 16 - 2009 - Tuesday 6 COMMENTS

! Man beachte das Update am Ende dieses Artikels :) !

VPN? Was ist das?

Oft haben Firmen Virtual Private Networks eingerichtet um das Netzwerk gegenüber dem Internet abzuschotten. Mit einem speziellen VPN Client kann man dann trotzdem über das Internet auf das interne Firmennetzwerk zugreifen um z.B. seine Firmen-Mails zu lesen.

Auch wird diese Technologie oft genutzt um den Zugang zum W-Lan in Firmen und Universitäten zu kontrollieren.

VPN und Android

Leider gibt es immernoch keinen (praktikablen) VPN Client für Android.

Die Konsequenzen sind:

  • kein W-Lan  Zugang in der Firma / Uni
  • keine Firmen-Mails auf dem Android Handy abrufbar

Dies ist für viele “ernsthafte” Smartphone Benutzer ein riesiges K.O.-Kriterium. Viele Benutzer sehen diesen Punkt als einziges Hauptargument gegen ein Android Handy und für den Kauf eines iPhones.


Android VPN Client !?

VPN zu unwichtig?

Für Windows Mobile und iPhone gibt es schon länger VPN Clients, welche einwandfrei funktionieren. Warum hat Google nicht “einfach” auch einen VPN Client integriert?!?

Vermutlich, wurde es von der Prioriät nicht besonders hoch eingestuft. Bis heute ist ja auch die Bluetooth-Funktionalität von Android stark eingeschränkt. Nicht etwa, weil es schlecht implementiert wurde sondern einfach weil Google noch nicht dazu gekommen ist sich darum zu kümmern. Daher wird es bei dem VPN Client wohl ähnlich sein.

Und die Community?

Google hat jedoch von Anfang an gesagt, dass Android noch nicht vollkommen ausgereift ist. Für eine Veröffentlichung  in dieser frühen Phase haben sie sich trotzdem entschieden, um der Community die Möglichkeit zu geben eigene Applikationen zu entwickeln.

Dieser Plan hat eigentlich auch hervorragen funktioniert. Innerhalb weniger Monate waren schon sehr viele (sinnvolle und unsinnige) Applikationen verfügbar. Im Fall von Tethering ist der Plan sogar so gut aufgegangen, dass eingegriffen wurde.

Auf einen VPN Client im Android Market wartet man jedoch vergeblich.

VPN Client in Arbeit?

Ich habe mich auf die Suche gemacht um im World Wide Web Spuren von Entwicklungsteam oder Nachrichten zu finde, welche uns einen kleinen Hoffnungsschimmer sehen lassen könnten.

Es folgen nun die Ergebnisse meiner Suche:

[ad#google_article_mo]

Entwickler aus Singapur

In einem zdnetasia Artikel stehen folgende Sätze.

“Developers in Singapore have been working on a wide variety of apps on the mobile platform, said Chua. The last meet-up saw interest in topics ranging from augmented reality to VPN (virtual private network) applications.” (Link)

Laut diesem recht aktuellen Artikel geht hervor, dass sich Entwickler aus Singapur unter anderem mit einem VPN Client beschäftigen.

Security Framework für Entwickler

Auf http://mocana.com/NanoPhone-Android.html findet man ein Framework für Entwickler um verschiedene Sicherheitsrelevante Dinge auf Android umzusetzen. Unter Anderem enthält es offensichtlich auch VPN Funktionalitäten, was eigentlich perfekt für Entwickler ist.

Leider befinden sich keine Preisangaben auf der Seite und Lizenzrechtliche Informationen konnte ich spontan auch nicht entdecken.

Einen VPN Client gefunden!

Auf ubergeeky.com steht sogar eine sehr ausführliche Anleitung wie man mit einem Android Handy auf eine Cisco VPN Netzwerk zugreifen kann.

Der Artikel trägt den Titel:

Connect to Cisco VPN from Android (Link)

Das Problem ist, dass es für sehr wenige Benutzer eine Lösung darstellt. Diese Anleitung erfordert ein relativ hohes Maß an technischem Verständnis im Umgang mit dem “Innenleben” von Android und richtet sich eher an die Entwicklergemeinde.

Es gibt Hoffnung!

Auch wenn es momentan noch keinen vernünftigen VPN Client gibt scheint es doch mehrere Ansätze zu geben, welche Hoffen lassen.

  • Es ist theoretisch und praktisch Möglich.
  • Es gibt sogar ein Framework.
  • Es gibt Entwickler, die sich mit dem Thema befassen.

Das beste ist jedoch:

Wer einen VPN Client entwickelt und zu einem fairen Preis im Android Market veröffentlicht kann aufgrund der Zielgruppe “Business-Nutzer”, eine ganze Menge Geld verdienen.

Update !!!

Der aktuelle Entwicklungszweig von Android namens Donut enthält offensichtlich auch VPN-Funktionalität. Somit ist es nur eine Frage des nächsten Over-The-Air Firmware Updates bis wir VPN auf unseren Android-Phones genießen dürfen.

Hier noch ein Screenshot aus den Donut Setting:
Donut - VPN-Client
Mehr Screenshots zu Donut gibt es hier.