Archiv vom Juli 2011

Handbuch und Datenblatt zum Floppy Emulator V3 überarbeitet / Floppy Emulator Image Tool für Windows, Linux und Mac OS X verfügbar.

Floppy Emulator V3 Datenblatt / Handbuch

Datenblatt / Handbuch

Das Handbuch und das Datenblatt zum Floppy Emulator V3 sind überarbeitet und verbessert worden. Beide stehen ab sofort im Supportbereich und auf der Produktseite zum herunterladen bereit.

FEI-Tool (Floppy Emulator Image Tool)

Floppy Emulator Image Tool für Mac OS X, Linux und Windows
Für den Zugriff auf Floppy-Emulator Images am PC/Mac wird das FEI-Tool benötigt.
Um eine größtmögliche Betriebssystemabdeckung zu erreichen gibt es das Floppy Emulator Image Tool (FEI-Tool) jetzt für alle gängigen Betriebssysteme:
Windows, Mac OS X und Linux.
Die Konsolenanwendung FEI-Tool ermöglicht den Zugriff auf Dateien in einem Floppy-Emulator Image, dass durch den ipcas Disketten-Emulator-V3 erstellt oder geändert worden ist.
Der Floppy Emulator V3 selber arbeitet selbstverständlich betriebssystemunabhängig.
Zusätzlich finden Sie im Supportbereich ein vorformatiertes 1,44 MB DOS Disketten-Image.

Produktseite des Floppy Emulator V3 >>
Floppy Emulator V3 – Support und Downlaod >>

Threads in Android Teil 2 (Aktualisierung der UI)

Werden Threads in Android überhaupt benötigt? Ja, besonders bei Operationen mit langer Laufzeit wie z.B. beim Zugriff auf das Netzwerk. Solche Operationen können den Haupt-Thread, durch den die Benutzeroberfläche (UI) ausgeführt wird, blockieren. Als Folge dessen ist die UI nicht in der Lage Benutzereingaben entgegenzunehmen.
Das folgende Beispiel soll das Problem veranschaulichen:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    // simulate long running operation
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        Log.e(TAG, "Thread.sleep", e);
    }
}

Gekapselte Prozesse brauchen in Threads länger, besonders dann wenn der Prozess in den Lebenszyklus der Aktivität gestartet wird (onCreate, onResume, onPause).
Wenn eine Anwendung nicht reagiert, liefert das Android-System normalerweise die Meldung "Anwendung reagiert nicht. Sofort schließen / Warten" zurück (ANR Dialog – Application Not Responding).

Um dies zu umgehen kann ein Thread erstellt werden der am Ende eine Meldung ausgibt:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    thread = new Thread() {
        public void run() {
            try {
                // simulate long running operation
                Thread.sleep(5000);
                textView.setText("new value...");
            } catch (InterruptedException e) {
                Log.e(TAG, "run in thread", e);
            }
        }
    };
    thread.start();
}

War es das schon? Nein, leider nicht. Nachfolgend können Sie sehen wie sich die Anwendung mit folgender Meldung verabschiedet:
CalledFromWrongThreadException Only the original thread that created a view hierarchy can touch its views.

CalledFromWrongThreadException

Genau wie in Java oder C# ein UI-Objekt (in unserem Fall ein textView) kann dieses nur durch den Thread verändert werden, durch den es auch erzeugt wurde. In diesem Beispiel ist dies der Main-UI-Thread (nicht der neue Thread).

Schauen Sie sich dazu auch den Artikel Painless Threading auf der Android-Entwickler-Seite an. Ein weiteres Tutorial dort zeigt wie mittels des Handler die UI eines Threads aktualisiert werden kann.

Eine Handler-Instanz wird an den Main-UI-Thread gebunden und ist so in der Lage das UI-Objekt zu aktualisieren, wenn Benachrichtigungen von anderen Threads empfangen werden.

Als nächstes wird eine Unterklasse der Handler-Klasse erzeugt und die handleMessage Methode überschrieben. msg.obj enthält die Nachricht aus dem anderen Thread.

private Handler uiHandler = new UIHandler();

class UIHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        // a message is received; update UI text view
        textView.setText(msg.obj.toString());
        super.handleMessage(msg);
    }
}

Erstellen Sie in dem länger laufenden Thread das Message-Objekt und übergeben Sie es an den Handler. Verwenden Sie Message.obtain() um eine Nachricht aus dem Pool wieder verwendbarer Objekte zu übernehmen.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView);
    textView.setText("hello World");

    thread = new Thread() {
        public void run() {
            try {
                // simulate long running operation
                Thread.sleep(5000);

                // create message which will be send to handler
                Message msg = Message.obtain(uiHandler);
                msg.obj = "new value...";
                uiHandler.sendMessage(msg);

            } catch (InterruptedException e) {
                Log.e(TAG, "run in thread", e);
            }
        }
    };
    thread.start();
}

Das uiHandler-Objekt erhält die Nachricht und gibt den Inhalt im UI-Objekt aus.

Falls Sie unterschiedliche Threads haben und kontrollieren möchten woher die Nachricht kommt, können Sie msg.what verwenden um die Nachricht zu identifizieren:

