Fwd „Thoughts on Flash“

Der Herr Jobs hat sich öffentlich und ausführlich zum Thema Flash geäußert.

Mein persönliches Highlight ist

We have routinely asked Adobe to show us Flash performing well on a mobile device, any mobile device, for a few years now. We have never seen it. Adobe publicly said that Flash would ship on a smartphone in early 2009, then the second half of 2009, then the first half of 2010, and now they say the second half of 2010.

Und da behaupten manche noch immer, ein 3rd-Party-Provider wäre nicht so schlimm. Hallo, ein Jahr und mehr Verzögerung? Und es ist ja zu erwarten, dass die Performance erst mit weiteren Updates steigen wird.

If developers grow dependent on third party development libraries and tools, they can only take advantage of platform enhancements if and when the third party chooses to adopt the new features. We cannot be at the mercy of a third party deciding if and when they will make our enhancements available to our developers.

Das kommt doch meinem Beitrag schon nahe..

For example, although Mac OS X has been shipping for almost 10 years now, Adobe just adopted it fully (Cocoa) two weeks ago when they shipped CS5. Adobe was the last major third party developer to fully adopt Mac OS X.

Gut, verstehen viele Windowsnutzer nicht. Die arbeiten noch, oder bis vor kurzem, mit einem uralten Windows XP. Was sind da 10 Jahre..

Java Reflection API – mit Cache, ey!

Einführung in Reflection

Die Java Reflection API ist Bestandteil des JDK und ermöglicht den Zugriff auf Methoden, Felder und Annotationen von Klassen und Objekte, also den Instanzen von Klassen. Mit Ausnahme von Annotationen geschieht der Zugriff immer auf einer Meta-Ebene.

Mit einem einfachen Beispiel ist diese Meta-Ebene erklärt.

class Foo {
private String bar = "private value";
}

Stellen wir uns vor, wir wollen ganz exemplarisch von dem Objekt foo (foo = new Foo())den Inhalt von dem Feld bar erhalten. Auf dem üblichen Weg ist das nicht möglich, da das Feld ein privates ist.

Über den zugehörigen Klassentyp (via getClass()) stellt Java eine Reihe von Methoden aus dem Reflection-Paket zur Verfügung, u.a. auch Field getDeclaredField(String). Ein declared field ist ein Instanzattribut eines Objektes, während ein field (Field getField(String)) statische Felder finden.

Field field = foo.getClass().getDeclaredField('bar');

Die Variable field enthält aber – wie man dem Typ entnehmen kann – aber nicht den Inhalt von bar, sondern ein Field-Objekt. Um an den Inhalt zu kommen, verwendet man die Methode get(Object). Der Parameter bezeichnet den entsprechenden Kontext, also das Objekt foo von weiter oben.

Object value = field.get(foo);

Das bedeutet im Klartext: Auch für eine weitere Instanz der Klasse Foo (etwa foo2) kann dieses Field für dieses Feld verwendet werden. Es beinhaltet nur die Metainformationen, nicht die eigentlichen Inhalte.

Dabei ist jedoch zu beachten, dass der SecurityManager aktiv werden kann; in diesem Falle wird eine Exception bei get(Object) geschmissen, weil das Feld nicht sichtbar ist.

field.setAccessible(true);

schafft dabei Abhilfe.

Und Methoden?

Im Prinzip funktioniert das Schema bei Methoden genauso – aber bevor ich jetzt einen Radio Eriwan-Witz loslasse…

Im Gegensatz zu den vorherigen Feldern werden Methoden nicht nur über den Namen, sondern auch über die Parametertypen identifiziert (daher können Methoden sich vom Namen her in Java auch „überladen“).

class Bar {
private String name;
String getName() {
return name;
}
String getName(String defaultName) {
return (name == null) ? defaultName : name;
}
}

Die Metainformationen werden entsprechenderweise geladen:

Method method1 = bar.getClass().getDeclaredMethod("getName");
Method method2 = bar.getClass().getDeclaredMethod("getName", String.class);

Selbstverständlich ist bei Kenntnisnahme der Klasse auch folgende Zeile gleichwertend:

