Integration zwischen Android-Laufzeitkomponenten

Cross App Mashability
Kommentare

Die Fähigkeit, Anwendungsbausteine dynamisch zu kombinieren, ist ein mächtiges Alleinstellungsmerkmal der Android-Plattform, das bislang nur wenig ausgeschöpft wird. So können zum Beispiel Plug-in-Konzepte realisiert oder bereits vorhandene Funktionalität einbezogen oder ausgetauscht werden. Der folgende Artikel vertieft das grundlegende Verständnis der Android-Laufzeitumgebung und zeigt verschiedene Mechanismen auf, wie eine applikationsübergreifende Integration über Prozessgrenzen hinweg umgesetzt werden kann.

„Good programmers know what to write. Great ones know what to rewrite (and reuse)“ [1]. Dieses kurze Zitat aus „The Cathedral and the Bazar“ von Eric S. Raymond betont sehr schön, wie sinnvoll es ist, das Rad nicht neu zu erfinden, sondern stattdessen bereits vorhandene Funktionalität wiederzuverwenden. So spart man wertvolle Zeit und kann die eigenen Ressourcen stärker auf das Kerngeschäft lenken, das einen selbst auszeichnet und von anderen unterscheidet. Nirgendwo ist eine derartige Integration so einfach möglich wie auf der Android-Plattform, wo sich lose gekoppelte Komponenten zur Laufzeit spontan zusammenschließen lassen und so einen durchgängigen Interaktionsfluss für den Benutzer bilden. Dieses „Late Runtime Binding“ erlaubt zudem, lizenzrechtliche Einschränkungen zu lockern. Zum Beispiel greifen (viral ansteckende) „strong copy left“ Open-Source-Lizenzen nicht auf das eigene Projekt über, da zur Compile-Zeit nicht gegen den eigenen Code gelinkt wird. Das dynamische Binden von Komponenten zur Laufzeit über definierte Schnittstellen, auch über Sandbox-Barrieren hinweg, ist ein spannendes Alleinstellungsmerkmal der Android-Plattform. Dieses Konzept ermöglicht das Zusammenspiel verschiedener Anwendungsbausteine und verhindert, dass bereits existierende Funktionalität wiederholt entwickelt werden muss. 

Es gibt keine „App“

Der durch das iPhone populär gewordene Begriff „App“ ist in aller Munde und bezeichnet schlicht eine Softwareapplikation für mobile Endgeräte. Wenn man genauer hinsieht, existiert das Konzept „App“ jedoch auf der Android-Plattform in diesem Sinne gar nicht, wie es schon Ronan Schwarz in seiner sehenswerten Präsentation auf der Droidcon 2011 sehr anschaulich illustrierte [2].

Die Softwarepakete (APK), die man zum Beispiel im Android Market vorfindet, sind lediglich Komponenten-Bundles, also eine Sammlung von Anwendungsbausteinen, die von einem einzigen Herausgeber signiert wurden und in einem Rutsch installiert werden können. Für gewöhnlich findet man darin eine monolithisch in sich geschlossene Applikation, die kaum mit fremden Komponenten interagiert. Dabei können sich durchaus fünf komplette „Apps“ in einem einzigen solchen Android-Paket befinden, zum Beispiel eine Textverarbeitungs-, Tabellenkalkulations- und Präsentations-„App“. Sie können sich in die systemweite „Global QuickSearch“-Suche integrieren oder als Widgets auf dem Homescreen erscheinen. Das Paket kann aber auch eine oder mehrere halbe „Apps“ enthalten, die für sich allein, d. h. ohne das Zusammenspiel mit weiteren Komponenten, nicht lauffähig sind. Die Radar-Activity der Anwendung Radar [3] zum Beispiel, die mittels Kompass und GPS zum Ziel führt, muss erst von einem anderen Entwickler integriert werden, der die gewünschten Zielkoordinaten im Intent übergibt.

Die „Android Runtime“ stellt eine Komponenten-Laufzeitumgebung zur Verfügung und koordiniert die Interaktion zwischen verschiedenen Anwendungsbausteinen. Konzeptionell erinnert das stark an eine kleine SOA (Service Oriented Architecture) oder multimodulare Frameworks wie seinerzeit CORBA. Standardmäßig erhält jedes Android-Paket einen eigenen Linux-User (uid) und eine eigene Dalvik-VM-Instanz in einem separaten Linux-Prozess (pid). Der „PackageManager“ fungiert als Verzeichnis für installierte Komponenten und deren angebotene Dienste, Schnittstellen oder Fähigkeiten. Der „ActivityManagerService“ lokalisiert und instanziiert Komponenten auf Wunsch (Intent) und kontrolliert deren Lebenszyklus, auf den der Entwickler selbst nur bedingt Einfluss hat. Wird Arbeitsspeicher oder Rechenzeit knapp, kümmert sich der „MemoryManager“ (auch „LowMemoryKiller“ genannt) darum, Speicher freizugeben, indem er unwichtige Prozesse ohne Vorwarnung beendet. 

