AJAX, WebSockets на Javascript в Android Webview

android-to-server-connecting

Итак, как же произвести обмен данными вашего web-приложения на Android с удалённым сервером на Javascript?

В этом материале мы поговорим о том как можно отправлять и принимать данные с удалённого сервера с помощью javascript нашего приложения или игры. Очень многие игры и приложения могут быть зависимы от внешнего сервера. Например какие-то функции может осуществить только удалённые вычисления или просто регистрация, всё это требует некоторых подходов, когда речь заходит о web-приложениях на JavaScript. 

На данный момент существует четыре способа получить или отправить данные на удалённый сервер с помощью JS внутри webview-компонента Android. Давайте их рассмотрим.

 

#1 Вставка файла JS

Это просто банальное подключение к странице файла, который находится на удалённом сервере.

Например: <script type="text/javascript" src="http://example.com/func.js"></script>

На сервере мы, например, включаем выполнение PHP в JS файлах и просто собираем его на стороне сервера, засовывая нужные приложению данные в объявляемые тут же переменные.

Мы можем отправить серверу GET переменными вызывая скрипт, который имеет адрес: http://example.com/func.js?var=abc&foo=bar

До вызова скрипта вы можете сами составить на JS нужные запросы и вставить весь код импорта файла куда-то в HTML-код.

 

#2 AJAX

Было бы всё просто, если бы приложение не было бы локальным. Так как на Android HTML-код с JS хранятся внутри приложения (т.е. как раз локально), по этой причине приходится немного исхитриться для реализации AJAX в нужном нам приложении.

Вы можете удивиться, почему же не работает локально Аякс? Если вы запустите приложение Ajax на компьютере, с локального файла, то получите в консоли ошибку, где браузер будет ругаться на отличающийся заголовок Access-Control-Allow-Origin у удалённого сервера. Дело в том, что по умолчанию AJAX в обычном виде не может совершить кроссдоменный запрос, что приводит к ошибке.

Для того чтобы решить проблему, вам просто на удалённом сервере нужно послать заголовок Access-Control-Allow-Origin, в котором стоит разрешить подключение со всех доменов. Делается на PHP это следующим образом:

<?
Header("Access-Control-Allow-Origin: *");
Header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With");
Header("Access-Control-Allow-Methods: GET, PUT, POST");
Header("Connection: Keep-Alive"); /*Можно убрать*/
echo "Тестовое соединение: ";
echo $_POST['what'];
?>

Конечно поставив Access-Control-Allow-Origin:*, мы тем самым подшатнули безопасность и теперь все кому не лень могут беспрепятственно совершать запросы к нашему серверу, а это всё же не есть хорошо.

Клиентская часть выглядит вполне обычно для AJAX:

function getXmlHttp(){
var xmlhttp;
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
xmlhttp = false;
}
}
if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
xmlhttp = new XMLHttpRequest();
}
return xmlhttp;
} /*closing getXmlHttp*/
function ajax(g) {
var what='123';
var xmlhttp = getXmlHttp();
xmlhttp.open('POST', 'http://domen.com/url.php';, true);
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlhttp.send("what=" + encodeURIComponent(what));
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
if(xmlhttp.status == 200) {
document.body.innerHTML=xmlhttp.responseText;
}
}
};
}
window.onload=function(){ajax();}

Как видно для кроссбраузерности мы сделали пользовательскую функцию getXmlHttp(), которую используем для открытия соединения с удалённым сервером.

Говоря о AJAX и его реализации в играх и приложениях, могут понадобиться некоторые вариации логики работы.

AJAX Polling - Зацикленно опрашиваем AJAX запросом наш сервер с интервалом допустим в 500 ms.

AJAX Long-Polling - После запроса сервер не сразу присылает ответ, а как только появится новая информация для отправки клиенту.

Рекомендую погуглить это если вы думаете использовать одну из таких конструкций.

 

#3 JSON

JSON отличается от AJAX пожалуй только тем, что способен принимать и отправлять только JSON-данные. Для того чтобы обмениваться данными меж сервером и клиентом, файл на сервере должен быть следующего вида:

<?
Header("Content-Type: application/javascript");
$jsonp = false;
if ( isset( $_GET[ 'callback' ] ) ) {
$_GET[ 'callback' ] = strip_tags( $_GET[ 'callback' ] );
$jsonp= true;
$pre = $_GET[ 'callback' ] . '(';
$post = ');';
}
//Присланная скриптом переменная name содержится в $_GET[ 'name' ]
$json = json_encode( array('one' => 'ABCDE', 'b' => 2, 'c' => 3));
print( ( $jsonp ) ? $pre . $json . $post : $json );
?>

Ну а часть клиента лучше будет представить с использованием библиотеки jQuery и её функции:

