Artykuły, tutoriale, wsparcie dla programistów
March 22, 2014, 6:40 am

Aplikacje AIR w przeglądarce – jednoczesne utrzymywanie aplikacji dla środowiska AIR oraz przeglądarki

Ta sama technologia pozwala nam tworzyć aplikacje AIR oraz ich wersje webowe. Poznając techniki jednoczesnego ich utrzymywania, możemy dostarczać obie wersje jednocześnie.

Autor: Mateusz Małczak
Źródło: Software Developer’s Journal 11/2009 (179) http://sdjournal.org

Dowiesz się:

  • Jak rozwijać aplikację AIR równolegle z wersją webową;
  • Jak wykorzystywać kompilację warunkową;
  • Czym są domeny bezpieczeństwa (security domains).

Powinieneś wiedzieć:

  • Podstawy technologii Flex i AIR;
  • Podstawy języka ActionScript 3.

Poziom trudności 2

Aplikacje tworzone w technologiach Adobe maja jedną zaletę, której nie mogą im odmówić nawet najwięksi krytycy. Zaleta ta to możliwość jednoczesnego tworzenia wersji desktopowej (środowisko uruchomieniowe Adobe AIR) oraz wersji online (aplikacje uruchamiane w przeglądarce z wykorzystaniem wtyczki Adobe Flash). Oczywiście ze względu na obszar działania wersje te różnią się nieznacznie między sobą, większość kodu źródłowego jest jednak wspólna. Podstawowym powodem powstawania różnic są kwestie bezpieczeństwa. Ograniczenia działania aplikacji, jej prawa oraz możliwości są określane poprzez domenę bezpieczeństwa (security sandbox). Jest ona związana z typem aplikacji oraz obszarem jej działania. W zależności od domeny bezpieczeństwa część funkcjonalności może być niedostępna. Najlepszym przykładem ilustrującym ten aspekt jest ładowanie zewnętrznych danych. Kiedy aplikacja jest uruchomiona w przeglądarce, nie może ona wczytywać danych z innych serwerów niż ten, na którym działa. Ograniczenia tego nie mamy jednak, kiedy program jest uruchomiony z dysku (musi być oznaczony jako mający dostęp do sieci) oraz w przypadku, kiedy jest aplikacją AIR. W przypadku aplikacji online rozwiązaniem przynajmniej częściowym jest umieszczenie na serwerze, z którego chcemy pobierać dane specjalnego pliku xml. Plik ten najczęściej noszący nazwę crossdomain. xml określa adresy url aplikacji, które mają prawo pobierać dane z serwera, na którym plik ten się znajduje. Szczegóły wykorzystania plików crossdomain są bardzo dokładnie omówione w dokumentacji opisującej bezpieczeństwo Flash Playera.

Domeny bezpieczeństwa

Najważniejszym aspektem z punktu widzenia użytkownika jest bezpieczeństwo aplikacji. W technologiach Adobe jest ono regulowane poprzez wykorzystanie domen bezpieczeństwa (security domains). Domeny bezpieczeństwa nie można zmienić w czasie działania aplikacji. Jeśli nasza aplikacja ładuje zewnętrzny plik swf, jest on automatycznie umieszczany w domenie tego samego typu. Typ domeny, w jakiej pracujemy, decyduje o tym, co wolno, a czego nie wolno aplikacji. Domenę, w jakiej się znajdujemy, możemy odczytać ze statycznej stałej Security.sandboxType. W aktualnej chwili dostępne wartości to :

  • Security.REMOTE Aplikacja jest wykonywana w przeglądarce. Nie można załadować danych z serwisów, które jawnie na to nie zezwalają. Nie jest dostępna część funkcjonalności. Przykładowo, nie można modyfikować załadowanych do aplikacji rysunków. Próba dostępu do pikseli (BitmapData) obrazka zakończy się otrzymaniem błędu wykonywania.
  • Security.LOCAL _ WITH _ FILE W tej domenie znajdują się aplikacje uruchomione z dysku użytkownika, ale nie określone jako zaufane. Aplikacje w tej domenie mogą ładować jedynie zasoby z dysków lokalnych, nie mają dostępu do sieci.
  • Security.LOCAL _ WITH _ NETWORK Domena podobna do poprzedniej. Różnica polega na tym, że aplikacja ma dostęp do Internetu. Aby znaleźć się w tej domenie, aplikacja musi zostać skompilowana z ustawioną flagą dostępu do sieci. Dla programów w tej domenie nie jest możliwe odwołanie do dysków lokalnych.
  • Security.LOCAL _ TRUSTED Aplikacja została uruchomiona z dysku użytkownika i została przez niego określona jako zaufana. Aplikacje znajdujące się w tej domenie mają dostęp zarówno do dysku, jak i do zasobów sieciowych.
  • Security.APPLICATION Do tej domeny trafiają aplikacje AIR uruchomione na komputerze użytkownika. W domenie tej nie mamy praktycznie żadnych ograniczeń. Dodatkowo zyskujemy dostęp do dysku użytkownika, obsługę baz danych SQLite, renderowanie stron WWW, otwieranie plików w formacie PDF oraz wiele innych. Nie istnieją także ograniczenia związane z wczytywaniem zewnętrznych danych. Możemy pobierać zarówno multimedia, jak i dane z dowolnego serwera.