class UIHandler extends Handler {
    private static final int ID_0 = 0;
    private static final int ID_1 = 1;

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case ID_0:
            // a message is received; update UI text view
            if (msg.obj != null)
                textView.setText(msg.obj.toString());
            break;

        default:
            break;
        }
        super.handleMessage(msg);
    }
}

Ändern Sie den Thread um eine Nachricht zu senden (benutzerdefinierter Code):

// create message which will be send to handler
Message msg = Message.obtain(uiHandler, UIHandler.ID_0);
msg.obj = "new value...";
uiHandler.sendMessage(msg);

Subversion in Android Projekten (bei Verwendung von Eclipse)

Es ist eigentlich nicht erforderlich den bin und gen Ordner im Subversion-Repository zu halten. Weil der Inhalt dieser beiden Ordner generiert wird, kann man sie genauso gut zur Liste der nicht zu beachtenden Ordner (ignore lis) hinzufügen.
bin und gen Ordner

Aber was passiert wenn das Projekt mit Eclipse kompilieren und erstellt wird?

Eclipse kopiert die versteckten .svn Ordner aus dem Quellverzeichnis in das bin Verzeichnis.

Hier ist die Lösung: Gehen Sie in die Projekt-Eigenschaften (Project Properties) und wählen Sie Java Build Path aus. Fügen Sie nun das Ausschluss-Muster **/.svn/ zum Quellverzeichnis hinzu.

Java Build Path: exclusion **/.svn/

Beim Aufräumen (Cleaning) und das Erstellen des Projektes kann der .svn Ordner entfernt werden. Beachten Sie daher, dass die Datei .classpath jetzt den folgenden, neuen Eintrag beinhaltet: <classpathentry excluding=“**/.svn/“ kind=“src“ path=“src“/>

Threads in Android Teil 1 (Endlosschleife)

Diese Serie zeigt Tipps und Tricks über die Verwendung von Threads in der Android-Entwicklung. Teil 1 beschreibt, wie man einen Thread mit einer Endlosschleife erzeugt. Da der Thread nur gebraucht wird wenn die Anwendung läuft, muss zunächst sichergestellt werden, dass der Thread stoppt, sobald die Anwendung nicht mehr aktiv ist.

In diesem Beispiel haben wir einen Thread erstellen, der alle n Sekunden ausführen wird.
Schauen Sie sich den nachfolgenden Quelltext an. Die onCreate Methode der Activity wird überschrieben und in einem separaten Thread wird die Aufgabe abgearbeitet. Dieser Thread wird nach erledigter arbeit „Schlafen“ geschickt.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    thread = new Thread() {
        public void run() {
            while (true) {
                try {
                    // do something here
                    Log.d(TAG, "local Thread sleeping");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Log.e(TAG, "local Thread error", e);
                }
            }
        }
    };
    thread.start();
}

Was passiert, wenn die Anwendung pausiert, z.B. wenn eine andere Anwendung in den Vordergrund gebracht wird?
Der nachfolgende ScreenShot vom Debugger zeigt, dass der Thread weiterhin ausgeführt wird wenn die Anwendung nicht aktiv ist. Debugger: Der Thread läuft noch

Es ist also erforderlich den Thread zu kontrollieren und ihn in der onPause-Methode zu stoppen.

Die Lösung dazu stammt aus dem Google Artikel "Updating the UI from a Timer" (http://developer.android.com/resources/articles/timed-ui-updates.html). Wir instanziieren einen Handler der es uns erlaubt ablauffähige-Objekte (Runnable Objects) verarbeiten können.

Der Handler ist an den Main-UI-Thread der Activity gebunden.
Der neue Thread wird durch den Aufruf handler.postDelayed(this, 1000); zur "Message-Queue" des Main-UI-Thread hinzugefügt und nach 1 Sekunde ausgeführt.

private Thread thread;
private Handler handler = new Handler();

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	
	thread = new Thread() {
		public void run() {
			// do something here
			Log.d(TAG, "local Thread sleeping");
			handler.postDelayed(this, 1000);
		}
	};
}

Der Rest ist einfach. Wir beginnen den Thread in onResume, welches nach onCreate aufgerufen wird, sobald die Activity durch den Aufruf von handler.postDelayed(thread, 0); in den Vordergrund kommt (Parameter 0 zeigt, dass es keine Verzögerung gibt).

Bevor der Thread zum Handler hinzugefügt wird, muss er entfernt werden um sicherzustellen, dass er nicht schon an den Handler gebunden ist. Das kann mit onPause erledigt werden:

@Override
protected void onResume() {
	super.onResume();
	
	handler.removeCallbacks(thread);
	handler.postDelayed(thread, 0);
	Log.d(TAG, "onResume");
}

@Override
protected void onPause() {
	super.onPause();

	handler.removeCallbacks(thread);
	Log.d(TAG, "onPause");
}

Wenn man sich die Debugger anschaut, merkt man, dass der Thread gestoppt wird wenn die Activity pausiert. Threads in Android - onResume  onPause