Das Konzept, das in der Android-Welt am ehesten einer „App“ nahekommt, ist die sogenannte „Task“ [4], [5]. Aus Sicht des Benutzers ist das der durchgängige Interaktionsfluss von Komponenten, die sich gemeinsam wie eine „App“ anfühlen. Typischerweise beginnt eine Task mit einem Launcher-Icon auf dem Homescreen und umfasst dann eine Sequenz von Benutzeraktivitäten (Activity) mit zugehöriger Historie (Back Stack). Mit dem Zurück-Knopf kann der Benutzer innerhalb dieser Historie navigieren. Multitasking erlaubt es, mehrere solcher Tasks parallel auszuführen oder mit dem „TaskSwitcher“ zwischen verschiedenen gleichzeitig laufenden Tasks hin und her zu springen. Die involvierten Komponenten können jedoch aus ganz unterschiedlichen Android-Paketen stammen und von verschiedenen Herausgebern signiert worden sein (Kasten „Task“).

Task

Aus dem Englischen, dt: Aufgabe. Sequenz von Aktivitäten, die zur Umsetzung einer Aufgabe notwendig sind. Die Task besteht aus einem Icon im „TaskSwitcher“ sowie einem Back Stack der ausgeführten Aktivitäten. Aus Sicht des Benutzers fühlt sich die Task an wie die App zum Erledigen einer bestimmten Aufgabe. 

Intent

Aus dem Englischen, dt: Absicht oder Wunsch. Abstrakte Beschreibung einer auszuführenden Operation ohne konkrete Angabe, wie oder womit diese „Absicht“ umgesetzt werden soll. In der Regel besteht ein Intent aus einer String-Repräsentation (Action), der gewünschten Aktion mindestens einer Kategorie (Category) sowie einem URI oder einem MIME Type (Data). Intents sind das verbindende Element für applikationsübergreifende Kommunikation und Integration über Prozessgrenzen hinweg.

Hier ein einfaches Beispiel, um solcherlei Interaktion zu verdeutlichen: Der Benutzer startet einen Twitter-Client, der standardmäßig in einer eigenen Task läuft (Abb. 1).

Abb. 1: Neuen Tweet senden (Twitter-Client)

Nach Eingabe eines Textes soll ein Bild mitgepostet werden. Hierzu wird ein Intent-Objekt instanziiert, das die gewünschte Absicht „Bild auswählen“ abstrakt formuliert, ohne anzugeben, wie oder womit die gewünschte Aktion konkret ausgeführt werden soll (Listing 1). 

final Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// intent.setFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
startActivityForResult(intent, REQUEST_CODE);

Die Android Runtime wählt für einen angefragten Intent eine passende Komponente aus, die in ihrem IntentFilter angegeben hat, welche Aufgaben sie erfüllen kann. Die Implementierung eines solchen Filters kann wie in Listing 2 aussehen.

<intent-filter>
<action android:name="android.intent.action.GET_CONTENT"/>
<category android:name="android.intent.category.OPENABLE"/>
<data android:mimeType="image/*"/>
</intent-filter>

In diesem Fall wäre das die Photo Gallery (Abb. 2), die zur Laufzeit in die Twitter-Task eingebunden wird, ohne den Interaktionsfluss zu unterbrechen. Dies bedeutet, dass die Gallery-Activity kein eigenes Icon im TaskSwitcher erhält, da sie Teil der Twitter-„App“ geworden ist. Wechselt der Benutzer zu einer anderen Task und wieder zurück, so landet er in der Photo Gallery, obwohl er im TaskSwitcher das Twitter-Icon ausgewählt hat.

Abb. 2: Ein Bild auswählen (Photo Gallery)

Nachdem der Benutzer ein Bild ausgewählt hat, setzt die Gallery-Activity dieses Bild als Ergebnis und beendet sich dann von selbst (Listing 3).

intent.setData(uri_to_open_selected_picture);
setResult(RESULT_OK, intent);
finish();