Method method1 = Bar.class.getDeclaredMethod("getName");

Methoden haben im Gegensatz zu Feldern keinen „Inhalt“, sondern sind eine Aktivität oder Operation — und zwar auf einem Objekt. Dementsprechend funktioniert das Ausführen mittels der Method Object invoke(Object, Object…args):

Object result1 = method1.invoke(bar); // getName()
Object result2 = method2.invoke(bar, "default name"); // getName("defaultName")

Und Annotationen?

Annotationen sind „Anmerkungen“ im Quellcode, und seit Java 5 fester Bestandteil des JDKs. Technisch gesehen sind Annotationen besondere Klassen mit eingeschränkten (Java-) Möglichkeiten, deren Instanz auf einem Meta-Attribut wie Klasse, Feld, Methode oder Methodenparameter „hängen“. Mit anderen Worten bedeutet dass, dass man mittels Reflection tatsächlich eine Instanz einer Annotation erhält — allerdings mittels einem Proxy (aus der Java API).

@Entity
class Account {
@Column(name="id_x")
private Long id;
}

Die Methode getAnnotation(Class) existiert in allen Typen: Class, Field und Method.

Account account = new Account();
Entity annotation1 = Account.class.getAnnotation(Entity.class).annotationType();
Column annotation2 = Account.class.getDeclaredField("id").getAnnotation(Column.class).annotationType();
Assert.assertEquals("id_x", annotation2.name());

Performance

Im Regelfall benötigt man keinen Zugriff auf Reflection und sollte es vermeiden. Nichtsdestotrotz gestalten sich Konfigurationen, welche auf Basis von Annotationen, meist als sehr einfach, simpel und vor allem Code-konzentriert. Das JPA-Mapping (etwa mit Hilfe von Hibernate) gestaltet sich via Annotationen wesentlich einfacher, schneller und schlanker als mit XML.

Jeder Zugriff über die Reflection API kostet Zeit. Für Konfigurationen und Pläne ist das meist nebensächlich; die JPA-Konfiguration wird beim Start der Applikation eingelesen, analysiert und gespeichert. Nebenbei profitiert das natürlich von der anfänglichen „Warmup-Phase“.

Besteht jedoch ein Access on Demand, so sollte man sich Gedanken um eine geeignete Cachestruktur machen. Wichtig ist dabei, die Metadaten von den eigentlichen Inhalten zu trennen.

Cachen & Lösungsansätze

Jeder der oben genannten Reflection-Getter gibt es auch jeweils eine getAll-Variante: Class.getDeclaredFields(),Class.getDeclaredMethods(), Class.getAnnotations(), Field.getAnnotations() und Method.getAnnotations().

Mit einem einfachen Algorithmus kann man die Laufzeit drastisch senken, wenn Objekte des gleichen Typs mittels Reflection untersucht werden.

