Дано веб-приложение SPA, которое мы хотим преобразовать в собственное приложение для Android с помощью PhoneGap/Cordova.

Предположим, что веб-приложение ожидает найти глобальный объект конфигурации, который внедряется на сервер в теге SCRIPT. Итак, ответ сервера содержит что-то вроде этого:

<!DOCTYPE html>
<html>
<head>
&lt;script type=”text/javascript”&gt;
var xconfig = {
 root: “/myapp/”,
 appName: “My Application”
};
&lt;/script&gt;
</head>
<body>
 <! — markup →
 &lt;script src=’client/vendor/require.js’ data-main=’client/main.js’ type=’text/javascript’&gt;&lt;/script&gt;
</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);
}