W ramce W Sieci znajdziemy link do rozdziału dokumentacji szczegółowo opisującej aspekty bezpieczeństwa aplikacji tworzonych z wykorzystaniem aplikacji Adobe. Domeny bezpieczeństwa są jednym z powodów, dla których tworzenie aplikacji AIR oraz wersji webowej różnią się. Musimy doskonale orientować się, co możemy, a czego nie możemy osiągnąć w docelowej domenie. Drugim powodem jest głównie dostęp do dysku, jaki posiadamy, tworząc aplikację AIR. Dostęp ten stawia przed nami całkiem nowe możliwości – tworzenie, czytanie, edycja plików oraz katalogów. Możliwość wczytywania standardowo nieobsługiwanych formatów plików, poprzez samodzielną implementację odczytu. Największym problemem utrzymywania dwóch wersji aplikacji jest, jak rozdzielić zadania wspólne od kodu specyficznego dla domeny docelowej.

Rysunek 1. Aplikacja uruchomiona w środowisku AIR

Metoda separacji kodu

Na stronach Adobe.com znajduje się artykuł omawiający sposób jednoczesnego utrzymywania aplikacji webowej oraz AIR. Rozwiązanie to zakłada wyizolowanie kodu wspólnego dla obu wersji do osobnego projektu – nazywanego CommonProject. Projekt ten zawiera wszystko, co jest wspólne dla obu środowisk – definicja wyglądu, ikony itp. Kod specyficzny dla platformy jest natomiast trzymany w osobnych projektach – nazywanymi odpowiednio AirProject oraz FlexProject. Aplikacja główna, oparta o kod wspólny, realizuje zadania, które różnią się dla obu projektów, jedynie w oparciu o ogólny interfejs. Specjalizacja, realizowana poprzez implementację ogólnego interfejsu, odbywa się w każdym z projektów oddzielnie. W wyniku tego w czasie kompilacji dołączony jest odpowiedni plik z implementacją. Na Listingu 1 przedstawiony został kod źródłowy aplikacji AIR realizującej to podejście. Jak widać, na listingu aplikacja zaraz po załadowaniu tworzy instancję widoku wspólnego oraz umieszcza go na scenie – MainCanvas. W tym samym czasie tworzona jest także klasa implementująca zadania specyficzne dla platformy AIR. Jedyną różnicą w kodzie głównego pliku aplikacji uruchamianej w przeglądarce jest zmiana klasy bazowej z mx.core.WindowedApplication na mx.core.Application. Reszta realizowana jest analogicznie. Wynik działania aplikacji został przedstawiony na Rysunkach 1 (środowisko AIR) oraz 2 (przeglądarka). W obu przypadkach realizowane są te same zadania – aplikacja pozwala otworzyć rysunek z dysku użytkownika, dodać na nim tekst oraz zapisać powstały rysunek na dysku. W obu aplikacjach częścią wspólną jest definicja wyglądu oraz podstawowa logika. Obszar wymagający utworzenia specjalnego kodu to etap odczytu oraz zapisu obrazków z dysku użytkownika. Na Listingu 2 przedstawiona została metoda tworząca instancję klasy realizującej zadania zależne od środowiska. Jak widać na przykładzie typu domeny, w której działa nasza aplikacja, został wykorzystany do określenia, czy mamy do czynienia z wersją AIR. W przykładzie tym zakładamy, że pracujemy na odtwarzaczu Flash Player w wersji 10, aby mieć możliwość odczytu i zapisu danych na dysk użytkownika.

Rysunek 2. Aplikacja uruchomiona w przeglądarce