Die aufrufende Activity ist nun wieder das oberste Element im Activity-Back-Stack und erhält den Fokus. Das ausgewählte Bild wird über den Result Intent in einer Callback-Methode übergeben (Listing 4).

@Override
protected void onActivityResult(int requestCode, 
int resultCode, Intent intent) {
try {
InputStream in = getContentResolver()
.openInputStream(intent.getData()); 
} catch (FileNotFoundException e) {}
}

Dieses Beispiel illustriert die Wiederverwendbarkeit von Komponenten. Standardmäßig werden alle Activities in diejenige Task hinein gestartet, die den zugehörigen Intent begonnen hat. Im Folgenden werden einige Mechanismen genauer beleuchtet, um dieses Standardverhalten zu beeinflussen.

Aufmacherbild: Two android shake hands von Shutterstock / Urheberrecht: Dvpodt [ header = Seite 2: Task-Konfiguration ]

Task-Konfiguration

Das Zusammenspiel zwischen Activities und Tasks lässt sich beeinflussen. In den meisten Fällen gibt es sowohl ein Manifest-Attribut als auch eine zugehörige Intent-Flag mit vergleichbarer Wirkung. Das Manifest-Attribut ermöglicht dem Entwickler einer Activity, das Standardverhalten zu bestimmen. Die Intent-Flag erlaubt dem Aufrufer der Activity, das im Manifest spezifizierte Verhalten bei Bedarf zu überschreiben. Die Manifest-Attribute können dem „application“-Tag hinzugefügt werden, das für alle Komponenten im APK gilt, oder auch einzelnen „activity“-Tags, um nur diese zu beeinflussen.

Mit dem „launchMode“-Manifest-Attribut [6] kann das Startverhalten von Activities in der Task bestimmt werden. Es gibt dabei die folgenden Möglichkeiten: „standard“, „singleTop“, „singleTask“ und „singleInstance“. Der launchMode „standard“ verrät dabei schon durch seine Namensgebung, dass es sich dabei um den voreingestellten Startmodus von Android-Activities handelt. Activities mit diesem launchMode können mehrmals und aus jeder Task heraus instanziiert werden. Bei jedem Aufruf wird eine neue Instanz erzeugt und dem Activity-Back-Stack der Task hinzugefügt. 

Das Manifest-Attribut launchMode=”singleTask” oder die entsprechende Intent-Flag intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); bewirkt, dass immer eine neue Task gestartet wird. Zum Beispiel hat die Browser-Activity dieses Manifest-Attribut gesetzt. Der Homescreen Launcher oder die Notification Bar gibt allen Intents die Flag NEW_TASK. Läuft bereits eine Task (mit gleicher Task-Affinität), dann wird diese in den Vordergrund gebracht und die Activity hier eingebunden. Der Zurück-Knopf navigiert dann im Back Stack dieser Task, sofern hier bereits eine Historie existiert. Mit den Flags intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); kann die bereits existierende Task zurückgesetzt oder können alle Activities oberhalb der aufgerufenen Activity vom Back Stack entfernt werden.

Der launchMode launchMode=”singleTop” bewirkt hingegen, dass sich jeweils nur maximal eine Instanz der Activity oben auf dem Back Stack der Task befindet. Bei erneutem Start wird keine weitere Instanz erzeugt, sondern stattdessen die Callback-Methode onNewIntent(intent) der bereits existierenden Activity aufgerufen und diese in den Vordergrund gebracht. Die gleichnamige Intent-Flag intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); hat eine vergleichbare Wirkung.

Der launchMode launchMode=”singleInstance” sorgt dafür, dass nur eine einzige Instanz in einem einzigen Task existieren kann. Activities, die mit diesem Attribut gekennzeichnet sind, werden immer als Root-Element der Task ausgeführt. 

Die Task-Affinität [7] taskAffinity=”someString” drückt aus, zu welcher Task eine Activity gerne zugehörig sein möchte. Standardmäßig haben alle Activities in einem APK dieselbe Affinität füreinander, nämlich der Package Identifier String. Das Setzen von eigenen Affinitäten erlaubt es, Activities zu gruppieren, wenn mehrere „gefühlte“ Apps in ein Android-Paket gepackt werden sollen oder Activities aus verschiedenen APKs konzeptionell zusammengehören. Wenn beispielsweise die Calendar-App eine Notification für eine fällige Erinnerung setzt, so hat die Activity für den entsprechenden Termin einen leeren String als Affinität. Das bewirkt (gemeinsam mit der Flag NEW_TASK), dass die Detailansicht für den Termin nicht in die eventuell bereits existierende Calendar-Task eingebunden wird, sondern für sich alleine steht.

