Дано веб-приложение SPA, которое мы хотим преобразовать в собственное приложение для Android с помощью PhoneGap/Cordova.
Предположим, что веб-приложение ожидает найти глобальный объект конфигурации, который внедряется на сервер в теге SCRIPT. Итак, ответ сервера содержит что-то вроде этого:
<!DOCTYPE html> <html> <head> <script type=”text/javascript”> var xconfig = { root: “/myapp/”, appName: “My Application” }; </script> </head> <body> <! — markup → <script src=’client/vendor/require.js’ data-main=’client/main.js’ type=’text/javascript’></script> </body> </html>
Очевидно, что конфигурация JSON не жестко закодирована и не указана в коде сервера, ни в разметке View, а загружается из файла во время выполнения.
При переносе приложения на Cordova нам нужно преобразовать нашу разметку в HTML-страницу («индексировать .html»). Очевидно, мы не будем размещать конфигурацию JSON на этой html-странице. В идеале он должен быть внедрен на страницу во время выполнения. Вот как это сделать.
Предположим, что конфигурация представляет собой текстовый файл JSON «config.json»:
{ "root": "/myapp/", "appName": "My Application" }
Итак, давайте поместим его в папку «assets» Android:
Затем в методе onCreate нашей Cordova-Activity нам нужно загрузить файл из ресурсов, разобрать его в JSON и передать в WebView.
Чтобы передать объект в клиентский код, мы используем метод WebView addJavascriptInterface.
открытый класс MyApp расширяет DroidGap
{
@Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(savedInstanceState);
super .в этом();
JSONObject jsonConfig; try { String jsonString = getConfigJsonString(); jsonConfig = new JSONObject(jsonString); } catch (JSONException e) { e.printStackTrace(); throw new RuntimeException(e); } this.appView.addJavascriptInterface(jsonConfig, “xconfig”); // Set by <content src=”index.html” /> in config.xml super.loadUrl(Config.getStartUrl()); } }
Метод getConfigJsonString читает json-файл из ресурсов и возвращает его содержимое:
private String getConfigJsonString() { InputStream stream ; InputStreamReader reader; BufferedReader reader2; StringBuilder bld; Resources res = this.getResources(); try { stream = this.getAssets().open(“config.json”); reader = new InputStreamReader(stream, Charset.forName(“UTF-8”)); reader2 = new BufferedReader(reader); String str; bld = new StringBuilder(); while((str = reader2.readLine()) != null) { bld.append(str); } reader2.close(); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } return bld.toString(); }
Затем мы анализируем его в JSONObject для передачи на клиентскую сторону через addJavascriptInterface.
Довольно просто.
К сожалению, есть некоторые тонкости.
Что вы ожидаете увидеть в коде javascript в качестве глобальной переменной xconfig?
Я ожидал увидеть обычный JS-объект с теми же полями, что и в JSON («appName», «root»).
Но это не так. В коде javascript «xconfig» станет экземпляром объекта без полей, но с методом «toString», возвращающим фактическую строку JSON:
console.log(typeof xconfig); // prints: object console.log(Object.prototype.toString.call(xconfig)); // prints: [object Object] console.log(Object.keys(xconfig)); // prints: console.log(JSON.stringify(xconfig)); // prints: {} console.log(xconfig.toString()); // prints: {“root”: “/myapp/”, “appName”: “My Application”}
Не очень понятно, верно?
Итак, чтобы наконец получить фактический объект конфигурации в клиентском коде, мы должны разобрать его через JSON:
xconfig = JSON.parse(xconfig);
Интересно, что если в коде Java мы передаем String:
this.appView.addJavascriptInterface(jsonConfig.toString(), "xconfig");
ничего не изменится — клиент по-прежнему будет получать тот же странный объект.
А что, если клиентский код разделяется между Web-приложением и Cordova-приложением? JSON.parse для обычного объекта завершится ошибкой SyntaxError.
Поэтому я придумал следующий код начальной загрузки:
var xconfig = window.xconfig; if (!xconfig) { var err = “No xconfig global object found. Unable to proceed.”; alert(err); throw new Error(err); } if (Object.prototype.toString.call(xconfig) == “[object String]” || Object.keys(xconfig).length === 0 && xconfig.toString() != “[object Object]”) { xconfig = JSON.parse(xconfig); }