Listing 1. Aplikacja AIR korzystająca z kodu wspólnego

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”

applicationComplete=”onApplicationComplete();”>

<mx:Script>

<![CDATA[

//to jest potrzebne aby kompilator podczas optymalizacji nie usunal

potrzebnej klasy

private static const neededForCompilation:AirGeneralImplementation = null;

//interfes realizujący zadania zależne od środowiska

private var general : IGeneral;

private function onApplicationComplete():void

{

//wspólna część programu

var can:MainCanvas = new MainCanvas();

this.addChild(can);

//pobieramy implementację dla środowiska AIR

general = GeneralFactory.getGeneralInstance()

}

]]>

</mx:Script>

</mx:WindowedApplication>

Listing 2. Metoda tworząca instancję klasy specjalistycznej

static public function getGeneralInstance():IGeneral

{

var general:IGeneral;

//sprawdzamy w jakiej domenie operujemy

var cls:String = (Security.sandboxType.toString() == „application” ? „AirGeneral

Implementation” : „FlexGeneralImplementation”);

//pobieramy odpowiednią klasę w zależności od domeny działania

var clsToCreate:Object = getClassToCreate(cls);

general = new clsToCreate();

return general;

}

Rysunek 3. Zakładka ustawień kompilatora, w której podajemy dodatkowe parametry

Metoda wykorzystująca kompilację warunkową

Poprzednio przedstawione rozwiązanie jest bardzo dobre i poprawne z punktu widzenia projektowania aplikacji. Co jednak, jeśli ilość kodu różniącego się w obu przypadkach jest naprawdę niewielka ? Przykładem może być chęć wykorzystania składowej allowLoa dBytesCodeExecution klasy LoaderContext, która jest dostępna tylko w środowisku AIR. Właściwość ta określa, czy zezwalamy na wykonywanie kodu znajdującego się w ładowanych zasobach. Aby uniknąć tworzenia uogólnienia w oparciu o interfejs, z pomocą przychodzi nam kompilacja warunkowa. Ten typ kompilacji polega, w najprostszym tłumaczeniu, na uwzględnianiu różnych fragmentów kodu źródłowego, w zależności od zdefiniowanych warunków kompilacji. Warunki te podajemy, wykorzystując parametr define kompilatora. Na Rysunku 3 przedstawione zostało wykorzystanie tego parametru we Flex Builderze. Dzięki kompilacji warunkowej możemy zmniejszyć ilość pisanego kodu, w odróżnieniu od poprzedniego sposobu. Tworzymy jedną wersję klasy, a kod specyficzny dla wersji zamykamy w blokach warunkowych. Resztę zrobi za nas kompilator. Przykład wykorzystania kompilacji warunkowej dla osiągnięcia efektu jak w poprzednim rozwiązaniu przedstawia Listing 3. Podobnie jak w poprzednim rozwiązaniu teraz również tworzymy dwa projekty – AirProject oraz FlexProject. Różnica polega na tym, że kod dla obu wersji trzymany jest wspólnie, jedynie pogrupowany w bloki warunkowe. Zdefiniowane w parametrze wartości możemy jednak także wykorzystać, aby przekazywać do aplikacji dodatkowe informacje. Przykładowo, możemy w ten sposób przekazać do aplikacji, na etapie kompilacji, numer wersji pobrany z repozytorium svn.

Listing 3. Przykład wykorzystania kompilacji warunkowej do pomijania bloków kodu

//uwzględnij klasy dostępne jedynie w środowisku AIR, jeśli

kompilujemy pod to środowisko

TARGET::AIR {

import flash.filesystem.File;

import flash.filesystem.FileMode;

import flash.filesystem.FileStream;

private var file : File;

}

//jeśli jesteśmy w aplikacji webowej wykorzystaj obiekt

FileRefernce do dostępu do dysku

TARGET::FLEX {

private var file : FileReference;

}

//funkcja ładująca rysunek z dysku

private function loadImage():void