$.ajax({
type: 'GET',
url: 'http://domen.com/url.php';,
data: 'name=JOHN&surname=Smith',
contentType: "jsonp",
dataType: 'jsonp',
success: function (json) {
document.body.innerHTML=json.one;
}
});

Вот так всё несложно делается. На примере показаны все ключевые аспекты использования JSON.

 

#4 WebSocket

Сокеты из всего перечисленного имеют самую трудоёмкую реализацию в Android внутри WebView. Для сокетов нужна более уверенная и продуманная и серверная часть и клиентская.

С серверной стороны лучше использовать Node.js + ws ну или Socket.io. Также есть возможность сделать очень простым PHP Socket +WebSocket. Тут уже как говориться на вкус и цвет... Если всё же посмотрите в сторону PHP, то рекомендовал бы Ratchet. Да и ещё много может зависит от цели для которой выбирается нужная сокет-связка.

Мы рассмотрим в этом разделе как реализовать для вашей игры или приложения на HTML+JS соединение с удалённым сервером при помощи сокетов.

Сервер

Для реализации сокетов, со стороны сервера мы выбрали Node.js + ws, поскольку этот вариант показался нам самым перспективным и удачным. Согласитесь, что очень удобно когда и серверная часть на родном JavaScript. Давайте рассмотрим как и что нужно устанавливать.

- Для начала открываем SSH соединение с нашим сервером и устанавливаем Node.js

Способы установки разные и они могут варьироваться в зависимости от вашей системы.

- Выбираем на сервере папку, где разворачиваем сервер и переходим в неё через SSH-терминал командой cd.

- Устанавливаем ws (WebSocket) командой npm install --save ws

- Ну вот пожалуй и всё с установкой. Теперь создаём файл сервера server.js в нашей папке на сервере.  Теперь нужно его наполнить. Если вы не разбираетесь в этом, то рекомендуем его наполнить так:

var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ port: 8080 });
var clients = [];

wss.on('connection', function(ws) {
console.log('connected');
clients.push(ws);
ws.on('close', function(){
console.log('client closing');
});
ws.on('message', function(msg){
console.log('message received');
for (var i = 0, len = clients.length; i < len; i++)
try {
// do not use try-catch here, do it properly
if (clients[i])
clients[i].send(msg);
}
catch(e){
clients[i] = null;
}
});
});

Код небольшой и в нём можно даже методом "тыка" всё изучить.

Запускаем сервер через SSH командой node server.js. Чтобы остановить сервер в терминале нажимаем CTRL+C ( НЕ CTRL+Z). Если вы нажали CTRL+Z, то тем самым свернули процесс из активной строки ввода - перезагрузка сервака это поправит. Конечно использовать постоянно SSH-терминал не пойдёт - это только для отладки, в будущем нужно будет прикручивать демона.

Теперь самое время узнать как дела с клиентской частью. К сожалению в Андроид WebView нет использования сокетов и поэтому придётся делать всё мостом JavaScript-Java.

Чтобы не расписывать всё от начала до конца, мы решили сложить в архив всё что нужно. Мы сейчас будем всё это использовать.

В классе вашей активити пишите:

vw.getSettings().setJavaScriptEnabled(true);
vw.setWebChromeClient(new WebChromeClient() {
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Log.d("Browser => ", consoleMessage.message());
return true;
}
});
vw.setWebViewClient(
new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return false;
}
}
);
vw.addJavascriptInterface(new WebSocketFactory(vw), "WebSocketFactory");

Где vw, как вы догадались ваш WebView-экземпляр.

Теперь подключаем к андроид-проекту файлы WebSocket.java и WebSocketFactory.java, а также библиотеку jetty-websocket-8.1.15.v20140411.jar (засовываем в папку libs нашего проекта).

В папке HTML сложено всё что нужно для теста. Теперь просто копируем содержимое папки HTML в папку assets нашего проекта и вызываем vw.loadUrl("file:///android_asset/HTML/index.html"); в классе основного активити.

Если вам потребуется использовать Draft76 (hixie-76), тогда замените в файле WebSocketFactory.java функцию WebSocket getInstance на:

public WebSocket getInstance(String url) {
return getInstance(url, WebSocket.Draft.DRAFT76);
}
public WebSocket getInstance(String url, WebSocket.Draft draft) {
WebSocket socket = null;
Thread th = null;
try {
socket = new WebSocket(appView, new URI(url), draft, getRandonUniqueId());
th = socket.connect();
return socket;
} catch (Exception e) {
if(th != null) {
th.interrupt();
}
}
return null;
}

Кстати модуль ws для Node.js также поддерживает Draft76.

Надеемся вам помогла данная статья.


You have no rights to post comments

Карта сайта