AJAX, WebSockets на Javascript в Android Webview
- Подробности
- Категория: Разработка
- Опубликовано 07.12.2014 15:12
Итак, как же произвести обмен данными вашего 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 переменными вызывая скрипт, который имеет адрес:
До вызова скрипта вы можете сами составить на 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, то рекомендовал бы
Мы рассмотрим в этом разделе как реализовать для вашей игры или приложения на HTML+JS соединение с удалённым сервером при помощи сокетов.
Сервер
Для реализации сокетов, со стороны сервера мы выбрали
- Для начала открываем 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.
Надеемся вам помогла данная статья.