{

var filters:Array = [ new FileFilter('JPG','*.j

pg'),

new FileFilter('JPEG','*.jpeg'), new FileFilter('P

NG','*.png'),

new FileFilter('GIF','*.gif') ];

//jeśli kompilujemy aplikację AIR otwórz plik korzystając z

bezpośredniego dostępu do dysku

TARGET::AIR {

file = new File();

file.addEventListener( Event.SELECT,

inputFileSelectHandler );

file.browseForOpen( ‚Wybierz obrazek’, filters );

};

//w aplikacji webowej pozwól wybrać użytkownikowi plik a

następnie go wczytaj

TARGET::FLEX {

file = new FileReference();

file.addEventListener( Event.SELECT,

inputFileSelectHandler );

file.browse( filters );

};

};

// funkcja rysunek zapisująca na dysk

private function saveImage():void

{

var encoder:JPEGEncoder = new JPEGEncoder(80);

var bd:BitmapData = new BitmapData( imgMain.width,

imgMain.height );

bd.draw( imgContainer );

buffer = encoder.encode( bd );

bd.dispose();

bd = null;

// w środowisku AIR użytkownik może wybrać plik a zapis

następuje w drugiej kolejności

TARGET::AIR {

file = File.desktopDirectory.resolvePath(‚ouput.j

pg’);

file.addEventListener( Event.SELECT,

outputFileSelectHandler );

file.browseForSave(‚Zapisz rysunek’);

};

// w aplikacji webowej przed wybraniem pliku do zapisu

musimy już określić dane do zapisu

TARGET::FLEX {

file = new FileReference();

file.addEventListener( Event.COMPLETE,

fileSavedHandler );

file.save( buffer, ‚output.jpg’ );

}

};

//otwarcie pliku w zależności od środowiska

private function inputFileSelectHandler( evt:Event ):

void

{

var ba:ByteArray = new ByteArray();

//w AIR skorzystajmy z bezpośredniego dostępu do dysku

TARGET::AIR {

var fs:FileStream = new FileStream();

fs.open( file, FileMode.READ );

fs.readBytes( ba );

ba.position = 0; fs.close();

file.removeEventListener( Event.SELECT,

inputFileSelectHandler );

file = null; imgMain.source = ba; _imgLoaded = true;

};

//w aplikacji webowej użytkownik wybrał już plik, teraz

możemy wywołać metodę wczytującą

//wczytane dane będą dostępne w składowej file.data

TARGET::FLEX {

file.addEventListener( Event.COMPLETE,

fileLoadedHandler );

file.load();

};

};

W Sieci

  • http://www.adobe.com/devnet/air/flex/ articles/flex_air_codebase_02.html – artykuł na stronach Adobe o tworzeniu aplikacji jednocześnie dla dwóch środowisk;
  • http://livedocs.adobe.com/flex/3/html/ help.html?content=compilers_21.html – kompilacja warunkowa w środowiskach Flex/AIR;
  • http://livedocs.adobe.com/flex/3/html/ help.html?content=05B_Security_01.html – dokumentacja Adobe dotycząca bezpieczeństwa odtwarzacza Flash Player.

Podsumowanie

Dzięki omówionym w artykule technikom możemy teraz w prosty sposób tworzyć aplikacje działające zarówno w środowisku AIR, jak i w przeglądarce. Rozwiązanie, w którym nasz produkt jest dostępny jako samodzielna aplikacja oraz w przeglądarce, znacznie zwiększa jej atrakcyjność. Wersja aplikacji dla środowiska AIR wymaga instalacji, dzięki czemu może oferować dodatkową funkcjonalność, która nie jest osiągalna w przeglądarce. Jednak fakt wymaganej instalacji może użytkownika odstraszyć od aplikacji, wersja webowa daje możliwość szybkiego dostępu do programu każdemu, kto odwiedzi stronę internetową. Dzięki temu łatwiej nam zainteresować ludzi naszym produktem. Zamiast zachwalać korzyści płynące z instalacji naszego rozwiązania, możemy przedstawić działającą aplikację. Kody źródłowe omawianych aplikacji znajdują się na stronie – http: //segfaultlabs.com/docs

O autorze

MATEUSZ MAŁCZAK Autor jest programistą z bogatym doświadczeniem w tworzeniu aplikacji desktopowych dla systemów Windows i Linux. Aktualnie bardzo mocno związany z technologiami Flex/AIR. Tworząc aplikacje dla serwisu http://komixo.com ,wygrał ogólnopolski konkurs na najlepszą aplikację w tej technologii (http://www.flexchallenge.com/pl).
Kontakt z autorem: matuesz@malczak.info

Zostaw odpowiedź

Musisz być zalogowany aby publikować komentarz.



Software Press Sp. z o.o. Sp. Komandytowa 02-682 Warszawa, ul. Bokserska 1, NIP 9512279582, REGON 141804060, KRS: 0000327578