Ситуация, сложившаяся сегодня в мире мобильного программирования во многом напоминает обстановку конца 80-х, когда на рынке ПК существовало несколько практически несовместимых между собой устройств. Стараясь выжать из железа максимум и как можно быстрее запустить в продажу новые модели, производители не особо заботятся даже о совместимости собственных устройств, что уж говорить о совместимости между устройствами разных производителей. Когда мобильные телефоны являлись просто средством коммуникации, несовместимость не вызывала особых проблем. С течением времени телефоны стали превращаться в сложные функционально насыщенные устройства. Возникла потребность в разнообразном и качественном софте. Осознавая, что сторонние разработчики не будут тратить время на написание программы для единственной модели телефона, производители стали создавать платформы, позволяющие запускать одну и ту же программу на различных устройствах. В настоящее время существует несколько решений, но в России по настоящему большое распространение получила только Java 2 Platform Micro Edition (J2ME).
Основная идея J2ME проста: программист компилирует программу в промежуточный код, который затем выполняется специальным эмулятором – виртуальной Java машиной (VJM). VJM выступает посредником между программой и прошивкой телефона. Таким образом, основную часть работы по адаптации приложений к конкретной модели берет на себя производитель. Кроме того, VJM решает проблему безопасности, поскольку приложения не имеют прямого доступа к памяти и ресурсам телефона.
На практике VJM разных производителей не всегда выдают одинаковый результат. Из других недостатков J2ME можно отметить неспособность ее работать с вещественными числами (не актуально для CDLC 1.1), а также проблему с исходными кодами. Поскольку в результате компиляции получаются промежуточный, а не машинный код, он легко может быть переведен обратно в java исходник. 2. Рабочая среда
Для того чтобы начать разрабатывать мидлеты (так принято называть J2ME приложения для мобильных телефонов) Вам необходимы три вещи: Java 2 Standard Edition (J2SE) SDK version 1.4.2 или выше - компилятор и утилиты для создания Java архивов; J2ME Wireless Toolkit (WTK) 2.2 - набор утилит и эмуляторов для создания и отладки мидлетов; текстовый редактор или IDE.
Все эти компоненты абсолютно бесплатны и доступны по адресам: http://java.sun.com/products/, www.netbeans.org и http://www.mobilab.ru/soft/. 2.1 Создание нового проекта
Запустите WTK KToolbar. Для создания нового проекта выберите пункт меню File>New Project. В появившемся окне заполните поля "Project Name" и "MIDlet Class Name". Введите, например, SampleSuite и SampleMIDlet. После заполнение этих полей Вам будет предложено настроить параметры проекта. Ничего не меняйте, просто нажмите кнопку OK. В папке [WTK]\apps\ будет создан новый каталог SampleSuite. Каждый проект содержит стандартный набор каталогов: bin - сюда попадают .jar и .jad файлы после упаковки проекта; lib - сюда следует поместить файлы подключаемых библиотек; res - файлы ресурсов (например, .png, .txt); src - файлы .java с исходным текстом программы;
Название .java файла должно соответствовать имени расположенного внутри него класса. Не забудьте, что язык Java чувствителен к регистру букв. 2.2 Открытие и запуск примера
После написания кода программы, как правило, наступает этап тестирования. Сначала приложение отлаживается на эмуляторе, а затем непосредственно на телефоне. Давайте откроем один из стандартных примеров (например, UIDemo). Чтобы запустить проект в окне эмулятора, нужно его откомпилировать (кнопка Build), а затем нажать Run на панели инструментов.
Для переноса приложения на телефон, необходимо его упаковать. Для этого после компиляции надо выбрать пункт меню Project>Package. В папке bin проекта появятся файлы .jar и .jad. Файл jar является Java архивом, содержащим файлы проекта, а jad - так называемым дескриптором приложения. Дескриптор содержит вспомогательную информацию о классах архива. Эта информация используется телефоном для эффективного управления ресурсами. Именно эти два файла нужно копировать в телефон. 3. Простейшее приложение
Особенностью программной модели мидлета является возможность вмешательства операционной системы в ход выполнения программы. Работа приложения может быть в любой момент прервана каким-нибудь внешним событием, например входящим телефонным звонком. Это обстоятельство требует постоянного контроля системы над ходом выполнения программы. Реализуется этот контроль с помощью специального управляющего софта, называемого AMS (Application-Management Software).
Каждый Java программист знает, что работа программы начинается с запуска метода main(). В J2ME этот метод недоступен. Вместо него предлагается использовать startApp().
Запущенное приложение может находиться в трех возможных состояниях: paused: мидлет запущен, но не активен; active: мидлет активен и destroyed: мидлет остановлен и готов к завершению.
Рассмотрим жизненный цикл мидлета. Сразу после запуска он находится в состоянии paused. Прежде чем перейти в активный режим, он должен выполнить инициализацию. Мидлет не имеет специального инициализационного метода и все необходимые действия выполняются внутри startApp(). Через некоторое время после запуска программы, AMS вызывает метод startApp(), в результате приложение переходит в состояние active. Если при запуске возникли какие-нибудь ошибки, управление передается методу javax.microedition.midlet.MIDletStateChangeException, который переводит мидлет в состояние destroyed.
В любое время AMS может прервать выполнение мидлета и перевести его в состояние paused. При этом AMS вызывает метод pauseApp(). Для того чтобы перевести приложение в состояние destroyed и закрыть его, AMS вызывает метод destroyApp().
Мидлет не способен перевести сам себя из одного состояния в другое. Он может только послать соответствующий запрос AMS, воспользовавшись одним из методов: notifyDestroyed() - запрос на завершение работы; notifyPaused() - запрос на дезактивацию и переход в состояние паузы; resumeRequest() - запрос на реактивацию и переход в активное состояние. AMS решает, нужно ли удовлетворить запрос, и если нужно, то когда это сделать. После запроса resumeRequest() ASM вызывает метод startApp(). В ответ на notifyPaused() и notifyDestroyed() AMS напрямую переводит мидлет в требуемое состояние.
Ниже приведен код простого мидлета-каркаса. Я рекомендую сохранить его где-нибудь на диске и использовать в качестве шаблона для новых приложений. //файл SampleMIDlet.java import javax.microedition.lcdui.*; import javax.microedition.midlet.*;
public class SampleMIDlet extends MIDlet { private Display display; public SampleMIDlet(){}
protected void pauseApp() { // Сюда следует добавить код, который надо выполнять непосредственно // перед переводом приложения в режим паузы. }
protected void startApp() throws MIDletStateChangeException { if( display == null ) { // Этот код выполняется при запуске мидлета initApp(); } // Сюда следует добавить код, который надо выполнять непосредственно // перед переводом приложения в активный режим. }
private void initApp() { display = Display.getDisplay( this ); // Сюда добавляется код инициализации приложения }
public void exitApp() { // Сюда следует добавить код, который будет выполняться // при закрытии приложения. notifyDestroyed(); // уничтожение MIDlet-а } } 4. Пользовательский интерфейс высокого уровня
В состав J2ME входит пакет javax.microedition.lcdui, в котором реализованы классы пользовательского интерфейса. Принято различать низкоуровневые и высокоуровневые классы. В рамках данной статьи мы рассмотрим только последние. Программы, написанные с использованием только высокоуровневого интерфейса, практически не требуют адаптации при переносе на различные модели телефонов.
Разработка программ на основе высокоуровневого интерфейса сводятся к двум этапам: созданию окон и настройке команд. 4.1 Создание окон
Высокоуровневый интерфейс определяет четыре типа окон: Alert, TextBox, List и Form.
Alert - окно служебных сообщений. При создании требует четыре параметра: заголовок окна, текстовое сообщение, рисунок и тип сообщения (ALARM, CONFIRMANTION, ERROR, INFO, WARNING). С помощью метода setTimeout можно установить время (в миллисекундах), через которое окно будет закрыто. Alert myAlert = new Alert("Заголовок сообщения","сообщение",image,AlerType.ALARM); myAlert.setTimeout(1000);
TextBox - окно-редактор текста. При создании требует, чтобы ему передали заголовок, начальный текст, максимальный размер текста и ограничения. В качестве ограничений можно использовать константы: ANY - ограничений на текст нет, EMAILADDR - ввод e-mail адресов, NUMERIC - ввод чисел, PASSWORD - ввод пароля, PHONENUMBER - ввод телефонных номеров, URL –ввод URL адресов.
Для получения введенного текста можно воспользоваться либо методом getChars(char[] chAr), который записывает текст в переданный массив символов chAr; либо getString(), который возвращает строку. С помощью метода size() можно узнать размер введенного текста. TextBox myTb= new TextBox("Введите текст", "", 255,TextBox.ANY); ... String strRes=myTb.getString();
List - окно-список элементов. При создании список требует четыре параметра: заголовок списка, тип списка, массив строк и массив картинок для элементов списка. Два последних параметра можно опустить, но тогда для формирования списка нужно использовать метод append(String str, Image img).
В J2ME реализованы четыре типа списков. Тип EXCLUSIVE предназначен для пометки одного элемента из списка (соответствует TRadioButton в Delphi); MULTIPLE предназначен для пометки нескольких элементов (соответствует TCheckBox); IMPLICIT позволяет выбрать один элемент из списка (cоответствует TListBox).
Чтобы узнать, какие элементы выбрал или пометил пользователь, нужно воспользоваться одним из следующих методов. getSelectedIndex() - возвращает номер выбранного элемента. getSelectedFlags(boolean[] selArr) - записывает в переданный массив selArr состояние всех элементов списка. String st={"Элемент 1", "Элемент 2", "Элемент 3"}; List myList=new List("Элементы",Choice.IMPLICIT,st,null); ... int index=myList.getSelectedIndex();
Form. Форма - это контейнер, куда можно помещать различные визуальные элементы управления. Если элементы формы не умещаются на экране телефона, создается вертикальная полоса прокрутки. Для добавления элемента на форму используется метод append(). В качестве единственного параметра ему нужно передать либо строку, либо картинку, либо один из визуальных компонентов класса Item.
ChoiceGroup - группа элементов для выбора. Этот элемент аналогичен List. Как и List он позволяет создавать EXCLISIVE и MULTIPLE списки. Вместо типа IMPLICIT нужно использовать POPUP, который создает список в виде выпадающего меню.
DateField - элемент предназначен для установки даты и времени. При создании требуется передать три параметра: заголовок, режим даты (DATE, TIME или DATE_TIME) и часовой пояс. Последний параметр можно опустить.
TextField - поле для ввода текста. Этот элемент очень похож на TextBox, но, в отличие от последнего, не занимает весь экран.
StringItem - элемент предназначен для добавления на форму статических строк текста, кнопок и гиперссылок. При создании требуется передать три параметра: строку-метку, строку текста и форматирование текста. В качестве последнего параметра используются константы: BUTTON - для создания кнопки, HYPERLINK - для гиперссылки, LAYOUT_BOTTOM, LAYOUT_CENTER, LAYOUT_TOP, LAYOUT_LEFT, LAYOUT_RIGHT - для определения положения текста на экране.
Spacer - элемент предназначен для создания свободного пространства указанного размера. Используется для позиционирования других элементов на форме. В качестве параметров при создании задаются ширина и высота области.
ImageItem - элемент предназначен для размещения изображения на форме. При создании требуется передать пять параметров: текст над рисунком, объект класса Image с загруженным рисунком, расположение рисунка (LAYOUT_LEFT, LAYOUT_RIGHT, LAYOUT_CENTER), текст под рисунком и форматирование. Последний параметр аналогичен рассмотренному в StringItem и также может быть опущен.
J2ME гарантированно умеет работать с рисунками в формате PNG. Все рисунки проекта должны быть расположены в папке res. Код загрузки и использования рисунка нужно обязательно помещать в try{…}catch{…} скобки, в противном случае при сборке проекта возникнут ошибки. Для загрузки изображения из файла используется метод Image.createImage("/файл.png").
Guage - элемент для отображения полос состояния. При создании требуется передать три параметра: метку, флаг типа boolean, указывающий на то должна ли полоса быть интерактивной; значение, соответствующее заполненной полосе, и начальное значение. Для установки текущего положения используется метод setValue(int value).
Помимо указанных элементов, под заголовком окон TextBox, List и Form можно поместить бегущую строку с текстом. Для этого нужно создать объект класса Tricker и вызвать метод setTricker окна, указав в качестве параметра созданный объект. ChoiceGroup myCG=new ChoiceGroup("Элементы",Choice.POPUP); DateField myDF=new DateField("Дата и время",DateField.DATE_TIME); TextField myTF=new TextField("Введите текст", "", 20,TextField.ANY); StringItem mySI1=new StringItem("Ф.И.О.", "Иванов Иван Иванович"); StringItem mySI2=new StringItem("URL", "www.MobiLab.ru",Item.HYPERLINK); StringItem mySI3=new StringItem("OK", "", Item.BUTTON); Spacer mySp=new Spacer(50,0); Gauge myG=new Gauge("Идет процесс",true,100,45); try{ Image img=Image.createImage("/mobilab.png"); ImageItem myII=new ImageItem("Логотип",img,ImageItem.LAYOUT_CENTER,"www.mobilab.ru"); myForm.append(myII); }catch(java.io.IOException ex){} myForm.append(myCG); myForm.append(myDF); myForm.append(mySp); myForm.append(myTF); myForm.append(mySp); myForm.append(mySI1); myForm.append(mySI2); myForm.append(mySI3); myForm.append(myG); Tricker myTR=new Tricker("Это бегущая строка..."); myForm.setTricker(myTR); 4.2 Создание и настройка команд
Для перехода между окнами используется метод Display.setCurrent(Displayable d). Обычно переход выполняется после какого-то действия пользователя. Для настройки реакции на действие, необходимо: 1. создать команду; 2. связать ее с экраном или элементом управления; 3. связать с экраном или элементом класс, реализующий обработчик событий; 4. внутри метода commandAction запрограммировать реакцию на данную команду;
Для создания команды необходимо создать объект класса Command private Command MyCommand=new Command("Моя команда",Command.SCREEN,2);
Первый параметр определяет строковое представление команды, второй - тип команды, третий - ее приоритет. В качестве типа используется одна из: BACK, CANCEL, EXIT, HELP, ITEM, OK, SCREEN, STOP. Тип команды лишь указывает ее предполагаемое назначение и не влияет на функциональность.
Для связи команды с экраном используется метод Displayable.addCommand(Command cmd), а для связи с элементом управления - Item.setDefaultCommand(Command cmd).
Если с экраном связаны две команды, то для них создаются соответствующие подэкранные кнопки. Если команд больше, то для одной из них создается кнопка, а остальные помещаются в меню, вызываемое при нажатии второй кнопки. Система решает, какую из команд поместить на экран, ориентируясь на их тип. Например, если среди команд есть команда типа EXIT, то именно с ней будет связана подэкранная кнопка.
Класс может обрабатывать команды, только если он реализует интерфейсы CommandListener и ItemCommandListener: public class Navigator extends MIDlet implements CommandListener, ItemCommandListener{…}
Для связи обработчика с экраном используется метод Displayable.setCommandListener(CommandListener cl); для связи с элементом формы - Item.setItemCommandListener(ItemCommandListener cl). В качестве параметра нужно указать объект, реализующий обработчик событий. Если обработка команд выполняется внутри класса, откуда вызывается метод, то в качестве параметра можно использовать слово this.
При выполнении команды система вызывает метод commandAction(Command c, Displayable d) для окна и commandAction(Command c, Item i) для элементов формы. Вызвавшая метод команда передается в параметр c. Внутри этого метода необходимо идентифицировать команду и написать код ее обработки: //Обработка команд от окна. public void commandAction(Command c, Displayable d){ if (c==command1) { //Сюда помещаем код, выполняющийся при //возникновении команды command1 } if (c==command2) {... } ...}
//Обработка команд от формы public void commandAction(Command c, Item i){ if (c==comForm1) { //Сюда помещаем код, выполняющийся при //возникновении команды comForm1 } ...}
В данной момент Вы располагаете всей необходимой информацией для написания полноценного мобильного приложения. Давайте в качестве упражнения разработаем небольшую программу-тестирование. Программа будет иметь окна четырех типов: заглавное окно с логотипом программы, окно для ввода информации о пользователе, окна с вопросами и окно с результатом теста.