Das ist nur ein kleiner Auszug aus den verschiedenen Möglichkeiten, das Standardverhalten im Zusammenspiel von Tasks und Activities zu beeinflussen. Die offizielle Dokumentation [6], [7] ist eine gute Referenz, um mehr Details über diese Mechanismen zu erfahren. Im nachfolgenden Abschnitt werden zwei Vorschläge erläutert, wie eine modulare Softwarearchitektur realisiert werden kann, um Funktionalität je nach Bedarf nachzuinstallieren.

Mashup getrennter Android-Komponenten

Es gibt in Android verschiedene Möglichkeiten, die Grenzen von Sandboxes aufzuweichen und Daten auszutauschen (IPC). Nachfolgend werden Lösungen für zwei unterschiedliche Anwendungsfälle vorgestellt: zunächst ein Modell, welches voraussetzt, dass die kommunizierenden Komponenten vom selben Herausgeber stammen, also mit dem gleichen Zertifikat signiert worden sind. Anschließend zeigen wir ein Referenzmodell für die Broadcast-basierte Erstellung einer Plug-in-Architektur.

Process, Shared User ID und Permissions 

Android weist jeder Anwendung einen Linux-User und einen Prozess zu und führt sie in ihrer eigenen Sandbox aus. Dabei wird per Default jedem neu installierten APK ein eigener Prozess und eine eigene User-ID zugeteilt. Will eine externe Anwendung auf die Ressourcen eines anderen Prozesses und/oder eines anderen Linux-Nutzers zugreifen, zum Beispiel um den Inhalt einer Datenbank zu ändern, ist das nicht ohne weiteres möglich. Oft soll dies aber durchführbar sein, und meistens nur durch eigene Anwendungen. Ein typischer Anwendungsfall dieses Schemas wäre das mittlerweile dank In-App-Billing obsolete Verwenden eines kostenpflichtigen Ad Free Key, mit dem Nutzer sich von Werbung in einer Anwendung freikaufen können. 

Es gibt verschiedene Wege, diese Aufgabe zu lösen. Zwei der einfachsten werden hier beschrieben.

In der AndroidManifest.xml-Datei einer Anwendung ist es dem Entwickler möglich, im Manifest-Tag die sharedUserId seiner Anwendung festzulegen. Wird nun eine andere Anwendung mit derselben Shared User ID ausgestattet und mit demselben Zertifikat signiert, verwenden beide Anwendungen den identischen Linux-User. Nun können beide auf dieselben Daten zugreifen und es kann zum Beispiel in den Shared Preferences eine Flag showAds = true gesetzt werden. 

Sollen beide Anwendungen außerdem die Systemressourcen teilen, kann zusätzlich im Application-Tag das Attribut process gesetzt werden. Sowohl für die Shared User ID als auch für den Prozess wird per Konvention das Basis-Package einer Anwendung eingetragen.

Möchte man in seiner Anwendung eine Shared User ID verwenden, sollte man dies von Anfang an berücksichtigen. Wird sie im Nachhinein geändert, sorgt dies dafür, dass die Anwendung auf allen Geräten, auf denen sie bereits installiert war, beim Start abstürzt. Der Nutzer muss vor der Neuinstallation die alte Anwendung deinstallieren und verliert dadurch seine Daten. Hat man seine Anwendung bereits veröffentlicht und will dieses Verhalten umgehen, kann man also nicht mehr auf die Shared User ID bauen. 

Glücklicherweise gibt es eine andere einfache Möglichkeit, um ein ähnliches Verhalten zu erzielen: das Erstellen von mit Permissions geschützten Komponenten. Wird dabei bei der neuen Berechtigung das protectionLevel=”signature” gewählt, können nur Komponenten, die mit dem gleichen Zertifikat signiert wurden, diese verwenden. 

Listing 5 zeigt zunächst das Definieren einer Permission auf signature-Level und das anschließende Versehen einer Komponente mit derselben. In diesem Fall wird damit ein ContentProvider abgesichert. 

Wollen nun andere Komponenten, zum Beispiel die bereits erwähnte Anwendung zur Freischaltung von Werbefreiheit, auf den ContentProvider zugreifen, müssen sie wie in Listing 6 die erstellte Permission verwenden und mit demselben Zertifikat signiert werden wie die Anwendung, die die geschützte Komponente bereitstellt. Weiterreichende Details zum Thema „Permission Handling“ behandelt der Artikel „Safety First – Android sicher programmieren“ in dieser Ausgabe des Android360 Magazins.

