JavaScript Native Interface, JSNIJava может взаимодействовать с приложением, выполненным с использованием другого языка программирования, например С/С+. В этом случае необходимо объявить java native метод и обеспечить реализацию этого метода в другом не java-приложении. Выполняться код будет уже вне JVM (Java Virual Machine). Этот механизм называется Java Native Interface (JNI). Google, разработчик GWT, создал свой механизм вызова не java-методов, выполненных в виде механизма JavaScript Native Interface (JSNI). JSNI позволяет получить доступ к низкоуровневой функциональности браузера, не представленным API-классами GWT. При использовании JSNI-методов необходимо предусмотреть возможности исполнения кода в различных браузерах, контролировать утечки памяти и безопасность. Для GWT-приложения (java-кода), тело JavaScript-метода абсолютно непрозрачно, как и любой объект JavaScript. А это значит, что для отладки JavaScript-кода, придется использовать JavaScript отладчик, так как встроеный в Eclipse debuger его не видит. В остальном JSNI работает очень прозрачно. Компилятор фреймворка GWT транслирует java-код в JavaScript. Для того, чтобы вставить в java-код приложения определенные методы, исполненные в виде JavaScript-кода и которые компилятор не должет конвертировать, необходимо эти методы соответствующим образом оформить. Тело JSNI-метода «оборачивается» специальной комбинацией символов :
Ниже представлен пример JSNI-метода alert(String) для вызова окна с сообщением в браузере. public static native void alert(String msg) /*-{ $wnd.alert(msg); }-*/; При обращении к окну браузера window или документу document из JSNI-метода, необходимо использовать зарезервированные аббревиатуры $wnd и $doc соответственно. Cкомпилированный скрипт JSNI-метода будет работать во вложенном фрейме, а $wnd и $doc автоматически инициализируются в окно и документ. Вызывается JSNI-метод на исполнение точно также, как и любой другой метод. Сложнее обстоят дела с обращением к полям класса и с вызовом методов различных классов GWT-приложения из кода JSNI-метода. Имеются существенные различия при обращении к статическим и нестатическим полям и методам класса. С этим сейчас и попробуем разобраться. Получение доступа к полям и методам java-класса из JavaScriptПоскольку JavaScript использует динамическую типизацию, а Java - статическую, то для обращения из JSNI-метода к полям и методам java-класса необходимо использовать специальный синтаксис : [instance-expr.]@class-name::object-name(param-signature)(arguments) где
Как говорится лучше один раз увидеть, чем 100 раз услышать; поэтому рассмотрим использование JSNI на примере. Пример использования JSNI в GWT-проектеСоздадим небольшой GWT-проект с использованием библиотеки GXT в Eclipse. В интерфейсе приложения разместим кнопку и метку. В java-коде будем использовать статическое поле счетчика и нестатическое текстовое поле для сообщений. По нажатию на копку необходимо изменить значение счетчика и создать текстовое сообщение, которое отображается в метке. При обработке событий по нажатию на кнопку будем использовать JSNI-методы. Интерфейс примера представлен на следующем скриншоте. В качестве основы проекта используется пример, приведенный на странице описания GXT-контейнеров. Поскольку основной темой этой страницы является использование JSNI-методов в GWT-проекте, то полное описание примера не приводится. Желающие могут скачать пример в конце страницы. Класс Page.javaФормирование интерфейса и обработка всех событий осуществляется в классе Page.java, который наследует свойства шаблона TemplatePage. В интерфейсе страницы размещается кнопка button и метка label. Интерфейс страницы формируется в методе createBody. В качестве счетчика используется статическое целочисленное поле counter. Сообщение будет формироваться в нестатическом поле TEXT. Изменение значения счетчика осуществляется в статическом методе onButtonClick. Для отображения значения в метке используется нестатический метод showCounter(String). Эти методы вызываются непосредственно из JSNI-методов countClick и showCounter, которые вызываются из обработчика событий по нажатию на кнопку. public class Page extends TemplatePage { TextButton button = null; FieldLabel label = null; static int counter = 0; String TEXT = null; //-------------------------------------------------------- public Page() { super(); createBody(); } //-------------------------------------------------------- private void createBody() { HorizontalLayoutContainer hlcHead; HorizontalLayoutContainer hlcFoot; HorizontalLayoutData hld; hlcHead = new HorizontalLayoutContainer(); hlcHeader.add(hlcHead); button = new TextButton("Кнопка"); button.setPixelSize(80, 24); Margins margins = new Margins(4, 0, 0, 10); hld = new HorizontalLayoutData (-1, -1, margins); hlcHead.add(button, hld); button.addSelectHandler(new SelectHandler() { @Override public void onSelect(SelectEvent event) { countClick (); showCounter(Page.this, counter); } }); hlcFoot = new HorizontalLayoutContainer(); hlcFooter.add(hlcFoot); label = new FieldLabel(null, ""); label.setLabelSeparator(""); label.setId("FieldLabel"); label.setPixelSize(240, 24); label.setStyleName("font-10"); margins = new Margins(5, 0, 0, 10); hld = new HorizontalLayoutData (-1, -1, margins); hlcFoot.add(label, hld); } //-------------------------------------------------------- static void onButtonClick() { counter++; } //-------------------------------------------------------- void showCounter(final String text) { label.setText(text); } //-------------------------------------------------------- public native void countClick() /*-{ // тело метода }-*/; //-------------------------------------------------------- public native void showCounter(Page jsni, int count) /*-{ // тело метода }-*/; } Обращение к статическому полю и методуJSNI-метод countClick при работе со статическими объектами класса (полем и методом) вызывает сначала статический метод onButtonClick, увеличивая значения счетчика на 1, после чего обрабатывает значение напрямую - получает новое значение счетчика и увеличивает его на 2. public native void countClick() /*-{ // Вызов статического метода @com.common.client.Page::onButtonClick()(); // Получение значения статического поля класса var cnt = @com.common.client.Page::counter; // Изменение значение статического поля класса @com.common.client.Page::counter = cnt + 2; }-*/; Следует обратить внимание, что при взаимодействии со статическими полем и методом instance-expression не используется. Обращение к нестатическому полю и методуJSNI-метод showCounter (jsni, count) для работы с нестатическими объектами класса сначала формирует сообщение text, сохраняет значение в нестатическом поле класса TEXT, после этого читает данное сообщение из поля и вызывает нестатический метод showCounter с передачей в качестве параметра прочитанного сообщения. public native void showCounter(Page jsni, int count) /*-{ // Формирование сообщения var text = 'Кнопка была нажата '+ count + ' раз'; // Сохранение текста в нестатическом поле класса jsni.@com.common.client.Page::TEXT = text; // Получение значения из нестатического поля класса text = jsni.@com.common.client.Page::TEXT; // Вызов нестатического метода jsni.@com.common.client.Page::showCounter(Ljava/lang/String;)(text); }-*/; При взаимодействии с нестатическими полем и методом в качестве instance-expression используется экземпляр класса jsni. В качестве маленького «презента» предлагаем Вам класс Console.java, построенный полностью на JSNI-методах, который можно использовать в своих разработках. Класс Console c JSNI-методамиДля отладки GWT-приложения можно использовать представленный в начале страницы метод alert. Кроме этого фреймворк GWT предлагает свои окна вывода сообщений: Info.display (title, message) для кратковременного отображения информации и Window.alert (message). Можно использовать представленный ниже класс Console.java c JSNI-методами для вывода сообщения в консоль браузера. public class Console { /** * Вывод сообщения об ошибке в консоль браузера * @param msg сообщение */ public static native void error(String msg) /*-{ if (typeof(console) != "undefined") { console.error(msg); } }-*/; /** * Вывод информационного сообщения в консоль браузера * @param msg сообщение */ public static native void info(String msg) /*-{ if (typeof(console) != "undefined") { console.info(msg); } }-*/; /** * Вывод сообщения протоколирования в консоль браузера * @param msg сообщение */ public static native void log(String msg) /*-{ if (typeof(console) != "undefined") { console.log(msg); } }-*/; /** * Вывод предупреждающего сообщения в консоль браузера * @param msg сообщение */ public static native void warn(String msg) /*-{ if (typeof(console) != "undefined") { console.warn(msg); } }-*/; } Для вывода, к примеру, информационного сообщения о версии приложения в консоль браузера необходимо использовать следующий код. // вывод сообщения о версии Console.info("version : 3.2.1"); В окне консоли браузера Google Chrome, открываемое по нажатию кнопки F12, можно увидеть представленное на следующем скриншоте сообщение. Скачать исходный код примераИсходный код рассмотренного GWT-примера с использованием JSNI-методов в виде проекта Eclipse можно скачать здесь (43.2 Кб). В архив примера не включены библиотечные файлы gwt-servlet.jar из GWT SDK и GXT библиотека gxt-3.1.1.jar. Их необходимо подключить самостоятельно, как это описано в GWT-GXT примере с табличным компонентом Grid. |