Programowanie internetowe J2ME.pdf

(411 KB) Pobierz
44703331 UNPDF
Programowanie
J2ME
Krzysztof Rychlicki-Kicior
Programowanie internetowe
w J2ME
niestety, często przychodzi im zmagać się
z zagadnieniami powszechnie uważany-
mi za obitujące w potencjalne błędy. Pisanie apli-
kacji internetowych z pewnością zalicza się do tej
dziedziny – nie dość, że zamiast jednego progra-
mu trzeba napisać dwa, programista musi stwo-
rzyć język, przy pomocy którego obydwa progra-
my będą się porozumiewać (najbardziej znane są
dostępne publicznie i noszą nazwę protokołów).
Do tego „pięknego” obrazu trzeba jeszcze dodać
problemy z komunikacją, które, zgodnie z prawa-
mi Murphy’ego, zawsze pojawiają się w najmniej
oczekiwanym momencie. Jednym z ciekawszych
aspektów projektowania aplikacji komunikujących
się przy pomocy Internetu jest możliwość ich im-
plementacji w różnych językach programowania.
Niniejszy artykuł porusza kwestie związane z pi-
saniem tego rodzaju aplikacji na telefony komór-
kowe. Wykorzystamy zatem język J2ME wraz
z oprogramowaniem J2ME Wireless Toolkit irmy
Sun. Po krótkim zapoznaniu się z obsługą sieci
w J2ME napiszemy prosty telnet działający na te-
lefonie komórkowym.
gdzie otrzymujemy do dyspozycji interfejs Socket-
Connection. Do ważniejszych interfejsów MIDP 1.0
zalicza się:
• HttpConnection;
• ContentConnection;
• StreamConnection;
• DatagramConnection.
W MIDP 2.0 dodano kolejne, jeszcze bardziej funk-
cjonalne interfejsy:
• HttpsConnection;
• CommConnection;
• UDPDatagramConnection;
• SocketConnection.
Nawiązywanie połączenia
Stare indiańskie przysłowie mówi, że zanim za-
piszesz lub wczytasz informacje z jakiegokolwiek
źródła danych, musisz to źródło (w naszym przy-
padku połączenie) otworzyć. W J2ME do tego ce-
lu używa się statycznej metody Connector.open() .
Klasa Connector , podobnie jak wszystkie interfej-
sy do obsługi połączeń, znajduje się w pakiecie
javax.microedition.io . Zwraca ona obiekt typu
Connection . Jest to interfejs bazowy dla wszyst-
kich interfejsów sieciowych. Aby móc wykorzystać
w pełni nawiązane połączenie, należy zrzutować
je na żądany typ interfejsu (np. HttpConnection lub
SocketConnection ).
Komórki i sieć
Pisząc programy na telefony komórkowe musimy
pamiętać o kilku ważnych właściwościach tej plat-
formy sprzętowej. Chociaż obsługa połączeń sie-
ciowych (w różnym zakresie, ale o tym później)
teoretycznie jest dostępna na wszystkich telefo-
nach obsługujących Javę, nie każdy telefon ma „i-
zycznie” taką możliwość (dotyczy to głównie star-
szych modeli). Podstawowe komponenty do obsłu-
gi Internetu są zawarte w MID Proile 1.0. Niestety,
jedynym sensownym połączeniem, jakie możemy
utworzyć dysponującym tą wersją MIDP, jest połą-
czenie przy użyciu protokołu HTTP (interfejs Http-
Connection ). W praktyce oznacza to możliwość na-
wiązania połączenia z dowolnym hostem tylko na
porcie 80. Problem ten znika wraz z MIDP 2.0,
Kolejne czynności
Podobnie jak w przypadku innego rodzaju zasobów
(np. plików w J2SE), do wczytywania i zapisywania
danych wykorzystuje się klasy strumieni. Dla pro-
gramistów J2SE bolesnym może okazać się brak
klasy BufferedReader , dzięki której można wczyty-
wać łańcuchy znaków (w postaci zmiennych typu
String ). Na szczęście projektanci Javy komórkowej
okazali się łaskawi przynajmniej w zakresie wysy-
łania danych – klasa PrintStream udostępnia meto-
println() , dzięki której możemy wysyłać łańcu-
chy znaków. Oczywiście nie samymi tekstami żyje
człowiek, w związku z czym otrzymujemy również
szeroki zakres metod do wysyłania liczb, wartości
logicznych, a przede wszystkim bajtów i tablic baj-
tów. Po wykonaniu wszystkich wymaganych ope-
racji należy zamknąć gniazdko przy użyciu meto-
dy close() . Listing 1. zawiera program działający
jak echo – jego zadaniem jest odsyłanie wszystkich
otrzymanych danych.
Autor programuje w Javie i C# oraz tworzy aplikacje sie-
ciowe w oparciu o technologię PHP i MySQL. Napisał
książkę pt. “J2ME. Java dla urządzeń mobilnych. Ćwi-
czenia” (Wydawnictwo Helion, 2006) oraz szereg ar-
tykułów z zakresu Delphi, PHP i Javy. Prowadzi zaję-
cia informatyczne z zakresu J2ME i C# dla młodzieży w
Łódzkim Centrum Doskonalenia Nauczycieli i Kształce-
nia Praktycznego.
Kontakt z autorem: kitikatpl@gmail.com
54
www.sdjournal.org Software Developer’s Journal 7/2006
P rogramiści lubią upraszczać sobie życie;
44703331.011.png
Programowanie internetowe w J2ME
Listing 1. Program wysyłający odebrane dane
Telnet w komórce
Głównym celem niniejszego warsztatu jest napisanie pro-
gramu przypominającego telnet na telefon komórkowy.
Przy jego tworzeniu uwzględnimy następujące reguły:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import java.io.*;
import javax.microedition.io.*;
public class echo extends MIDlet
{
public echo ()
{
try
{
SocketConnection conn = ( SocketConnection ) Connector . open
( "socket://domena.org:11111" ) ;
InputStream in = conn . openInputStream () ;
OutputStream out = conn . openOutputStream () ;
int b ;
while (( b = in . read ()) !=- 1 )
{
out . write ( b ) ;
}
in . close () ;
out . close () ;
conn . close () ;
}
catch ( Exception e )
{
e . printStackTrace () ;
}
}
public void startApp (){}
public void pauseApp (){}
public void destroyApp ( boolean unconditional ){}
}
• polecenia tekstowe będą wysyłane wraz ze znakami po-
wrotu karetki i nowej linii ( \r\n ) jako osobne porcje da-
nych (nie znak po znaku);
• odbierane będą pojedyncze łańcuchy znaków (zakończo-
ne znakiem nowej linii) lub dane otrzymane w przeciągu
określonego czasu (jeśli nie zostanie nadesłany cały ciąg
znaków).
Listing 2. Tworzenie interfejsu formatek MIDletu
public class telnet extends MIDlet
// Runnable posłuży do nawiązania połączenia
implements CommandListener , Runnable
{
public telnet ()
{
// tworzymy interfejs pierwszej formatki
dataform = new Form ( "Wpisz dane:" ) ;
host = new TextField ( "Host:" , "" ,30, TextField . ANY ) ;
port = new TextField ( "Port:" , "" ,5, TextField . NUMERIC ) ;
dataform . append ( host ) ;
dataform . append ( port ) ;
dataform . setCommandListener ( this ) ;
dataform . addCommand ( new Command ( "Polacz" , Command . OK ,0 )) ;
dataform . addCommand ( new Command (
"Koniec" , Command . EXIT ,1 )) ;
sendform = new Form ( "Wyslij komunikat:" ) ;
text = new TextField ( "Polecenie:" , "" ,150, TextField . ANY ) ;
sendform . append ( text ) ;
// wewnętrzna klasa zdarzenia
sendform . setCommandListener ( new SendFormEvent ()) ;
sendform . addCommand ( new Command ( "Wyslij" , Command . OK ,0 )) ;
sendform . addCommand ( new Command (
"Podejrzyj" , Command . EXIT ,1 )) ;
mainform = new Form ( "Okno glowne:" ) ;
mainform . setCommandListener ( new MainFormEvent ()) ;
mainform . addCommand ( new Command ( "Powrot" , Command . OK ,0 )) ;
mainform . addCommand ( new Command (
"Zamknij" , Command . EXIT ,1 )) ;
display = Display . getDisplay ( this ) ;
display . setCurrent ( dataform ) ;
// jednocześnie będzie wyświetlane maksymalnie 5
// komunikatów
messages = new String [ 5 ] ;
// do tego celu wykorzystamy utworzone w tym fragmencie
// etykiety
labels = new StringItem [ 5 ] ;
for ( int i = 0 ; i < 5 ; i ++ )
{
messages [ i ] = "" ;
labels [ i ] = new StringItem ( "" , "" ) ;
mainform . append ( labels [ i ]) ;
}
}
W powyższym przykładzie wykorzystujemy podstawowe
klasy strumieni – OutputStream i InputStream . Adres URL jest
oczywiście zmyślony; program taki nie ma zresztą praktycz-
nego zastosowania, ale dobrze pokazuje podstawowe me-
chanizmy funkcjonowania gniazdek w J2ME.
Rysunek 1. Interfejs formy formadane
Software Developer’s Journal 7/2006
www.sdjournal.org
55
44703331.012.png 44703331.013.png 44703331.014.png 44703331.001.png
Programowanie
J2ME
Listing 3. Zdarzenie wybrania polecenia (formatka
dataform)
Przy tworzeniu połączenia należy zwrócić uwagę na waż-
ny fakt – jest to czynność dość czasochłonna, w związku
z czym nie należy umieszczać ich w metodach zdarzeń ta-
kich jak commandAction() . Bez przeszkód można jednak użyć
wątków. W J2ME wykorzystuje się dwa klasyczne sposo-
by tworzenia wątków: utworzenie klasy pochodnej od klasy
Thread oraz utworzenie klasy implementującej interfejs
Runnable . W naszej sytuacji najprościej jest zaimplementować
interfejs Runnable w klasie MIDletu (Listing 2.) i zadeklarować
metodę run() . Jej zadaniem jest utworzenie połączenia oraz
obiektów strumieni, a także rozpoczęcie procesu odbierania
danych w specjalnym wątku.
// zdarzenie
public void commandAction ( Command c , Displayable s )
{
if ( c . getCommandType () == Command . EXIT )
{
destroyApp ( true ) ;
notifyDestroyed () ; // zakończ aplikację
}
if ( c . getCommandType () == Command . OK )
{
Thread thread = new Thread ( this ) ;
thread . start () ; // uruchom wątek tworzący połączenie
}
}
Transmisja danych
Po nawiązaniu połączenia nadszedł czas, aby zająć się od-
bieraniem i wysyłaniem danych. Klasa Receiver ma za zada-
nie wczytywać całe linijki tekstu, zakończone sekwencją zna-
ków o kodach 13 i 10 (znaki nowej linii i powrotu karetki). Goto-
wy tekst będzie dodawany do formularza mainform .
Trzeba jednak być przygotowanym na odbieranie danych
binarnych, które nie są zakończone powyższymi znakami.
W takiej sytuacji, timer zdarzenia ScreenPrinter będzie co se-
Interfejs programu
Nasz MIDlet składa się z trzech formatek (pojemników klasy
Form , zwanych też formami lub formularzami). Pierwsza z nich
(o nazwie dataform ) zawiera dwa pola tekstowe ( TextField ),
służące do wprowadzenia nazwy hosta i portu, na którym zo-
stanie nawiązane połączenie. Działanie programu sprowa-
dza się do wprowadzania poleceń do pola tekstowego, znaj-
dującego się w drugiej formatce ( sendform ) , wysyłania ich i od-
bierania odpowiedzi serwera (formatka mainform ). W Listingu
2. znajduje się początkowy fragment kodu, zawierający kon-
struktor klasy MIDletu.
Pamięć emulatora, podobnie jak prawdziwych urządzeń,
ma pewne ograniczenia. Dotyczy to zwłaszcza możliwości
przechowywania na jednej formatce komponentów. Program
mógłby po prostu dodawać każde otrzymane polecenie jako
nową etykietę ( StringItem ). Niestety, wskutek owych ograni-
czeń musimy wprowadzić stałą liczbę etykiet i wykorzystać
metodę SetString() klasy StringItem do zmiany ich wartości.
Pierwszą czynnością, jaką musimy się zająć jest utwo-
rzenie połączenia. Musi ono nastąpić w wyniku wybrania od-
powiedniego polecenia ( Polacz ). Związana z nim jest me-
toda commandAction() – dzięki implementowaniu interfejsu
CommandListener przez nasz MIDlet możemy zadeklarować ją
jako zwykłą metodą w klasie MIDletu (później pokażemy, w ja-
ki sposób można inaczej utworzyć klasę zdarzenia).
Listing 4. Metoda nawiązująca połączenie
public void run ()
{
int portnumer = Integer . parseInt ( port . getString ()) ;
try
{
// połączenia przy użyciu protokołu http muszą być
// obsługiwane przy pomocy interfejsu HttpConnection
if ( portnumer == 80 )
conn = ( HttpConnection ) Connector . open (
"http://" + host . getString () + ":80" ,
Connector . READ_WRITE , false ) ;
else // reszta połączeń jest obsługiwana normalnie
conn = ( SocketConnection ) Connector . open (
"socket://" + host . getString () + ":" + port . getString () ,
Connector . READ_WRITE , false ) ;
in = conn . openInputStream () ;
out = new PrintStream ( conn . openOutputStream ()) ;
// wątek odbierania danych
Thread thread = new Thread ( new Receiver ()) ;
thread . start () ;
// timer odpowiedzialny za wypisywanie danych
// w sytuacji, gdy nie są przesyłane całe łańcuchy
// znaków
Timer timer = new Timer () ;
timer . schedule ( new ScreenPrinter () ,0,1000 ) ;
display . setCurrent ( sendform ) ;
}
catch ( IOException e )
{
e . printStackTrace () ;
destroyApp ( true ) ;
notifyDestroyed () ;
}
}
Rysunek 2. Prośba MIDletu o pozwolenie na połączenie z
Internetem
56
www.sdjournal.org Software Developer’s Journal 7/2006
44703331.002.png 44703331.003.png 44703331.004.png 44703331.005.png 44703331.006.png 44703331.007.png 44703331.008.png
 
Programowanie internetowe w J2ME
kundę sprawdzał zawartość bufora odebranych danych i wy-
pisywał jego zawartość (w przypadku danych tekstowych do
takich sytuacji nie będzie dochodzić).
Znacznie prostszą czynnością jest wysyłanie poleceń.
W naszym przypadku komendy wysyłać będziemy ze znakiem
nowej linii, korzystając z metody println() obiektu strumienia.
Jeśli, Drogi Czytelniku, będziesz chciał dostosować telnet
do swoich wymagań, możesz skorzystać z analogicznej meto-
dy print() – nie dołącza ona żadnych znaków do przesyłane-
go tekstu. Na początku tego artykułu wspominałem o koniecz-
ności używania stałej liczby etykiet tekstowych do wyświetla-
nia danych. W związku z tym do dodawania poleceń używa-
my metody addText() , która uwzględnia problemy związane ze
zmianą wartości etykiet.
Listing 6. Dodawanie tekstu do formatki mainform
public void addText ( String text )
{
// Jeśli nie osiągnięto maksymalnej ilości komunikatów,
// dodaj normalnie
if ( number < 5 )
{
messages [ number ] = text ;
labels [ number ] . setText ( text + " \n " ) ;
number += 1 ;
}
else
{
for ( int i = 0 ; i < 4 ; i ++ )
// przesuń “w górę” etykiety
messages [ i ] = new String ( messages [ i + 1 ]) ;
// nadaj nową wartość tekstowi, znajdującemu
// się na dole formatki
messages [ 4 ] = new String ( text ) ;
for ( int i = 0 ; i < 5 ; i ++ )
{
// aktualizacja etykiet
labels [ i ] . setText ( messages [ i ] + " \n " ) ;
}
}
}
Testowanie
Kompilacja i uruchamianie programu przebiegają normalnie,
jednak w trakcie nawiązywania połączenia wyświetli się ko-
munikat (Rysunek 2).
Listing 5. Klasy służące do odbierania danych
class Receiver implements Runnable
{
public void run ()
{
int b = 0 ;
buffer = new StringBuffer () ; // bufor na dane
try
{
while (( b = in . read ()) !=- 1 ) // wczytywanie bajtów
{
buffer . append (( char ) b ) ;
if (( b == 10 ) || ( buffer . length () == 256 ))
{
// kontrola: znak nowego wiersza lub określony rozmiar
// bufora powodują dodanie tekstu i wyczyszczenie
// bufora
addText ( buffer . toString ()) ;
buffer . setLength ( 0 ) ;
}
}
}
catch ( IOException e ){}
}
}
class ScreenPrinter extends TimerTask
{
public synchronized void run ()
{
if ( buffer . length () > 0 )
{
// jeśli nie mamy do czynienia z tekstami, trzeba
// regularnie wypisywać zawartość bufora
addText ( buffer . toString ()) ;
buffer . setLength ( 0 ) ;
}
}
}
Musimy pamiętać, że korzystanie z Internetu w telefonach
komórkowych to czynność wymagająca posiadania przez
MIDlet specjalnych uprawnień (podobnie jak w przypadku tra-
dycyjnych apletów w J2SE). Jeśli MIDlet nie posiada upraw-
nień, w trakcie działania programu zostanie prośba o zaak-
ceptowanie lub odrzucenie połączenia.
Podsumowanie
Pisanie aplikacji internetowych zawsze wiąże się z pewnym
ryzykiem, nie omija ono także programów mobilnych. W przy-
padku tego rodzaju programów trzeba jednak przede wszyst-
kim zwrócić uwagę głównie na ograniczenia sprzętowe (ma-
ła ilość pamięci, brak możliwości nawiązywania połączeń soc-
ket’owych na telefonach z MIDP 1.0). Nie powinno się również
(chociaż jest to teoretycznie możliwe) tworzyć aplikacji-serwe-
rów na telefony komórkowe; Moc obliczeniowa oraz parame-
try połączenia internetowego tego typu urządzeń nie stano-
wią dobrej podstawy do pełnienia funkcji jakiegokolwiek ro-
dzaju serwera. Program opisany w niniejszym artykule można
rozszerzyć o takie funkcje, jak chociażby zachowywanie więk-
szej ilości poleceń, dzięki wykorzystaniu klas ByteArrayInput /
OutputStream . Daje to znacznie większe możliwości, aniżeli
przechowywanie tekstu bezpośrednio w etykietach.
Telefony komórkowe mają coraz większe możliwości, choć
często nie zdajemy sobie z nich sprawy, a to właśnie od nas,
programistów, zależy w jakim stopniu użytkownicy „komórek”
będą mogli korzystać z udostępnionych w nich technologii.
Strona, z której można pobrać J2ME Wireless Tool-
kit (najnowsza stabilna wersja – 2.2, proces pobrania wy-
maga uprzedniej rejestracji – http://java.sun.com/j2me/
download.html ). n
Software Developer’s Journal 7/2006
www.sdjournal.org
57
44703331.009.png 44703331.010.png
Zgłoś jeśli naruszono regulamin