2014-01-30

Neues in TKWeek: Zuletzt angeklickt

Ich habe in das Kalender-Modul von TKWeek eine Art Verlauf eingebaut. Die App merkt sich die letzten drei angeklickten Tage. Das ist praktisch, wenn man auf ein Datum öfter zugreifen möchte. Dann nämlich muss man nicht mühselig durch Monate und Jahre blättern, sondern tippt das Datum einfach an.

Screenshot: TKWeek-Kalender mit Verlauf

Gefällt Ihnen diese neue Funktion? Schreiben Sie mir…

2014-01-26

Über den Tellerrand gucken

Unboxing Surface 2 (1/7)
Unboxing Surface 2 (2/7)
Unboxing Surface 2 (3/7)
Unboxing Surface 2 (4/7)
Unboxing Surface 2 (5/7)
Unboxing Surface 2 (6/7)
Unboxing Surface 2 (7/7)
Wer mein Weblog schon länger verfolgt, weiß, dass ich in Bezug auf Gadgets unersättlich bin. Deshalb musste ich mir natürlich irgendwann ein Surface holen. Dieses Irgendwann war vorgestern.
Die ersten Eindrücke sind rundweg positiv. Zugegeben, das Gerät ist verglichen mit anderen Tablets schwer. Aber das war mein Xoom auch. Und: das Surface 2 ist toll verarbeitet und wirkt sehr wertig. Die Oberfläche macht Spaß. Man kommt flott voran. Auf dem klassischen Desktop bin ich kaum. Wozu auch. Ich kann kein Java installieren, kein Eclipse, kein Android SDK. Vieles, was mir auf dem PC im Laufe der Jahre ans Herz gewachsen ist, ebenso wenig. Stört das? Nein. Das Surface ist für mich eben ein Tablet. Mit dem will ich surfen, Video und Fotos gucken, spielen, Musik hören, E-Books lesen, mal auf Facebook vorbei schauen. Das kann das Surface 2. Sogar sehr gut. Wenn ich einen klassischen PC brauche, nehme ich den.
Niemand erwartet vom iPad, dass es Mac OS X-Software ausführt. Warum also vom Surface? Klar, Microsoft geht gezwungenermaßen mit beiden Welten hausieren. Weil es etliches noch nicht als Windows Store App gibt. Aber mit x86-Hardware (zum Beispiel im Surface Pro 2) kann der Käufer ja beides haben. Das ist der Unterschied zu Apple. Dort wird man immer zwei Geräte (Mac und iPad) kaufen müssen.
Das Surface, wie es vor mir liegt, ist definitiv kein hybrid-Gerät. Sein Desktop, wenn Sie so wollen, für die Katz’. Die Kernfunktionen, die man von einem Sofa-Gerät erwartet, bedient es aber hervorragend. Ich würde mir wünschen, dass Microsoft seine Entwicklungslinien differenzierter kommuniziert. Dann gäbe es vielleicht sogar weniger schlechte Kritiken.

2014-01-25

Nachhaltig programmieren

Im Teil 29 von Ultimate Swing hatte ich Ihnen gezeigt, wie leicht man mittels Drag and Drop in Notes and Tasks Aufgaben hinzufügen kann. In diesem Zusammenhang gab es eine kleine Hausaufgabe:

Bauen Sie doch einmal eine Testdatei und ziehen diese auf das Hauptfenster. Einige Aufgaben werden angelegt, aber nicht alle. Sind Sie tief genug mit dem Quelltext vertraut, um herauszufinden, was da schief geht? Das Problem liegt in einer Methode, die in fileDropped() aufgerufen wird.

Um das Problem zu identifizieren, müssen wir zunächst einen Blick auf die genannte Methode werfen. Sie liest die Datei mit den Aufgaben zeilenweise ein, erzeugt Task-Objekte und speichert diese. Dass Aufgaben beim Erzeugen verloren gehen, scheint unlogisch. Eher schon könnte es Probleme beim Schreiben auf Platte geben. Dann nämlich, wenn eine bereits vorhandene Aufgabe überschrieben wird. Die Methode save() der Klasse LocalTasksService nutzt die Eigenschaft id einer Aufgabe als Dateiname. Wäre die Id mehrfach vorhanden, käme es zu dem beschriebenen Phänomen. Sehen wir uns nun an, wie Aufgaben erzeugt werden. Dies geschieht in der Methode createTask(). Sie sah damals folgendermaßen aus:

 public static Task createTask(String text) {  
  return new Task(text, System.currentTimeMillis(), null,  
    Long.toString(System.currentTimeMillis()), false, null);  
 }  