public class CacheReflectionUtil {
// cache of declared fields of class types
private final Map<Class<?>, Field[]> classDeclaredFields = new HashMap<>();
// Return the declared fields of the given class type.
public Field[] getDeclaredFields(Class<?> type) {
Field[] fields = classDeclaredFields.get(type);
if (fields == null) {
fields = type.getDeclaredFields();
classDeclaredFields.put(type, fields);
ensureAccessibility(fields);
}
return fields;
}
// Ensure that the given fields are accessible.
public void ensureAccessibility(Field[] fields) {
for (Field field : fields) {
// setAccessible will not only set a property but invoke SecurityManager stuff
if (field.isAccessible()) {
field.setAccessible(true);
}
}
}
// Return the declared field of the given class type.
public Field getDeclaredField(Class<?> type, String name) {
for (Field field : getDeclaredFields(type)) {
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
}

Um einen stetigen Speicherverbrauch zu verhindern, empfiehlt sich das Nutzen einer Least-Recently-Used-Struktur. Das Apache-Commons-Paket bietet dies etwa mit der LRUMap an.

Context Affair

Bei Spring ist die globale Einheit in der Konfiguration der so genannte ApplicationContext. Dieser Context ist etwa ein Container im Applicationserver (beispielsweise web.xml). Soweit so gut.

Tatsächlich ist der WebApplicationContext eine Spezialisierung des oben genannten ApplicationContext — und zuständig für Webanwendungen. Da ein Servlet die Steuerung im Applicationserver übernimmt, benötigt der Spring-Context ein DispatcherServlet. Damit ist die „Verbindung“ User->Server->Spring geschaffen. Soweit so gut.

Konfiguriert man das DispatcherServlet etwa – sinnigerweise – mit dem Namen“dispatcher“, dann sucht Spring standardgemäß nach einer dispatcher-servlet.xml. Diese XML-Konfigurationsdatei kann ähnlich der applicationContext.xml (u.ä.) ganz normale Bean-Konfigurationen enthalten. Interessant ist dabei, dass dabei ein zusätzlicher ServletContext erstellt wird. Das hat zwei Auswirkungen:

  1. Beans aus dem ServletContext sind nicht im ApplicationContext verfügbar
  2. Annotations-gestützte Konfigurationen müssen jeweilse in beiden Contexten konfiguriert, also aktiviert, werden

Shortcut of the week

Das ursprüngliche Problem: Ich habe eine URL zu einem MP4-Video, welches der Safari sofort abspielen will. Allerdings will ich das Video erst später gucken, also speichern. Hm, dumme Sache. Dem Link folgen von der ursprünglichen Seite ging aus bestimmten technischen Gründen nicht — und ein Dokument/Seiten speichern scheint zu mindestens nicht zu funktionieren, so lange das Video noch lädt.

Überraschenderweise ist es einfacher, als man denkt: Das Safari-Downloads-Fenster nimmt die Tastenkombination +V für Einfügen an — dementsprechend auch jegliche Drag ’n‘ Drop Aktionen, welche eine URL als Objekt haben.

Kurzer Gegenscheck: Nope, Firefox kann das nicht. 😉

Adobe vs. Apple

Es gibt derzeit eigentlich hauptsächlich zwei gute Artikel, wenn man sich mit etwas Hintergrundinformationen über diesen angeblichen „Krieg“ (laut Presse…) bereichern will.

Zum einen hat John Gruber bereits vor einigen Tagen hübsch zusammengefasst, was es sich mit dem Multitasking und der Section 3.3.1 (das ist der Abschnitt, der Adobe zur Zeit die Galle hoch kommen lässt). Der Grundtenor: Es geht nur um die Kontrolle der Plattform (siehe unten). Außerdem können die fremden, 3rd-Party-Tools nicht alle Features wie Multitasking automatisch. Apple ist also darauf angewiesen, dass der Dritte dies mit einbaut. Der Anwender bekommt sonst ein inkonsistentes Produkt, und das will Apple nicht. Das ist eine Strategie, die sich bei der iPhoneOS-Produktpalette nachweislich bezahlt hat.

Desweiteren hat ein Entwickler auf seinem Blog /dev/why!?! sehr schön aufgezeigt, wer hier eigentlich wen angegriffen hat. Dem kann ich nur beipflichten, ich zitier die interessantestem Stellen, der Titel lautet: It’s all about the framework.

Imagine if 10% of the apps on iPhone came from Flash. If that was the case, then ensuring Flash didn’t break release to release would be a big deal, much bigger than any other compatibility issues.

[…]

Shipping a release where they break a large percentage of apps is not generally an option. Letting any of these secondary runtimes develop a significant base of applications in the store risks putting Apple in a position where the company that controls that runtime can cause delays in Apple’s release schedule, or worse, demand specific engineering decisions from Apple, under the threat of withholding the information necessary to keep their runtime working.

[…]

So ultimately, preventing Flash on the platform is about control, but is not control over the user experience of the Flash applications, or even the languages used. It is about the runtimes they bring on to the system, and Apple’s control over future releases of iPhone OS.

Diese Problematik ist den meisten Trollen unserer hiesigen IT-Welt natürlich völlig fremd *g* Man muss sich das auf der Zunge zergehen lassen: Als Unternehmen einen potenziell nicht kleinen Teil der Anwendungen aus der Hand zu geben, mit allen Konsequenzen. Und das Haupteinfallstor Nummer 1 auf PCs (und auch Macs) ist… jawohl, Flash. Die Betriebssysteme (auch Windows) sind heutzutage meist sehr gut abgeriegelt, aber Flashlücken tauchen dauernd auf. Microsoft ist machtlos, Apple ist machtlos. Und den Pinguinen ist das total latte.. 🙂

There is no doubt in my mind that if they asked Apple to bless this they were rebuffed, and if they didn’t ask the only reason they didn’t was because they knew Apple would say no. In either event, they announced the product to their customers and sold them on an idea they were not in a position to deliver, hoping Apple would be unwilling to piss off developers by not fulfilling Adobe’s promises. They tried to force Apple’s hand by putting Apple in a position where in order stop the Flash they would have to do it publicly in front of Adobe’s users. That was a bad call on Adobe’s part.

Das ist eine völlig neue Perspektive: Das Ganze wäre nur ein taktisches Spielchen seitens Adobe, in welchem es nur zwei Auswege gab. Spiel ohne Trumpf, und mit hohem Einsatz. Und ganz wertfrei betrachtet: Es ist gut, wenn man nicht sofort vor anderen großen Firmen kuscht.

They can get an awesome, high performance, Flash environment working on Android, and get a bunch of great Flash apps running on Android phones. As much as Apple wants to control iPhone, I am willing to bet they want to beat Android more.

Tja, und da haben wir das Problem: Ich glaube nicht, dass Adobe das in der nächsten Zukunft machen werden. Die hatten genug Power und Geld, das in den letzten 3 Jahren zu machen; da bestand aber wohl keine (wirtschaftliche) Interesse. Warum sollten sie also jetzt damit anfangen?

Every day Adobe does not have a widely deployed mobile Flash, is a day they are not having Flash based mobile apps developed, and is a day the odds of mobile Flash being successful goes down a little bit.

[…]

That’s right, Adobe has been making the case for Flash on iPhone for 3 years, but still hasn’t deployed a non-lite version of Flash on any phones, even when Apple is not obstructing them.

Zusammengefasst lässt sich sagen: Für den Anwender kann das ganze nur positiv ausgehen — und deswegen ist es auch gut so:

  • Adobe schafft es tatsächlich und bringt mobil-optimierte Versionen der Flashruntime heraus, die zudem auch mit speziellen Geräteeigenschaften ausgestattet sind. Sie müssen zeigen, dass sie auch Neuerungen zeitnah (oder gleichzeitig) herausbringen. Stellt sich hingegen heraus, dass ein Flashplayer auf Android auch erst Monate später die neuen Features unterstützt, dann…
  • Adobe bringt wirklich mal einen optimierten Flashplayer für Mac OS X heraus (derzeit gibt es wohl eine WebKit nightly + 10.1-Beta, die ja scheinbar wirklich Welten besser ist).. wäre ja nach jahrelanger Stromverschwendung der CPU auch mal Zeit, oder?
  • Adobe schafft es nicht, und die Anwender können sich nicht über unzureichende Akkulaufzeiten oder Userinterfaces bei der Plattform „iPhone OS“ beschweren. Denn was ein Entwickler zu unterscheiden weiß, das kann der Anwender nicht („Ist das ein Flashspiel?“).

iPhone Webapps – Autokorrektur komplett deaktivieren

Für Webapps, also HTML-Seiten, gibt in speziellen Sitationen wie Loginformulare Problemen mit der Autovervollständigung bzw. -korrektur des iPhone OS.

Für input-Tags gibt es neben dem Attribut autocomplete=“off“ (unterstützt bspw. durch den Firefox) noch zwei weitere Attribute:

  • autocorrect=“off“ — die Autokorrektur abschalten
  • autocapitalize=“off“ — die Auto-Groß/Kleinschreibung-Korrektur abschalten

Alle drei Attribute zusammen schalten auf iPhone/iPad/iPad sämtliche Korrekturvorschläge ab.