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. |