<!-- Declare a permission -->
<permission android:name="de.andlabs.de.example.permission.SHARE_DATA"
android:label="Share Data"
android:description="Allows to share data only with applications 
signed by the same certificate"
android:protectionLevel="signature" />

<!-- Assign the permission to a component -->
<provider android:name="de.andlabs.example.DataProvider"
android:authorities="de.andlabs.example.dataprovider" 
android:permission="de.andlabs.de.example.permission.SHARE_DATA"
.../>
...
</provider>
<uses-permission android:name="de.andlabs.de.example.permission.SHARE_DATA" />

[ header = Seite 3: Plug-in-Referenzmodell ]

Plug-in-Referenzmodell

In Android gibt es viele Wege, um über Sandbox-Grenzen hinaus mit anderen Komponenten zu kommunizieren. ContentProvider, Intents, BroadcastReceiver, Adapter oder RemoteViews bieten verschiedene Möglichkeiten an. Da in diesem Artikel aus Platzgründen nicht auf alle eingegangen werden kann, wird nachfolgend ein Referenzmodell für die Realisierung eines Plug-in-Konzepts auf der Basis von BroadcastIntents und BroadcastReceivern vorgestellt.

Abbildung 3 zeigt den grundlegenden schematischen Ablauf dieses Modells. Zunächst wird dafür ein BroadcastReceiver registriert, der später auf Antworten von Plug-in-Komponenten reagiert. Je nachdem, ob auch auf Events reagiert werden soll, wenn die Anwendung gerade nicht im Vordergrund ist, geschieht dies entweder statisch in der AndroidManifest.xml oder dynamisch im Java-Code (Listing 7).

private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Show response in list, save in Database or else.
}
};
this.registerReceiver(mReceiver, new IntentFilter(MY_ACTION));

Damit nicht jeder BroadcastIntent an den definierten Receiver weitergeleitet wird, sollte dieser im Filter mit einer Action und mindestens einer Category oder einem Datentyp versehen werden. Löst der Nutzer nun eine Aktion aus, die eine Abfrage der Plug-ins anstößt, wird wie in Listing 8 ein BroadcastIntent abgefeuert. 

final Intent queryIntent = new Intent(MY_QUERY_ACTION);
queryIntent.putExtra("foo", "bar");

this.sendBroadcast(queryIntent);

Die Plug-in-Komponenten sollten dazu in der Lage sein, auf diesen Intent zu reagieren und die angehängten Daten zu verarbeiten. Sobald die Verarbeitung abgeschlossen ist, antworten die Komponenten via BroadcastIntent und die Hauptanwendung verarbeitet das Ergebnis. Falls der Anwendungsfall vorschreibt, dass der Nutzer auch zu der Komponente navigieren kann, die auf den Broadcast reagiert hat, empfiehlt es sich, dass die Plug-ins zudem einen PendingIntent (Kasten „PendingIntents“) mitsenden, der auf die entsprechende Komponente verweist. Meist sind diese Komponenten Activities, die eine angepasste grafische Darstellung des Ergebnisses zeigen.

PendingIntents

PendingIntents übergeben Intents an andere Anwendungen, ohne dabei ihren ursprünglichen Kontext zu verlieren. Das bedeutet, dass der übergebene Intent über dieselben Permissions und dieselbe Identität wie die Anwendung verfügt, in der der PendingIntent erstellt wurde. Aus diesem Grund können PendingIntents auch nicht mehr nachträglich verändert werden.

Soll ein Plug-in für andere Komponenten nicht sichtbar sein, kann so außerdem sichergestellt werden, dass es nur von Quellen aufgerufen wird, die auf den PendingIntent reagieren können und somit potenziell die Hauptanwendung sind. Dies erreicht man dadurch, dass – wie im Abschnitt zuvor beschrieben – eine Permission deklariert und die entsprechende Komponente an sie gebunden wird. Außerdem muss die Permission noch wie in Listing 6 verwendet werden, damit der PendingIntent auch über die Rechte verfügt, die Komponente anzusprechen.

Abb. 3: Struktur des Plug-in-Referenzmodells

In vielen Fällen ist das oben beschriebene Referenzmodell nicht ausreichend. Oft soll der Nutzer wie in Abbildung 4 dazu in der Lage sein, auszuwählen, welche Komponenten er ansprechen will und welche nicht. Um dies zu erreichen, kann das Modell noch erweitert werden. 