Die Id wird also auf System.currentTimeMillis() gesetzt. Klingt doch recht sauber, oder? Nein, im Gegenteil. Die Wahrscheinlichkeit, dass das Erzeugen von zwei Aufgaben (während dem zeilenweisen Lesen) weniger als 1 Millisekunden braucht, ist recht hoch. Dass genau hier das Problem liegt, lässt sich recht einfach belegen, indem man von jeder erzeugten Aufgabe die Id ausgibt.

Sehen Sie sich nun an, wie ich das Problem löse:

 public static Task createTask(String text) {  
  return new Task(text, System.currentTimeMillis(), null,  
    UUID.randomUUID().toString(), false, null);  
 }  

Auch UUID.randomUUID() birgt das potenzielle Risiko einer Kollision. Die Wahrscheinlichkeit hierfür ist aber vergleichsweise gering.

Was will ich damit sagen? Wir treffen im Rahmen der Programmierung regelmäßig Entscheidungen, die auf Annahmen beruhen. In meinem Fall war dies die Annahme, dass Aufgaben nicht öfter als alle 2 Millisekunden angelegt werden. Sofern der Benutzer die Aufgaben von Hand anlegt, stimmt das ja auch. Der Anwendungsfall Stapelverarbeitung war damals nicht im Fokus. Und deshalb kam die ursprüngliche Implementierung damit nicht zurecht. Falsch wäre, jede mögliche Änderung im Voraus einplanen zu wollen. Vielmehr rate ich zu einer gesunden Vorausschau. In meinem Fall wäre dies das Beantworten der Frage: Ist eine Id auf der Basis von Millisekunden genau genug? Falls Sie jetzt sagen: “Na, dann soll er halt den Dateinamen anders aufbauen” – ja, stimmt, könnte ich. Aber vielleicht tritt das Problem mit der zu ungenauen Id an anderer Stelle ja auch auf. Fixe ich nur den Dateinamen, habe ich keine nachhaltige Lösung. Fixe ich die Id selbst, bin ich in jedem Fall auf der sicheren Seite.

2014-01-19

Android-Tipp: DoneBar

Das Eingeben/Erfassen von Daten wird in modernen Android-Apps in jeweils eigenen Activities erledigt. Diese haben eine besonders aussehende Action Bar. Wie man das in eigenen Programmen umsetzt, hat Google in einer Demo zusammengefasst. Nach dem Download hat man die folgende Verzeichnisstruktur vor sich:

Verzeichnisstruktur der DoneBar-Demo

Das README schlägt vor, das Projekt direkt mit Gradle zu bauen oder in Android Studio zu importieren. Die zweite Variante bringt aber zumindest unter der derzeit aktuellen Version 0.4.2 einige Nacharbeiten mit sich. Die IDE meldet nämlich:

Failed to refresh Gradle project 'DoneBar' You are using Gradle version 1.8, which is not supported. Please use version 1.9. Please point to a supported Gradle version in the project's Gradle settings or in the project's Gradle wrapper (if applicable.) Fix Gradle wrapper and re-import project Gradle settings

Zum Glück gibt es kaum eine Frage, auf die Stackoverflow keine Antwort parat hat. So wird man auch hier fündig. Zusätzlich musste ich noch eine veraltete Version der Android SDK Build-tools (18.0.1) herunter laden und alle installierten Pakete mit den Android SDK Manager auf den aktuellen Stand bringen. Ganz schln viel Brimborium, für eine – zugegeben hübsche – Demo:

Screenshot DoneBar (1/3)
Screenshot DoneBar (2/3)

Screenshot DoneBar (3/3)

2014-01-18

Wie geht es weiter mit Windows?

In seinem Artikel Windows 9 in 2015: Desperation isn't pretty beschäftigt sich Steven J. Vaughan Nichols mit der Frage, wie es mit Microsoft und Windows weiter geht. Die These: Windows 8 war ein Reinfall und mit dem schon 2015 kommenden Nachfolger wird es nicht besser. Sein Vorschlag: Windows 8 vergessen und mit einem generalüberholten Windows 7 einen Neustart machen. Denn: die Leute hassen den neuen Look und lieben Aero.
Meine Meinung? Absurd. Natürlich hat Microsoft mit der umfassenden Fokussierung auf die neue Geräteklasse Tablets seine altgedienten Nutzer vor den Kopf gestoßen und überfordert. Natürlich ist dem Enterprise die Kachel egal. Und ja – Microsoft hat den Desktop verleugnet. Dabei darf aber nicht übersehen werden, dass mit dem Einbrechen des PC-Marktes für Microsoft deutlich geworden ist, dass sie auf das neue Terrain (die Tablets) nicht vorbereitet waren. Redmond hat einfach ein Händchen für das Verschlafen von Veränderungen. Wie praktisch bei jedem unerwarteten Manöver fallen die Reaktionen dann eben extrem aus.
Ich denke aber, Microsoft hat begriffen, dass der Desktop nicht tot ist. Man wird deshalb in der kommenden Version viele Verbeugungen vor diesem Klientel sehen können. Insbesondere wird die neue Welt nahtloser mit der alten verbunden werden. Aber: Microsoft braucht auch die Kacheln. Denn letztlich hat sich der Markt eben doch stark verschoben. Viele Leute wollen nur noch ein Tablet und eben keinen PC mehr.
Auch Apple wird irgendwann vor der Frage stehen, ob sie zwei Systemlinien (Mac OS X und iOS) fortentwickeln wollen. Ich behaupte, auch hier wird es langfristig zu einer Verschmelzung kommen. Ob Cupertino dann ein glücklicheres Händchen hat, wird sich zeigen. Der Port von iOS-Apps auf Mac OS X macht meiner Meinung nach nur bedingt eine gute Figur. Ich habe schon darüber gebloggt.

2014-01-16

Endlich fair phonen

Eine gefühlte Ewigkeit musste ich auf mein FairPhone warten. Die Bestellung hatte ich am 9. Juni 2013 abgeschickt. Gestern (15. Januar 2014) wurde das gute Stück endlich zugestellt. Das sind 220 Kalendertage. Man kann nicht sagen, dass sich die Macher während dieser Zeit nicht via E-Mail, Facebook und Blogposts um die Käufer gekümmert hätten. Unglücklicherweise bedeutete aber jeder Nachricht eine weitere Verzögerung. Klar – wer sich auf ein solches Projekt einlässt (indem er das Gerät bestellt), muss mit Wartezeiten rechnen. Und tut dies auch. Welchen Problemen die mehrfachen Aufschübe geschuldet waren, fasst ein schöner Kurztest des FairPhones auf heise.de zusammen. Klar ist auch, dass das junge Unternehmen viele Dinge erst lernen musste. Ich denke, auch hierfür hat jeder der 25.000 Kunden Verständnis. Dennoch hätte ich den freundlich gemeinten Rat an die Macher, künftig konservativere Termine zu kommunizieren. Selbst nach dem vermeintlichen “Jetzt geht es los!” kam es zu erneuten Verzögerungen. Diese sind aber offen im Production and Distribution Update dokumentiert. Insgesamt empfinde ich das Engagement der Amsterdamer als bewundernswert, höchst engagiert und kundenfreundlich.
Jetzt zum Unboxing. Das Telefon wird ohne Netzteil geliefert. Da ich kein solches übrig habe, hatte ich mir eines kommen lassen. Und einen hübschen Schutz für die Rückseite des Smartphones. Genau dieser ließ mich übrigens noch länger warten (siehe das oben erwähnte Production and Distribution Update).
FairPhone Unboxing 1
FairPhone Unboxing 2
FairPhone Unboxing 3
FairPhone Unboxing 4
FairPhone Unboxing 5
FairPhone Unboxing 6
FairPhone Unboxing 7
FairPhone Unboxing 8
FairPhone Unboxing 9
Das Gerät und das Zubehör machen einen wertigen Eindruck. Ja – es ist schwerer und dicker als die aktuelle Collection anderer Hersteller. Ich finde aber, das FairPhone liegt gut in der Hand. Es liegt gut in der Hand. Die Bedienung geht flüssig von statten. Das Display ist schön scharf.
Schön ist, dass kaum unnötiger Kram mitgeliefert wird. Keine Stapel von Konformitäts- und Garantieerklärungen. Dafür aber ein richtiges (gedrucktes) Handbüchlein. Die Verpackung ist zweckmäßig, schlicht, aber stabil und (wer möchte) leicht entsorgbar.
Alles in allem hat sich für mich das Experiment gelohnt. Wer mich kennt, weiß dass ich ein Gadget-Junkie bin. Trotzdem (oder gerade deshalb?) wird das FairPhone für die nächste Zeit mein täglicher Begleiter.

2014-01-10

Leaking this in constructor

Manche Dinge lassen sich sehr schnell beheben. Träge, wie man nun mal ist, schiebt man scheinbare Kleinigkeiten aber oft zu lange vor sich her. Hier also ein Appell an das schnellere Beseitigen von Unzulänglichkeiten.

Im Quelltext von Notes and Tasks finden sich diverse Stellen, an denen in NetBeans ein Leaking this in constructor moniert wird. Hier ein Beispiel:

 public class TasksView extends BasePanel implements ActionListener,  
     ListSelectionListener, ListDataListener {  
   
    [...]  
   
   public TasksView(ListModel modelTasks) {  
     super(Constants.LARGE_GAP, Constants.LARGE_GAP);  
     // create actions  
     actionChangeCategory = ActionUtilities.createAction(  
         ACTION_CHANGE_CATEGORY, this);  
     actionChangeState = ActionUtilities.createAction(ACTION_CHANGE_STATE,  
         this);  
     actionDelete = ActionUtilities.createAction(ACTION_DELETE, this);  
   
    [...]  

Die Warnung erfolgt beim Aufruf meiner Methode ActionUtilities.createAction(). Sie sieht folgendermaßen aus:

      public static Action createAction(String actionName,  
                final ActionListener listener) {  

Das this beim Aufruf bezieht sich auf den zweiten Parameter, die Referenz auf einen ActionListener. Die aufrufende Klasse implementiert dieses Interface. Soweit doch alles in Ordnung, oder?

Die Warnung erscheint, weil der Methodenaufruf aus dem Konstruktor heraus erfolgt. Möglicherweise ist das Objekt zu diesem Zeitpunkt noch nicht vollständig initialisiert. Das kann die aufgerufene Methode allerdings nicht wissen. Es besteht also die Gefahr, dass sie auf unvollständigen Daten operiert. Oft sind Nullpointer die Folge. Wie man das Problem löst, ist situationsabhängig. Ignorieren ist aber keine gute Idee.

Zwinkerndes Smiley

Im konkreten Fall weiß ich, dass die Methode createAction() die Referenz nur für den späteren Gebrauch speichert. Deshalb kann ich sicher auf ActionListener casten und somit die Warnung verschwinden lassen. Das ist aber nicht immer ein geeignetes Mittel.

Fazit: Warnungen erscheinen aus einem bestimmten Grund. Sie möchten den Entwickler daran erinnern, genau zu prüfen, ob das, was er geschrieben hat, auch wirklich das ist, was er möchte.

Hatten Sie diese Warnung schon? Wie sind Sie damit umgegangen? Schreiben Sie mir…

2014-01-06

Ultimate Swing, Teil 30

Willkommen zurück zu einer neuen Folge meiner unendlichen Geschichte über Swing. Heute begeben wir uns in die Tiefen der privaten Teile der Klassenbibliothek. Und zwar, um dort ein wenig zu spicken…
Smiley

Notes and Tasks hat eine Eingabezeile, die mit dem Shortcut Alt-I aktiviert wird.

Screenshot von Notes and Tasks
Screenshot von Notes and Tasks

Bislang hatte ich sowohl den ausgegebenen Text als auch den korrespondierenden KeyStroke fest verdrahtet. Das ist aber fehleranfällig, wenn ich beschließe, die Auslösekombination zu ändern. Denn neben der Deklaration meiner Konstante muss ja auch der Meldungstext angepasst werden. Er wurde in eine Properties-Datei ausgelagert.

 public static final KeyStroke KEY_ALT_I = KeyStroke.getKeyStroke('I', KeyEvent.ALT_DOWN_MASK);  

Die Frage ist deshalb, ob es eine Methode gibt, die das Tastenkürzel als Klartext (String) zurück liefert. Die Klasse KeyStroke bietet nichts Passendes: toString() produziert die Ausgabe alt pressed I. Die Methoden getKeyChar(), getKeyCode() und getModifiers() liefern nur Teilinformationen. Aber da wäre noch die Klasse KeyEvent. Sie enthält unter anderem getKeyModifiersText() und getKeyText(). Leider aber auch hier kein fertiger Text.

Dass es etwas Passendes geben muss, scheint auf der Hand zu liegen. Menüeinträge zeigen ihre Shortcuts ja schließlich auch an. Wenn Sie den Quelltext von JMenuItem zücken, werden Sie dort aber nichts finden. Das Malen der Shortcuts ist nämlich Aufgabe des UI delegates. Für ein Menüelement ist deshalb eine mögliche Fundstelle in der Klasse BasicMenuItemUI versteckt. Sehen Sie sich bitte deren private Methode paintAccText() an. Acc ist hierbei die Abkürzung von Accelerator (ein Synonym für Tastenkürzel). Ihr wird (unter anderem) ein Objekt des Typs MenuItemLayoutHelper übergeben. Diese Klasse befindet sich im Paket sun.swing, ist also nicht Teil der öffentlichen API. Ihre parameterlose Methode getAccText() ist eine für uns unnötige Iteration. Die von ihr zurück gegebene Variable accText wird innerhalb der Methode reset() belegt. Damit sind wir endlich am Ziel, der leider nicht direkt aufrufbaren Methode private String getAccText(String acceleratorDelimiter).

Vielleicht fragen Sie sich nun, was uns diese Detektivarbeit bringt. Verwenden können und dürfen wir die Methode ja nicht direkt. In diesem Falle halte ich es aber für legitim, die Logik in angepasster Form als eigene kleine Hilfsmethode zur Verfügung zu stellen. Mir ist keine Klasse bekannt, die eine vergleichbare Funktionalität von Haus aus bietet. Kennen Sie eine? Melden Sie sich…

2014-01-04

"Lean UX" von Jeff Gothelf, O'Reilly Media

I review for the O'Reilly Blogger Review ProgramDer erste Teil dieses Posts ist auf Englisch, weil ich am O'Reilly Blogger Review Program teilnehme, das ein internationales Publikum anspricht. Der zweite Teil des Posts enthält die inhaltlich gleiche Version in Deutsch.
The first part of this post is written in English, because I participate in the O'Reilly Blogger Review Program which is intended for an international audience. The second part contains the German version of my review.

Lean UX is a new approach to interaction design. In his book Jeff Gothelf advises the reader to rapidly experiment with design ideas, validate them with real users and continually adjust the design based on the result of this ongoing (iterative) process.
As other agile development theories do, Lean UX focuses on actual experience rather than predefined deliverables. The book encourages close collaboration among the product team, constant learning from feedback and fast adoption. The idea is to develop design in short, iterative cycles. The book is easy to read. It contains many practical tips. I found Lean UX helpful and comprehensive.

Lean UX ist ein neuer Ansatz des Designs von Benutzerinteraktionen. In seinem Buch rät Jeff Gothelf dem Leser, in schneller Folge mit Designideen zu experimentieren, sie mit echten Anwendern zu überprüfen und das Design auf Basis der Erkenntnisse dieses zyklischen Prozesses kontinuierlich anzupassen.
Wie alle anderen agilen Theorien konzentriert sich Lean UX auf tatsächliche Erfahrung statt auf im Vorfeld definierte Liefergegenstände. Das Buch ermutigt zu enger Zusammenarbeit im Produktionsteam, zu ständigen Lernen von Rückmeldungen und zu schneller Anpassung an geänderte Voraussetzungen. Die Idee ist, das Design in kurzen, iterativen Zyklen zu entwickeln. Das Buch ist einfach zu lesen. Es enthält viele praktische Tipps. Ich finde Lean UX umfassend und hilfreich.

2014-01-01

Leichtfüßiger Android-Emulator mit Startschwierigkeiten

Der Android-Emulator war noch nie ein Performance-Wunder. Im Laufe der Jahre wurde er mit den steigenden Anforderungen der Android-Versionen zunehmend schlecht fertig. Ein Tiefpunkt war mit dem Erscheinen von Android 3 erreicht. Selbst mit potenter Hardware war der Emulator kaum bedienbar. Natürlich hat Google versucht, gegen zu steuern. Mit den SDK Tools, Revision 17 wurde im März 2012 eine hardwarebeschleunigte Grafikausgabe eingeführt. Noch mehr Performance lässt sich durch die Nutzung von Android-x86-Systemabbildern in einer speziellen virtuellen Maschine, die die Virtualisierungstechnologie moderner Prozessoren nutzt, erzielen. Entsprechender Support ist seit derselben SDK Tools-Version vorhanden. Unter Windows ist hierzu der Intel® Hardware Accelerated Execution Manager zu installieren. Was beim Einrichten zu beachten ist, hat Google hier zusammen gefasst. Oft muss die Virtualisierungstechnologie erst im BIOS des PCs aktiviert werden. Bei Konflikten mit anderen Produkten sollten diese während der Nutzung des Android-Emulators deaktiviert werden. Treten gar Abstürze des Hostsystems auf, müssen Sie nach Updates Ausschau halten. Auf meinem Vaio Pro hatte sich Windows 8.1 regelmäßig mit einem Bluescreen of Death (CRITICAL_STRUCTURE_CORRUPTION) verabschiedet. Auf Intels Hardware Accelerated Execution Manager-Produktseite habe ich einen entsprechenden Hotfix entdeckt. Seitdem läuft mein Vaio wieder zuverlässig.