Abb. 4: Einstellungsansicht des Plug-in-basierten Projekts TeleportR.org [8]

Für diese Erweiterung bietet sich das Verwenden des PackageManagers an. Listing 9 zeigt eine beispielhafte Abfrage aller registrierten BroadcastReceiver zu einem bestimmten Intent und das anschließende Auslesen der Komponentendaten. Dabei muss bei den Plug-in-Komponenten darauf geachtet werden, dass ausschließlich statische, im Android-Manifest deklarierte BroadcastReceiver bereitgestellt werden. Die Methode queryBroadcastReceivers() ist so implementiert, dass sie keine dynamisch registrierten Receiver berücksichtigt.

final Intent intent = new Intent(MY_ACTION);
List<ResolveInfo> resolveInfos = getPackageManager().queryBroadcastReceivers(intent, 0);

String packageName;
String name;
for(ResolveInfo resolveInfo : resolveInfos) {
packageName = resolveInfo.activityInfo.packageName;
name = resolveInfo.activityInfo.name;
// ... Save packageName and name …
}

In der Android-Manifest-Datei gibt es außerdem die Möglichkeit, erweiterte Informationen über die Komponenten zu hinterlegen. Diese können anschließend ebenfalls mit dem PackageManager abgefragt und gespeichert werden. Listing 10 zeigt die Deklaration eines Metadaten-Tags in XML, Listing 11 fragt dieses mithilfe des PackageManagers ab.

<meta-data android:name="key" android:value="foo"/>
Bundle bundle = getPackageManager()
.getApplicationInfo(pkg_id, PackageManager.GET_META_DATA).metaData;
final String key = bundle.getString("key");

Führt der Nutzer nun erneut eine Aktion aus, auf die die externen Komponenten reagieren sollen, können die ausgewählten Komponenten einzeln per explizitem BroadcastIntent angesprochen werden (Listing 12).

//...Retrieve packageName and name
sendBroadcast(new Intent().setComponent(new ComponentName(packageName, name)));

Die bestehenden Konzepte auf Android sind gut, aber noch nicht perfekt. Bislang unterstützt der Android-PackageManager kein Auflösen von Abhängigkeiten zwischen Android-Paketen zur Installationszeit. Es ist nicht möglich, mehrere voneinander abhängige Pakete in einem Rutsch zu installieren, so wie das bei anderen Linux-PackageManagern üblich ist. Erst zur Laufzeit können auf dem Gerät fehlende Komponenten erkannt und der Benutzer zum Installieren in einen Market geschickt werden. Die gängigen App Stores können überdies momentan nicht nach Komponenten abgefragt werden, die auf bestimmte Intents auflösen.

Mehr Attraktivität durch Plug-ins

Die vorgestellte Plug-in-Struktur bietet einen Ansatz für die Umsetzung zahlreicher Anwendungen, die dem Nutzer über die eigenen Grenzen hinaus Mehrwert bieten. Die umfassenden Möglichkeiten der Android-Plattform für das Zusammenfassen unterschiedlichster unabhängiger Komponenten erleichtern die Programmierung dieser Anwendungen und machen es Entwicklern möglich, auch Dritte in die Entwicklung mit einzubeziehen. Durch die lose Kopplung der einzelnen Plug-ins besteht trotz der engen Zusammenarbeit keine Gefahr, sich an „viralen“ Open-Source-Lizenzen „anzustecken“. Dies kann das Bereitstellen von Datenquellen für viele proprietäre Entwickler attraktiver machen. Somit kann ein Mehrwert für alle Beteiligten entstehen: Entwickler, Drittentwickler und Nutzer.

Für eine Übersicht und weitere Informationen über Intents und die Möglichkeiten, die sie bieten, empfiehlt sich ein Blick auf OpenIntents [9]. OpenIntents ist ein Open-Source-Projekt, das sich der Interoperabilität und Kompatibilität zwischen Android-Komponenten verschrieben hat. In einem offenen Verzeichnis können gängige, häufig verwendete Intents standardisiert werden, um Komponenten einfach austauschbar zu machen. Die IntentFilter von verbreiteten Apps können unter [10] abgefragt werden.

Mashability

Vom Englischen „the ability to mashup“. Der Begriff „mashup“ wurde ursprünglich vor allem in der Musikindustrie verwendet und bezeichnet das Zusammenführen unterschiedlicher bestehender Komponenten zu einem neuen Ganzen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -